diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f0c9fa66..87213a74b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,7 @@ jobs: pip install coverage tblib - name: Build documentation - run: mkdocs build + run: zensical build - name: Collect static files run: python netbox/manage.py collectstatic --no-input diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 361dfe72f..79b680f6b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,11 +21,11 @@ repos: language: system pass_filenames: false types: [python] - - id: mkdocs-build + - id: zensical-build name: "Build documentation" - description: "Build the documentation with mkdocs" + description: "Build the documentation with Zensical" files: 'docs/' - entry: mkdocs build + entry: zensical build language: system pass_filenames: false - id: yarn-validate diff --git a/.readthedocs.yaml b/.readthedocs.yaml index c69c51748..5e6dda025 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,10 +1,10 @@ version: 2 build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: python: "3.12" -mkdocs: - configuration: mkdocs.yml -python: - install: - - requirements: requirements.txt + commands: + - pip install -r requirements.txt + - python -m zensical build --config-file mkdocs.yml + - mkdir -p $READTHEDOCS_OUTPUT/html/ + - cp -r netbox/project-static/docs/* $READTHEDOCS_OUTPUT/html/ diff --git a/base_requirements.txt b/base_requirements.txt index eb1ecd84b..d88174c5e 100644 --- a/base_requirements.txt +++ b/base_requirements.txt @@ -4,7 +4,7 @@ colorama # The Python web framework on which NetBox is built # https://docs.djangoproject.com/en/stable/releases/ -Django==5.2.* +Django==6.0.* # Django middleware which permits cross-domain API requests # https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst @@ -35,7 +35,9 @@ django-pglocks # Prometheus metrics library for Django # https://github.com/korfuri/django-prometheus/blob/master/CHANGELOG.md -django-prometheus +# TODO: 2.4.1 is incompatible with Django>=6.0, but a fixed release is expected +# https://github.com/django-commons/django-prometheus/issues/494 +django-prometheus>=2.4.0,<2.5.0,!=2.4.1 # Django caching backend using Redis # https://github.com/jazzband/django-redis/blob/master/CHANGELOG.rst @@ -69,7 +71,7 @@ django-timezone-field # A REST API framework for Django projects # https://www.django-rest-framework.org/community/release-notes/ # TODO: Re-evaluate the monkey-patch of get_unique_validators() before upgrading -djangorestframework==3.16.1 +djangorestframework==3.17.1 # Sane and flexible OpenAPI 3 schema generation for Django REST framework. # https://github.com/tfranzel/drf-spectacular/blob/master/CHANGELOG.rst @@ -99,10 +101,6 @@ jsonschema # https://python-markdown.github.io/changelog/ Markdown -# MkDocs -# https://github.com/mkdocs/mkdocs/releases -mkdocs<2.0 - # MkDocs Material theme (for documentation build) # https://squidfunk.github.io/mkdocs-material/changelog/ mkdocs-material @@ -177,3 +175,7 @@ tablib # Timezone data (required by django-timezone-field on Python 3.9+) # https://github.com/python/tzdata/blob/master/NEWS.md tzdata + +# Documentation builder (succeeds mkdocs) +# https://github.com/zensical/zensical +zensical diff --git a/contrib/openapi.json b/contrib/openapi.json index 5b4c99a0a..57bfe97bd 100644 --- a/contrib/openapi.json +++ b/contrib/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.3", "info": { "title": "NetBox REST API", - "version": "4.5.10", + "version": "4.6.0-beta2", "license": { "name": "Apache v2 License" } @@ -733,6 +733,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -2103,6 +2112,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -3969,6 +3987,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -6007,6 +6034,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -7999,6 +8035,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -9930,6 +9975,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -11327,6 +11381,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -12954,6 +13017,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -14212,6 +14284,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -15774,6 +15855,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -17100,6 +17190,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -19067,6 +19166,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "updated_by_request", @@ -20115,6 +20223,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -21770,6 +21887,15 @@ "format": "date-time" } }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "started", @@ -22720,6 +22846,15 @@ "format": "uuid" } }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "time_after", @@ -23447,6 +23582,15 @@ "type": "string" }, "description": "Search" + }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } } ], "tags": [ @@ -23514,6 +23658,1209 @@ } } }, + "/api/dcim/cable-bundles/": { + "get": { + "operationId": "dcim_cable_bundles_list", + "description": "Get a list of cable bundle objects.", + "parameters": [ + { + "in": "query", + "name": "created", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__empty", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__gt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__gte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__lt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__lte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__n", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created_by_request", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "query", + "name": "description", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "description__ic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__ie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__iew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__iregex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__isw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__nic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__nie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__niew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__nisw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__regex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "id__gt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__gte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__lt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__lte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__empty", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__gt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__gte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__lt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__lte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__n", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "name": "limit", + "required": false, + "in": "query", + "description": "Number of results to return per page.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "modified_by_request", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "query", + "name": "name", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "name__ic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__ie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__iew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__iregex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__isw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__nic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__nie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__niew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__nisw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__regex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "name": "offset", + "required": false, + "in": "query", + "description": "The initial index from which to return the results.", + "schema": { + "type": "integer" + } + }, + { + "name": "ordering", + "required": false, + "in": "query", + "description": "Which field to use when ordering the results.", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "owner", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner Group (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner Group (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group_id", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": "Owner Group (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": "Owner Group (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_id", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Owner (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Owner (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + }, + "description": "Search" + }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "tag", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "tag__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "tag_id", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "tag_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "updated_by_request", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "tags": [ + "dcim" + ], + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedCableBundleList" + } + } + }, + "description": "" + } + } + }, + "post": { + "operationId": "dcim_cable_bundles_create", + "description": "Post a list of cable bundle objects.", + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/CableBundleRequest" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/CableBundleRequest" + } + } + ] + } + }, + "multipart/form-data": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/CableBundleRequest" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/CableBundleRequest" + } + } + ] + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CableBundle" + } + } + }, + "description": "" + } + } + }, + "put": { + "operationId": "dcim_cable_bundles_bulk_update", + "description": "Put a list of cable bundle objects.", + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CableBundleRequest" + } + } + }, + "multipart/form-data": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CableBundleRequest" + } + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CableBundle" + } + } + } + }, + "description": "" + } + } + }, + "patch": { + "operationId": "dcim_cable_bundles_bulk_partial_update", + "description": "Patch a list of cable bundle objects.", + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CableBundleRequest" + } + } + }, + "multipart/form-data": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CableBundleRequest" + } + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CableBundle" + } + } + } + }, + "description": "" + } + } + }, + "delete": { + "operationId": "dcim_cable_bundles_bulk_destroy", + "description": "Delete a list of cable bundle objects.", + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CableBundleRequest" + } + } + }, + "multipart/form-data": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CableBundleRequest" + } + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + } + } + } + }, + "/api/dcim/cable-bundles/{id}/": { + "get": { + "operationId": "dcim_cable_bundles_retrieve", + "description": "Get a cable bundle object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this cable bundle.", + "required": true + } + ], + "tags": [ + "dcim" + ], + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CableBundle" + } + } + }, + "description": "" + } + } + }, + "put": { + "operationId": "dcim_cable_bundles_update", + "description": "Put a cable bundle object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this cable bundle.", + "required": true + } + ], + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CableBundleRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CableBundleRequest" + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CableBundle" + } + } + }, + "description": "" + } + } + }, + "patch": { + "operationId": "dcim_cable_bundles_partial_update", + "description": "Patch a cable bundle object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this cable bundle.", + "required": true + } + ], + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedCableBundleRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedCableBundleRequest" + } + } + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CableBundle" + } + } + }, + "description": "" + } + } + }, + "delete": { + "operationId": "dcim_cable_bundles_destroy", + "description": "Delete a cable bundle object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this cable bundle.", + "required": true + } + ], + "tags": [ + "dcim" + ], + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + } + } + } + }, "/api/dcim/cable-terminations/": { "get": { "operationId": "dcim_cable_terminations_list", @@ -24000,6 +25347,15 @@ "type": "string" } }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "termination_id", @@ -24188,6 +25544,60 @@ "operationId": "dcim_cables_list", "description": "Get a list of cable objects.", "parameters": [ + { + "in": "query", + "name": "bundle", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Cable bundle (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "bundle__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Cable bundle (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "bundle_id", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Cable bundle (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "bundle_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Cable bundle (ID)", + "explode": true, + "style": "form" + }, { "in": "query", "name": "circuittermination_id", @@ -25695,6 +27105,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -27562,6 +28981,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "type", @@ -30089,6 +31517,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -31715,6 +33152,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "type", @@ -34242,6 +35688,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -35317,6 +36772,13 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "enabled", + "schema": { + "type": "boolean" + } + }, { "in": "query", "name": "id", @@ -35838,6 +37300,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "updated_by_request", @@ -36829,6 +38300,13 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "enabled", + "schema": { + "type": "boolean" + } + }, { "in": "query", "name": "id", @@ -37764,6 +39242,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -39494,6 +40981,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -42307,6 +43803,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "subdevice_role", @@ -46792,6 +48297,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -48889,6 +50403,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "type", @@ -51503,6 +53026,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -53680,6 +55212,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "type", @@ -57828,6 +59369,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -60224,6 +61774,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -61891,6 +63450,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "updated_by_request", @@ -64472,6 +66040,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -66561,6 +68138,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -68215,6 +69801,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -69713,6 +71308,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -70459,6 +72063,13 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "enabled", + "schema": { + "type": "boolean" + } + }, { "in": "query", "name": "id", @@ -71159,6 +72770,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "updated_by_request", @@ -72150,6 +73770,13 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "enabled", + "schema": { + "type": "boolean" + } + }, { "in": "query", "name": "id", @@ -73262,6 +74889,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -74560,6 +76196,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -76309,6 +77954,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -78386,6 +80040,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -80083,6 +81746,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -82185,6 +83857,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -84546,6 +86227,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "type", @@ -87285,6 +88975,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -89221,6 +90920,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -90686,6 +92394,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "type", @@ -93229,6 +94946,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -94023,6 +95749,1360 @@ } } }, + "/api/dcim/rack-groups/": { + "get": { + "operationId": "dcim_rack_groups_list", + "description": "Get a list of rack group objects.", + "parameters": [ + { + "in": "query", + "name": "created", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__empty", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__gt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__gte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__lt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__lte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__n", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created_by_request", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "query", + "name": "description", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "description__ic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__ie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__iew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__iregex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__isw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__nic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__nie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__niew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__nisw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__regex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "id__gt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__gte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__lt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__lte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__empty", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__gt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__gte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__lt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__lte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__n", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "name": "limit", + "required": false, + "in": "query", + "description": "Number of results to return per page.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "modified_by_request", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "query", + "name": "name", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "name__ic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__ie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__iew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__iregex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__isw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__nic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__nie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__niew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__nisw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__regex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "name": "offset", + "required": false, + "in": "query", + "description": "The initial index from which to return the results.", + "schema": { + "type": "integer" + } + }, + { + "name": "ordering", + "required": false, + "in": "query", + "description": "Which field to use when ordering the results.", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "owner", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner Group (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner Group (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group_id", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": "Owner Group (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": "Owner Group (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_id", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Owner (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Owner (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + }, + "description": "Search" + }, + { + "in": "query", + "name": "slug", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "slug__ic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__ie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__iew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__iregex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__isw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__nic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__nie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__niew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__nisw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__regex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "tag", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "tag__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "tag_id", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "tag_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "updated_by_request", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "tags": [ + "dcim" + ], + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedRackGroupList" + } + } + }, + "description": "" + } + } + }, + "post": { + "operationId": "dcim_rack_groups_create", + "description": "Post a list of rack group objects.", + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/RackGroupRequest" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/RackGroupRequest" + } + } + ] + } + }, + "multipart/form-data": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/RackGroupRequest" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/RackGroupRequest" + } + } + ] + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RackGroup" + } + } + }, + "description": "" + } + } + }, + "put": { + "operationId": "dcim_rack_groups_bulk_update", + "description": "Put a list of rack group objects.", + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RackGroupRequest" + } + } + }, + "multipart/form-data": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RackGroupRequest" + } + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RackGroup" + } + } + } + }, + "description": "" + } + } + }, + "patch": { + "operationId": "dcim_rack_groups_bulk_partial_update", + "description": "Patch a list of rack group objects.", + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RackGroupRequest" + } + } + }, + "multipart/form-data": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RackGroupRequest" + } + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RackGroup" + } + } + } + }, + "description": "" + } + } + }, + "delete": { + "operationId": "dcim_rack_groups_bulk_destroy", + "description": "Delete a list of rack group objects.", + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RackGroupRequest" + } + } + }, + "multipart/form-data": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RackGroupRequest" + } + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + } + } + } + }, + "/api/dcim/rack-groups/{id}/": { + "get": { + "operationId": "dcim_rack_groups_retrieve", + "description": "Get a rack group object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this rack group.", + "required": true + } + ], + "tags": [ + "dcim" + ], + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RackGroup" + } + } + }, + "description": "" + } + } + }, + "put": { + "operationId": "dcim_rack_groups_update", + "description": "Put a rack group object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this rack group.", + "required": true + } + ], + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RackGroupRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/RackGroupRequest" + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RackGroup" + } + } + }, + "description": "" + } + } + }, + "patch": { + "operationId": "dcim_rack_groups_partial_update", + "description": "Patch a rack group object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this rack group.", + "required": true + } + ], + "tags": [ + "dcim" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedRackGroupRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedRackGroupRequest" + } + } + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RackGroup" + } + } + }, + "description": "" + } + } + }, + "delete": { + "operationId": "dcim_rack_groups_destroy", + "description": "Delete a rack group object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this rack group.", + "required": true + } + ], + "tags": [ + "dcim" + ], + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + } + } + } + }, "/api/dcim/rack-reservations/": { "get": { "operationId": "dcim_rack_reservations_list", @@ -94278,6 +97358,58 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "group", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Group (slug)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "group__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Group (slug)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "group_id", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": "Group (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "group_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": "Group (ID)", + "explode": true, + "style": "form" + }, { "in": "query", "name": "id", @@ -94825,6 +97957,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -95187,6 +98328,22 @@ "type": "number" } }, + { + "in": "query", + "name": "unit_count_max", + "schema": { + "type": "number" + }, + "description": "Maximum unit count" + }, + { + "in": "query", + "name": "unit_count_min", + "schema": { + "type": "number" + }, + "description": "Minimum unit count" + }, { "in": "query", "name": "updated_by_request", @@ -96687,6 +99844,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -98921,6 +102087,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "starting_unit", @@ -100940,6 +104115,60 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "group", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Group (slug)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "group__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Group (slug)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "group_id", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Group (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "group_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Group (ID)", + "explode": true, + "style": "form" + }, { "in": "query", "name": "id", @@ -102505,6 +105734,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "starting_unit", @@ -103926,6 +107164,15 @@ }, "description": "* `json` - json\n* `svg` - svg" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "unit_height", @@ -105060,6 +108307,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "type", @@ -107674,6 +110930,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -109504,6 +112769,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -111027,6 +114301,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -112923,6 +116206,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -114966,6 +118258,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -116487,6 +119788,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -117540,6 +120850,15 @@ "type": "string" } }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "user", @@ -118878,6 +122197,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -120804,6 +124132,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -121774,6 +125111,13 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "debug", + "schema": { + "type": "boolean" + } + }, { "in": "query", "name": "description", @@ -122854,6 +126198,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -123619,6 +126972,17 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "choice_colors", + "schema": { + "type": "array", + "items": {} + }, + "description": "Choice colors", + "explode": true, + "style": "form" + }, { "in": "query", "name": "created", @@ -124352,6 +127716,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "updated_by_request", @@ -126362,6 +129735,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "type", @@ -128941,6 +132323,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "updated_by_request", @@ -130768,6 +134159,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -132738,6 +136138,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "updated_by_request", @@ -134126,6 +137535,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "updated_by_request", @@ -135139,6 +138557,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -135633,6 +139060,15 @@ "schema": { "type": "string" } + }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } } ], "tags": [ @@ -136072,6 +139508,15 @@ "schema": { "type": "string" } + }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } } ], "tags": [ @@ -137544,6 +140989,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "updated_by_request", @@ -138413,6 +141867,15 @@ "type": "string" }, "description": "Search" + }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } } ], "tags": [ @@ -138718,6 +142181,15 @@ "schema": { "type": "string" } + }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } } ], "tags": [ @@ -139813,6 +143285,15 @@ "type": "boolean" } }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "table", @@ -140783,6 +144264,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -141992,6 +145482,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "updated_by_request", @@ -143858,6 +147357,15 @@ "type": "boolean" } }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -145129,6 +148637,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -146714,17 +150231,13 @@ "style": "form" }, { - "in": "query", "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", "schema": { - "type": "array", - "items": { - "type": "integer", - "format": "int32" - } - }, - "explode": true, - "style": "form" + "type": "integer" + } }, { "in": "query", @@ -148235,6 +151748,60 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "role", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Role (slug)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "role__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Role (slug)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "role_id", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Role (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "role_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Role (ID)", + "explode": true, + "style": "form" + }, { "in": "query", "name": "site", @@ -148335,6 +151902,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -149460,6 +153036,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "updated_by_request", @@ -151229,6 +154814,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -153038,6 +156632,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -154761,6 +158364,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "start_address", @@ -156953,6 +160565,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -158998,6 +162619,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -160343,6 +163973,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -161854,6 +165493,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -163356,6 +167004,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -165043,6 +168700,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -166297,6 +169963,13 @@ "type": "integer" } }, + { + "in": "query", + "name": "rack_group", + "schema": { + "type": "number" + } + }, { "in": "query", "name": "region", @@ -166578,6 +170251,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -166728,6 +170410,91 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "total_vlan_ids", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "total_vlan_ids__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "total_vlan_ids__gt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "total_vlan_ids__gte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "total_vlan_ids__lt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "total_vlan_ids__lte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "total_vlan_ids__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, { "in": "query", "name": "updated_by_request", @@ -167977,6 +171744,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -169136,6 +172912,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -170994,6 +174779,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -172819,6 +176613,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -174234,6 +178037,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -175707,6 +179519,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -177052,6 +180873,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -178898,6 +182728,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -180496,6 +184335,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -181965,6 +185813,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -182989,6 +186846,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "user_id", @@ -183848,6 +187714,15 @@ "type": "string" }, "description": "Search" + }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } } ], "tags": [ @@ -184737,6 +188612,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "user", @@ -185954,6 +189838,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "user", @@ -187004,6 +190897,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "user", @@ -188601,6 +192503,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "username", @@ -190117,6 +194028,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -191462,6 +195382,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -193091,6 +197020,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -195242,6 +199180,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -196697,6 +200644,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -197212,6 +201168,1663 @@ } } }, + "/api/virtualization/virtual-machine-types/": { + "get": { + "operationId": "virtualization_virtual_machine_types_list", + "description": "Get a list of virtual machine type objects.", + "parameters": [ + { + "in": "query", + "name": "created", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__empty", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__gt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__gte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__lt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__lte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created__n", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "created_by_request", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "query", + "name": "default_memory", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_memory__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "default_memory__gt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_memory__gte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_memory__lt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_memory__lte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_memory__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_platform", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_platform__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_platform_id", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_platform_id__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_vcpus", + "schema": { + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_vcpus__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "default_vcpus__gt", + "schema": { + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_vcpus__gte", + "schema": { + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_vcpus__lt", + "schema": { + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_vcpus__lte", + "schema": { + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "default_vcpus__n", + "schema": { + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "description__ic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__ie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__iew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__iregex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__isw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__nic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__nie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__niew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__nisw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "description__regex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "id__gt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__gte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__lt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__lte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "id__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__empty", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__gt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__gte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__lt", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__lte", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "last_updated__n", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "date-time" + } + }, + "explode": true, + "style": "form" + }, + { + "name": "limit", + "required": false, + "in": "query", + "description": "Number of results to return per page.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "modified_by_request", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "query", + "name": "name", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "name__ic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__ie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__iew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__iregex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__isw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__nic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__nie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__niew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__nisw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "name__regex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "name": "offset", + "required": false, + "in": "query", + "description": "The initial index from which to return the results.", + "schema": { + "type": "integer" + } + }, + { + "name": "ordering", + "required": false, + "in": "query", + "description": "Which field to use when ordering the results.", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "owner", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner Group (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Owner Group (name)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group_id", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": "Owner Group (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_group_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": "Owner Group (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_id", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Owner (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "owner_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true + } + }, + "description": "Owner (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + }, + "description": "Search" + }, + { + "in": "query", + "name": "slug", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "slug__ic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__ie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__iew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__iregex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__isw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__nic", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__nie", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__niew", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__nisw", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "slug__regex", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "tag", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "tag__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "tag_id", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "tag_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "updated_by_request", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "query", + "name": "virtual_machine_count", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "virtual_machine_count__empty", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "virtual_machine_count__gt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "virtual_machine_count__gte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "virtual_machine_count__lt", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "virtual_machine_count__lte", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "virtual_machine_count__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "explode": true, + "style": "form" + } + ], + "tags": [ + "virtualization" + ], + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedVirtualMachineTypeList" + } + } + }, + "description": "" + } + } + }, + "post": { + "operationId": "virtualization_virtual_machine_types_create", + "description": "Post a list of virtual machine type objects.", + "tags": [ + "virtualization" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + } + } + ] + } + }, + "multipart/form-data": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + } + } + ] + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VirtualMachineType" + } + } + }, + "description": "" + } + } + }, + "put": { + "operationId": "virtualization_virtual_machine_types_bulk_update", + "description": "Put a list of virtual machine type objects.", + "tags": [ + "virtualization" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + } + } + }, + "multipart/form-data": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + } + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VirtualMachineType" + } + } + } + }, + "description": "" + } + } + }, + "patch": { + "operationId": "virtualization_virtual_machine_types_bulk_partial_update", + "description": "Patch a list of virtual machine type objects.", + "tags": [ + "virtualization" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + } + } + }, + "multipart/form-data": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + } + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VirtualMachineType" + } + } + } + }, + "description": "" + } + } + }, + "delete": { + "operationId": "virtualization_virtual_machine_types_bulk_destroy", + "description": "Delete a list of virtual machine type objects.", + "tags": [ + "virtualization" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + } + } + }, + "multipart/form-data": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + } + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + } + } + } + }, + "/api/virtualization/virtual-machine-types/{id}/": { + "get": { + "operationId": "virtualization_virtual_machine_types_retrieve", + "description": "Get a virtual machine type object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this virtual machine type.", + "required": true + } + ], + "tags": [ + "virtualization" + ], + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VirtualMachineType" + } + } + }, + "description": "" + } + } + }, + "put": { + "operationId": "virtualization_virtual_machine_types_update", + "description": "Put a virtual machine type object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this virtual machine type.", + "required": true + } + ], + "tags": [ + "virtualization" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/VirtualMachineTypeRequest" + } + } + }, + "required": true + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VirtualMachineType" + } + } + }, + "description": "" + } + } + }, + "patch": { + "operationId": "virtualization_virtual_machine_types_partial_update", + "description": "Patch a virtual machine type object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this virtual machine type.", + "required": true + } + ], + "tags": [ + "virtualization" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedVirtualMachineTypeRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedVirtualMachineTypeRequest" + } + } + } + }, + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VirtualMachineType" + } + } + }, + "description": "" + } + } + }, + "delete": { + "operationId": "virtualization_virtual_machine_types_destroy", + "description": "Delete a virtual machine type object.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "description": "A unique integer value identifying this virtual machine type.", + "required": true + } + ], + "tags": [ + "virtualization" + ], + "security": [ + { + "cookieAuth": [] + }, + { + "tokenAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + } + } + } + }, "/api/virtualization/virtual-machines/": { "get": { "operationId": "virtualization_virtual_machines_list", @@ -199177,6 +204790,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "start_on_boot", @@ -199830,6 +205452,62 @@ }, "explode": true, "style": "form" + }, + { + "in": "query", + "name": "virtual_machine_type", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Virtual machine type (slug)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "virtual_machine_type__n", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Virtual machine type (slug)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "virtual_machine_type_id", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true, + "title": "Type" + } + }, + "description": "Virtual machine type (ID)", + "explode": true, + "style": "form" + }, + { + "in": "query", + "name": "virtual_machine_type_id__n", + "schema": { + "type": "array", + "items": { + "type": "integer", + "nullable": true, + "title": "Type" + } + }, + "description": "Virtual machine type (ID)", + "explode": true, + "style": "form" } ], "tags": [ @@ -201347,6 +207025,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -203503,6 +209190,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -204925,6 +210621,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -206386,6 +212091,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -208176,6 +213890,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -209303,6 +215026,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -211125,6 +216857,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -212974,6 +218715,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -214001,6 +219751,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -215755,6 +221514,15 @@ }, "description": "Search" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -217548,6 +223316,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "tag", @@ -219644,6 +225421,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -221948,6 +227734,15 @@ "explode": true, "style": "form" }, + { + "name": "start", + "required": false, + "in": "query", + "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.", + "schema": { + "type": "integer" + } + }, { "in": "query", "name": "status", @@ -222716,6 +228511,14 @@ ], "nullable": true }, + "role": { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRole" + } + ], + "nullable": true + }, "tenant": { "allOf": [ { @@ -223023,6 +228826,22 @@ ], "nullable": true }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "tenant": { "oneOf": [ { @@ -223768,6 +229587,57 @@ "url" ] }, + "BriefCableBundle": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "display": { + "type": "string", + "readOnly": true + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "display", + "id", + "name", + "url" + ] + }, + "BriefCableBundleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "name" + ] + }, "BriefCableRequest": { "type": "object", "description": "Base serializer class for models inheriting from PrimaryModel.", @@ -224990,6 +230860,21 @@ "address": { "type": "string" }, + "nat_inside": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedIPAddress" + } + ], + "nullable": true + }, + "nat_outside": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedIPAddress" + }, + "readOnly": true + }, "description": { "type": "string", "maxLength": 200 @@ -225000,6 +230885,7 @@ "display", "family", "id", + "nat_outside", "url" ] }, @@ -225011,6 +230897,14 @@ "type": "string", "minLength": 1 }, + "nat_inside": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedIPAddressRequest" + } + ], + "nullable": true + }, "description": { "type": "string", "maxLength": 200 @@ -226600,6 +232494,76 @@ "url" ] }, + "BriefRackGroup": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "display": { + "type": "string", + "readOnly": true + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "slug": { + "type": "string", + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "rack_count": { + "type": "integer", + "format": "int64", + "readOnly": true + } + }, + "required": [ + "display", + "id", + "name", + "rack_count", + "slug", + "url" + ] + }, + "BriefRackGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "name", + "slug" + ] + }, "BriefRackRequest": { "type": "object", "description": "Base serializer class for models inheriting from PrimaryModel.", @@ -226887,9 +232851,15 @@ "type": "integer", "format": "int64", "readOnly": true + }, + "asn_count": { + "type": "integer", + "format": "int64", + "readOnly": true } }, "required": [ + "asn_count", "display", "id", "name", @@ -227940,6 +233910,70 @@ "name" ] }, + "BriefVirtualMachineType": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "display": { + "type": "string", + "readOnly": true + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "slug": { + "type": "string", + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "display", + "id", + "name", + "slug", + "url" + ] + }, + "BriefVirtualMachineTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "name", + "slug" + ] + }, "BriefWirelessLANGroup": { "type": "object", "description": "Base serializer class for models inheriting from NestedGroupModel.", @@ -228191,6 +234225,14 @@ ], "nullable": true }, + "bundle": { + "allOf": [ + { + "$ref": "#/components/schemas/BriefCableBundle" + } + ], + "nullable": true + }, "label": { "type": "string", "maxLength": 100 @@ -228288,6 +234330,133 @@ "url" ] }, + "CableBundle": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "display_url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "display": { + "type": "string", + "readOnly": true + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwner" + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTag" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "created": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "last_updated": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "cable_count": { + "type": "integer", + "readOnly": true, + "default": 0 + } + }, + "required": [ + "cable_count", + "created", + "display", + "display_url", + "id", + "last_updated", + "name", + "url" + ] + }, + "CableBundleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "name" + ] + }, "CableRequest": { "type": "object", "description": "Base serializer class for models inheriting from PrimaryModel.", @@ -228405,6 +234574,22 @@ ], "nullable": true }, + "bundle": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefCableBundleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "label": { "type": "string", "maxLength": 100 @@ -230840,6 +237025,10 @@ "type": "boolean", "description": "Download file as attachment" }, + "debug": { + "type": "boolean", + "description": "Enable verbose error output when rendering this template. Not recommended for production use." + }, "data_source": { "$ref": "#/components/schemas/BriefDataSource" }, @@ -230944,6 +237133,10 @@ "type": "boolean", "description": "Download file as attachment" }, + "debug": { + "type": "boolean", + "description": "Enable verbose error output when rendering this template. Not recommended for production use." + }, "data_source": { "oneOf": [ { @@ -233046,6 +239239,10 @@ "description": "Regular expression to enforce on text field values. Use ^ and $ to force matching of entire string. For example, ^[A-Z]{3}$ will limit values to exactly three uppercase letters.", "maxLength": 500 }, + "validation_schema": { + "nullable": true, + "description": "A JSON schema definition for validating the custom field value" + }, "choice_set": { "allOf": [ { @@ -233153,6 +239350,29 @@ "minItems": 2 } }, + "choice_colors": { + "type": "object", + "additionalProperties": { + "enum": [ + "blue", + "indigo", + "purple", + "pink", + "red", + "orange", + "yellow", + "green", + "teal", + "cyan", + "gray", + "black", + "white" + ], + "type": "string", + "description": "* `blue` - Blue\n* `indigo` - Indigo\n* `purple` - Purple\n* `pink` - Pink\n* `red` - Red\n* `orange` - Orange\n* `yellow` - Yellow\n* `green` - Green\n* `teal` - Teal\n* `cyan` - Cyan\n* `gray` - Gray\n* `black` - Black\n* `white` - White", + "x-spec-enum-id": "de0a6a124020dfe0" + } + }, "order_alphabetically": { "type": "boolean", "description": "Choices are automatically ordered alphabetically" @@ -233226,6 +239446,29 @@ "minItems": 2 } }, + "choice_colors": { + "type": "object", + "additionalProperties": { + "enum": [ + "blue", + "indigo", + "purple", + "pink", + "red", + "orange", + "yellow", + "green", + "teal", + "cyan", + "gray", + "black", + "white" + ], + "type": "string", + "description": "* `blue` - Blue\n* `indigo` - Indigo\n* `purple` - Purple\n* `pink` - Pink\n* `red` - Red\n* `orange` - Orange\n* `yellow` - Yellow\n* `green` - Green\n* `teal` - Teal\n* `cyan` - Cyan\n* `gray` - Gray\n* `black` - Black\n* `white` - White", + "x-spec-enum-id": "de0a6a124020dfe0" + } + }, "order_alphabetically": { "type": "boolean", "description": "Choices are automatically ordered alphabetically" @@ -233398,6 +239641,10 @@ "description": "Regular expression to enforce on text field values. Use ^ and $ to force matching of entire string. For example, ^[A-Z]{3}$ will limit values to exactly three uppercase letters.", "maxLength": 500 }, + "validation_schema": { + "nullable": true, + "description": "A JSON schema definition for validating the custom field value" + }, "choice_set": { "oneOf": [ { @@ -234386,6 +240633,9 @@ "description": "Physical label", "maxLength": 64 }, + "enabled": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 200 @@ -234427,9 +240677,15 @@ "format": "date-time", "readOnly": true, "nullable": true + }, + "_occupied": { + "type": "boolean", + "readOnly": true, + "title": " occupied" } }, "required": [ + "_occupied", "created", "device", "display", @@ -234464,6 +240720,9 @@ "description": "Physical label", "maxLength": 64 }, + "enabled": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 200 @@ -234546,6 +240805,9 @@ "description": "Physical label", "maxLength": 64 }, + "enabled": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 200 @@ -234598,6 +240860,9 @@ "description": "Physical label", "maxLength": 64 }, + "enabled": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 200 @@ -243514,6 +249779,30 @@ "description": "Name of the queue in which this job was enqueued", "maxLength": 100 }, + "notifications": { + "type": "object", + "properties": { + "value": { + "enum": [ + "always", + "on_failure", + "never" + ], + "type": "string", + "description": "* `always` - Always\n* `on_failure` - On failure\n* `never` - Never", + "x-spec-enum-id": "57071f4400340a5c" + }, + "label": { + "type": "string", + "enum": [ + "Always", + "On failure", + "Never" + ] + } + }, + "readOnly": true + }, "log_entries": { "type": "array", "items": {} @@ -243527,6 +249816,7 @@ "id", "job_id", "name", + "notifications", "object", "object_type", "status", @@ -244819,14 +251109,6 @@ "type": "string", "maxLength": 64 }, - "installed_module": { - "allOf": [ - { - "$ref": "#/components/schemas/BriefModule" - } - ], - "nullable": true - }, "label": { "type": "string", "description": "Physical label", @@ -244837,10 +251119,21 @@ "description": "Identifier to reference when renaming installed components", "maxLength": 30 }, + "enabled": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 200 }, + "installed_module": { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModule" + } + ], + "nullable": true + }, "owner": { "allOf": [ { @@ -244870,9 +251163,15 @@ "format": "date-time", "readOnly": true, "nullable": true + }, + "_occupied": { + "type": "boolean", + "readOnly": true, + "title": " occupied" } }, "required": [ + "_occupied", "created", "device", "display", @@ -244918,6 +251217,23 @@ "minLength": 1, "maxLength": 64 }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "position": { + "type": "string", + "description": "Identifier to reference when renaming installed components", + "maxLength": 30 + }, + "enabled": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 200 + }, "installed_module": { "oneOf": [ { @@ -244934,20 +251250,6 @@ ], "nullable": true }, - "label": { - "type": "string", - "description": "Physical label", - "maxLength": 64 - }, - "position": { - "type": "string", - "description": "Identifier to reference when renaming installed components", - "maxLength": 30 - }, - "description": { - "type": "string", - "maxLength": 200 - }, "owner": { "oneOf": [ { @@ -245028,6 +251330,9 @@ "description": "Identifier to reference when renaming installed components", "maxLength": 30 }, + "enabled": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 200 @@ -245106,6 +251411,9 @@ "description": "Identifier to reference when renaming installed components", "maxLength": 30 }, + "enabled": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 200 @@ -245198,6 +251506,18 @@ "custom_fields": { "type": "object", "additionalProperties": {} + }, + "replicate_components": { + "type": "boolean", + "writeOnly": true, + "default": true, + "description": "Automatically populate components associated with this module type (default: true)" + }, + "adopt_components": { + "type": "boolean", + "writeOnly": true, + "default": false, + "description": "Adopt already existing components" } }, "required": [ @@ -247615,6 +253935,37 @@ } } }, + "PaginatedCableBundleList": { + "type": "object", + "required": [ + "count", + "results" + ], + "properties": { + "count": { + "type": "integer", + "example": 123 + }, + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=400&limit=100" + }, + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=200&limit=100" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CableBundle" + } + } + } + }, "PaginatedCableList": { "type": "object", "required": [ @@ -250157,6 +256508,37 @@ } } }, + "PaginatedRackGroupList": { + "type": "object", + "required": [ + "count", + "results" + ], + "properties": { + "count": { + "type": "integer", + "example": 123 + }, + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=400&limit=100" + }, + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=200&limit=100" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RackGroup" + } + } + } + }, "PaginatedRackList": { "type": "object", "required": [ @@ -251366,6 +257748,37 @@ } } }, + "PaginatedVirtualMachineTypeList": { + "type": "object", + "required": [ + "count", + "results" + ], + "properties": { + "count": { + "type": "integer", + "example": 123 + }, + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=400&limit=100" + }, + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=200&limit=100" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VirtualMachineType" + } + } + } + }, "PaginatedVirtualMachineWithConfigContextList": { "type": "object", "required": [ @@ -251636,6 +258049,22 @@ ], "nullable": true }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "tenant": { "oneOf": [ { @@ -251718,6 +258147,50 @@ } } }, + "PatchedCableBundleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + } + }, "PatchedCircuitGroupRequest": { "type": "object", "description": "Base serializer class for models inheriting from OrganizationalModel.", @@ -252260,6 +258733,10 @@ "type": "boolean", "description": "Download file as attachment" }, + "debug": { + "type": "boolean", + "description": "Enable verbose error output when rendering this template. Not recommended for production use." + }, "data_source": { "oneOf": [ { @@ -252533,6 +259010,9 @@ "description": "Physical label", "maxLength": 64 }, + "enabled": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 200 @@ -252606,6 +259086,9 @@ "description": "Physical label", "maxLength": 64 }, + "enabled": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 200 @@ -253159,6 +259642,23 @@ "minLength": 1, "maxLength": 64 }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "position": { + "type": "string", + "description": "Identifier to reference when renaming installed components", + "maxLength": 30 + }, + "enabled": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 200 + }, "installed_module": { "oneOf": [ { @@ -253175,20 +259675,6 @@ ], "nullable": true }, - "label": { - "type": "string", - "description": "Physical label", - "maxLength": 64 - }, - "position": { - "type": "string", - "description": "Identifier to reference when renaming installed components", - "maxLength": 30 - }, - "description": { - "type": "string", - "maxLength": 200 - }, "owner": { "oneOf": [ { @@ -253269,6 +259755,9 @@ "description": "Identifier to reference when renaming installed components", "maxLength": 30 }, + "enabled": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 200 @@ -253807,6 +260296,56 @@ } } }, + "PatchedRackGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + } + }, "PatchedRackRoleRequest": { "type": "object", "description": "Base serializer class for models inheriting from OrganizationalModel.", @@ -254053,6 +260592,17 @@ "interval": { "type": "integer", "nullable": true + }, + "notifications": { + "enum": [ + "always", + "on_failure", + "never" + ], + "type": "string", + "description": "* `always` - Always\n* `on_failure` - On failure\n* `never` - Never", + "x-spec-enum-id": "57071f4400340a5c", + "default": "always" } } }, @@ -254745,6 +261295,87 @@ } } }, + "PatchedVirtualMachineTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "default_platform": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPlatformRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "default_vcpus": { + "type": "number", + "format": "double", + "maximum": 10000, + "minimum": 0.01, + "exclusiveMaximum": true, + "nullable": true + }, + "default_memory": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "Default memory (MB)" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + } + }, "PatchedWebhookRequest": { "type": "object", "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", @@ -255026,6 +261657,22 @@ ], "nullable": true }, + "bundle": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefCableBundleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "label": { "type": "string", "maxLength": 100 @@ -255956,6 +262603,29 @@ "minItems": 2 } }, + "choice_colors": { + "type": "object", + "additionalProperties": { + "enum": [ + "blue", + "indigo", + "purple", + "pink", + "red", + "orange", + "yellow", + "green", + "teal", + "cyan", + "gray", + "black", + "white" + ], + "type": "string", + "description": "* `blue` - Blue\n* `indigo` - Indigo\n* `purple` - Purple\n* `pink` - Pink\n* `red` - Red\n* `orange` - Orange\n* `yellow` - Yellow\n* `green` - Green\n* `teal` - Teal\n* `cyan` - Cyan\n* `gray` - Gray\n* `black` - Black\n* `white` - White", + "x-spec-enum-id": "de0a6a124020dfe0" + } + }, "order_alphabetically": { "type": "boolean", "description": "Choices are automatically ordered alphabetically" @@ -256124,6 +262794,10 @@ "description": "Regular expression to enforce on text field values. Use ^ and $ to force matching of entire string. For example, ^[A-Z]{3}$ will limit values to exactly three uppercase letters.", "maxLength": 500 }, + "validation_schema": { + "nullable": true, + "description": "A JSON schema definition for validating the custom field value" + }, "choice_set": { "oneOf": [ { @@ -259447,6 +266121,18 @@ "custom_fields": { "type": "object", "additionalProperties": {} + }, + "replicate_components": { + "type": "boolean", + "writeOnly": true, + "default": true, + "description": "Automatically populate components associated with this module type (default: true)" + }, + "adopt_components": { + "type": "boolean", + "writeOnly": true, + "default": false, + "description": "Adopt already existing components" } } }, @@ -260788,6 +267474,22 @@ ], "nullable": true }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "tenant": { "oneOf": [ { @@ -262878,6 +269580,38 @@ "minLength": 1, "maxLength": 64 }, + "virtual_machine_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVirtualMachineTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "status": { "enum": [ "offline", @@ -262950,43 +269684,6 @@ ], "nullable": true }, - "serial": { - "type": "string", - "title": "Serial number", - "maxLength": 50 - }, - "role": { - "oneOf": [ - { - "type": "integer" - }, - { - "allOf": [ - { - "$ref": "#/components/schemas/BriefDeviceRoleRequest" - } - ], - "nullable": true - } - ], - "nullable": true - }, - "tenant": { - "oneOf": [ - { - "type": "integer" - }, - { - "allOf": [ - { - "$ref": "#/components/schemas/BriefTenantRequest" - } - ], - "nullable": true - } - ], - "nullable": true - }, "platform": { "oneOf": [ { @@ -263059,6 +269756,27 @@ "type": "string", "maxLength": 200 }, + "serial": { + "type": "string", + "title": "Serial number", + "maxLength": 50 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "owner": { "oneOf": [ { @@ -263078,6 +269796,16 @@ "comments": { "type": "string" }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "local_context_data": { + "nullable": true, + "description": "Local config context data takes precedence over source contexts in the final rendered config context" + }, "config_template": { "oneOf": [ { @@ -263094,16 +269822,6 @@ ], "nullable": true }, - "local_context_data": { - "nullable": true, - "description": "Local config context data takes precedence over source contexts in the final rendered config context" - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NestedTagRequest" - } - }, "custom_fields": { "type": "object", "additionalProperties": {} @@ -267537,6 +274255,14 @@ ], "nullable": true }, + "group": { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackGroup" + } + ], + "nullable": true + }, "tenant": { "allOf": [ { @@ -267855,6 +274581,146 @@ "url" ] }, + "RackGroup": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "display_url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "display": { + "type": "string", + "readOnly": true + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "slug": { + "type": "string", + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwner" + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTag" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "created": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "last_updated": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "rack_count": { + "type": "integer", + "format": "int64", + "readOnly": true + } + }, + "required": [ + "created", + "display", + "display_url", + "id", + "last_updated", + "name", + "rack_count", + "slug", + "url" + ] + }, + "RackGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "name", + "slug" + ] + }, "RackRequest": { "type": "object", "description": "Base serializer class for models inheriting from PrimaryModel.", @@ -267895,6 +274761,22 @@ ], "nullable": true }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "tenant": { "oneOf": [ { @@ -268164,6 +275046,11 @@ "minimum": 0 } }, + "unit_count": { + "type": "integer", + "format": "int32", + "readOnly": true + }, "status": { "type": "object", "properties": { @@ -268244,6 +275131,7 @@ "id", "last_updated", "rack", + "unit_count", "units", "url", "user" @@ -270145,9 +277033,15 @@ "type": "integer", "format": "int64", "readOnly": true + }, + "asn_count": { + "type": "integer", + "format": "int64", + "readOnly": true } }, "required": [ + "asn_count", "created", "display", "display_url", @@ -270602,6 +277496,17 @@ "interval": { "type": "integer", "nullable": true + }, + "notifications": { + "enum": [ + "always", + "on_failure", + "never" + ], + "type": "string", + "description": "* `always` - Always\n* `on_failure` - On failure\n* `never` - Never", + "x-spec-enum-id": "57071f4400340a5c", + "default": "always" } }, "required": [ @@ -273640,6 +280545,10 @@ "$ref": "#/components/schemas/IntegerRange" } }, + "total_vlan_ids": { + "type": "integer", + "readOnly": true + }, "tenant": { "allOf": [ { @@ -273704,6 +280613,7 @@ "name", "scope", "slug", + "total_vlan_ids", "url", "utilization", "vlan_count" @@ -275809,6 +282719,199 @@ "virtual_machine" ] }, + "VirtualMachineType": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "display_url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "display": { + "type": "string", + "readOnly": true + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "slug": { + "type": "string", + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "default_platform": { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPlatform" + } + ], + "nullable": true + }, + "default_vcpus": { + "type": "number", + "format": "double", + "maximum": 10000, + "minimum": 0.01, + "exclusiveMaximum": true, + "nullable": true + }, + "default_memory": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "Default memory (MB)" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwner" + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTag" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "created": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "last_updated": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "virtual_machine_count": { + "type": "integer", + "readOnly": true + } + }, + "required": [ + "created", + "display", + "display_url", + "id", + "last_updated", + "name", + "slug", + "url", + "virtual_machine_count" + ] + }, + "VirtualMachineTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "default_platform": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPlatformRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "default_vcpus": { + "type": "number", + "format": "double", + "maximum": 10000, + "minimum": 0.01, + "exclusiveMaximum": true, + "nullable": true + }, + "default_memory": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "Default memory (MB)" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "name", + "slug" + ] + }, "VirtualMachineWithConfigContext": { "type": "object", "description": "Base serializer class for models inheriting from PrimaryModel.", @@ -275835,6 +282938,22 @@ "type": "string", "maxLength": 64 }, + "virtual_machine_type": { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVirtualMachineType" + } + ], + "nullable": true + }, + "role": { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceRole" + } + ], + "nullable": true + }, "status": { "type": "object", "properties": { @@ -275913,27 +283032,6 @@ ], "nullable": true }, - "serial": { - "type": "string", - "title": "Serial number", - "maxLength": 50 - }, - "role": { - "allOf": [ - { - "$ref": "#/components/schemas/BriefDeviceRole" - } - ], - "nullable": true - }, - "tenant": { - "allOf": [ - { - "$ref": "#/components/schemas/BriefTenant" - } - ], - "nullable": true - }, "platform": { "allOf": [ { @@ -275991,6 +283089,19 @@ "type": "string", "maxLength": 200 }, + "serial": { + "type": "string", + "title": "Serial number", + "maxLength": 50 + }, + "tenant": { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenant" + } + ], + "nullable": true + }, "owner": { "allOf": [ { @@ -276002,6 +283113,16 @@ "comments": { "type": "string" }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTag" + } + }, + "local_context_data": { + "nullable": true, + "description": "Local config context data takes precedence over source contexts in the final rendered config context" + }, "config_template": { "allOf": [ { @@ -276010,24 +283131,10 @@ ], "nullable": true }, - "local_context_data": { - "nullable": true, - "description": "Local config context data takes precedence over source contexts in the final rendered config context" - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NestedTag" - } - }, "custom_fields": { "type": "object", "additionalProperties": {} }, - "config_context": { - "nullable": true, - "readOnly": true - }, "created": { "type": "string", "format": "date-time", @@ -276047,6 +283154,10 @@ "virtual_disk_count": { "type": "integer", "readOnly": true + }, + "config_context": { + "nullable": true, + "readOnly": true } }, "required": [ @@ -276072,6 +283183,38 @@ "minLength": 1, "maxLength": 64 }, + "virtual_machine_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVirtualMachineTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "status": { "enum": [ "offline", @@ -276144,43 +283287,6 @@ ], "nullable": true }, - "serial": { - "type": "string", - "title": "Serial number", - "maxLength": 50 - }, - "role": { - "oneOf": [ - { - "type": "integer" - }, - { - "allOf": [ - { - "$ref": "#/components/schemas/BriefDeviceRoleRequest" - } - ], - "nullable": true - } - ], - "nullable": true - }, - "tenant": { - "oneOf": [ - { - "type": "integer" - }, - { - "allOf": [ - { - "$ref": "#/components/schemas/BriefTenantRequest" - } - ], - "nullable": true - } - ], - "nullable": true - }, "platform": { "oneOf": [ { @@ -276253,6 +283359,27 @@ "type": "string", "maxLength": 200 }, + "serial": { + "type": "string", + "title": "Serial number", + "maxLength": 50 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "owner": { "oneOf": [ { @@ -276272,6 +283399,16 @@ "comments": { "type": "string" }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "local_context_data": { + "nullable": true, + "description": "Local config context data takes precedence over source contexts in the final rendered config context" + }, "config_template": { "oneOf": [ { @@ -276288,16 +283425,6 @@ ], "nullable": true }, - "local_context_data": { - "nullable": true, - "description": "Local config context data takes precedence over source contexts in the final rendered config context" - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NestedTagRequest" - } - }, "custom_fields": { "type": "object", "additionalProperties": {} @@ -277559,6 +284686,22 @@ ], "nullable": true }, + "bundle": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefCableBundleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "label": { "type": "string", "maxLength": 100 @@ -278526,6 +285669,29 @@ "minItems": 2 } }, + "choice_colors": { + "type": "object", + "additionalProperties": { + "enum": [ + "blue", + "indigo", + "purple", + "pink", + "red", + "orange", + "yellow", + "green", + "teal", + "cyan", + "gray", + "black", + "white" + ], + "type": "string", + "description": "* `blue` - Blue\n* `indigo` - Indigo\n* `purple` - Purple\n* `pink` - Pink\n* `red` - Red\n* `orange` - Orange\n* `yellow` - Yellow\n* `green` - Green\n* `teal` - Teal\n* `cyan` - Cyan\n* `gray` - Gray\n* `black` - Black\n* `white` - White", + "x-spec-enum-id": "de0a6a124020dfe0" + } + }, "order_alphabetically": { "type": "boolean", "description": "Choices are automatically ordered alphabetically" @@ -278698,6 +285864,10 @@ "description": "Regular expression to enforce on text field values. Use ^ and $ to force matching of entire string. For example, ^[A-Z]{3}$ will limit values to exactly three uppercase letters.", "maxLength": 500 }, + "validation_schema": { + "nullable": true, + "description": "A JSON schema definition for validating the custom field value" + }, "choice_set": { "oneOf": [ { @@ -282115,6 +289285,18 @@ "custom_fields": { "type": "object", "additionalProperties": {} + }, + "replicate_components": { + "type": "boolean", + "writeOnly": true, + "default": true, + "description": "Automatically populate components associated with this module type (default: true)" + }, + "adopt_components": { + "type": "boolean", + "writeOnly": true, + "default": false, + "description": "Adopt already existing components" } }, "required": [ @@ -283490,6 +290672,22 @@ ], "nullable": true }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "tenant": { "oneOf": [ { @@ -285666,6 +292864,38 @@ "minLength": 1, "maxLength": 64 }, + "virtual_machine_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVirtualMachineTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "status": { "enum": [ "offline", @@ -285738,43 +292968,6 @@ ], "nullable": true }, - "serial": { - "type": "string", - "title": "Serial number", - "maxLength": 50 - }, - "role": { - "oneOf": [ - { - "type": "integer" - }, - { - "allOf": [ - { - "$ref": "#/components/schemas/BriefDeviceRoleRequest" - } - ], - "nullable": true - } - ], - "nullable": true - }, - "tenant": { - "oneOf": [ - { - "type": "integer" - }, - { - "allOf": [ - { - "$ref": "#/components/schemas/BriefTenantRequest" - } - ], - "nullable": true - } - ], - "nullable": true - }, "platform": { "oneOf": [ { @@ -285847,6 +293040,27 @@ "type": "string", "maxLength": 200 }, + "serial": { + "type": "string", + "title": "Serial number", + "maxLength": 50 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, "owner": { "oneOf": [ { @@ -285866,6 +293080,16 @@ "comments": { "type": "string" }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "local_context_data": { + "nullable": true, + "description": "Local config context data takes precedence over source contexts in the final rendered config context" + }, "config_template": { "oneOf": [ { @@ -285882,16 +293106,6 @@ ], "nullable": true }, - "local_context_data": { - "nullable": true, - "description": "Local config context data takes precedence over source contexts in the final rendered config context" - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NestedTagRequest" - } - }, "custom_fields": { "type": "object", "additionalProperties": {} diff --git a/docs/_theme/partials/copyright.html b/docs/_theme/partials/copyright.html deleted file mode 100644 index cfbfd5444..000000000 --- a/docs/_theme/partials/copyright.html +++ /dev/null @@ -1,18 +0,0 @@ - -{% if not config.extra.build_public %} - -{% endif %} diff --git a/docs/administration/permissions.md b/docs/administration/permissions.md index a73eddcd8..0b7b88bcd 100644 --- a/docs/administration/permissions.md +++ b/docs/administration/permissions.md @@ -20,7 +20,9 @@ There are four core actions that can be permitted for each type of object within * **Change** - Modify an existing object * **Delete** - Delete an existing object -In addition to these, permissions can also grant custom actions that may be required by a specific model or plugin. For example, the `run` permission for scripts allows a user to execute custom scripts. These can be specified when granting a permission in the "additional actions" field. +In addition to these, permissions can also grant custom actions that may be required by a specific model or plugin. For example, the `sync` action for data sources allows a user to synchronize data from a remote source, and the `render_config` action for devices and virtual machines allows rendering configuration templates. + +Some models have registered actions that appear as checkboxes in the "Actions" section when creating or editing a permission. These are shown in a flat list alongside the built-in CRUD actions. Additional actions (such as those not yet registered by a plugin, or for backwards compatibility) can be entered manually in the "Additional actions" field. !!! note Internally, all actions granted by a permission (both built-in and custom) are stored as strings in an array field named `actions`. diff --git a/docs/configuration/development.md b/docs/configuration/development.md index a76fb80b0..de6969800 100644 --- a/docs/configuration/development.md +++ b/docs/configuration/development.md @@ -4,9 +4,9 @@ Default: `False` -This setting enables debugging. Debugging should be enabled only during development or troubleshooting. Note that only -clients which access NetBox from a recognized [internal IP address](./system.md#internal_ips) will see debugging tools in the user -interface. +This setting enables debugging and displays a debugging toolbar in the user interface. Debugging should be enabled only during development or troubleshooting. + +Note that the debugging toolbar will be displayed only for requests originating from [internal IP addresses](./system.md#internal_ips), if defined. If no internal IPs are defined, the toolbar will be displayed for all requests. !!! warning Never enable debugging on a production system, as it can expose sensitive data to unauthenticated users and impose a diff --git a/docs/configuration/index.md b/docs/configuration/index.md index e3e2d061c..0ae37b134 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -21,6 +21,7 @@ Some configuration parameters are primarily controlled via NetBox's admin interf * [`BANNER_BOTTOM`](./miscellaneous.md#banner_bottom) * [`BANNER_LOGIN`](./miscellaneous.md#banner_login) * [`BANNER_TOP`](./miscellaneous.md#banner_top) +* [`CHANGELOG_RETAIN_CREATE_LAST_UPDATE`](./miscellaneous.md#changelog_retain_create_last_update) * [`CHANGELOG_RETENTION`](./miscellaneous.md#changelog_retention) * [`CUSTOM_VALIDATORS`](./data-validation.md#custom_validators) * [`DEFAULT_USER_PREFERENCES`](./default-values.md#default_user_preferences) diff --git a/docs/configuration/miscellaneous.md b/docs/configuration/miscellaneous.md index 382cc8620..650833129 100644 --- a/docs/configuration/miscellaneous.md +++ b/docs/configuration/miscellaneous.md @@ -73,6 +73,23 @@ This data enables the project maintainers to estimate how many NetBox deployment --- +## CHANGELOG_RETAIN_CREATE_LAST_UPDATE + +!!! tip "Dynamic Configuration Parameter" + +Default: `False` + +When pruning expired changelog entries (per `CHANGELOG_RETENTION`), retain each non-deleted object's original `create` +change record and its most recent `update` change record. If an object has a `delete` change record, its changelog +entries are pruned normally according to `CHANGELOG_RETENTION`. + +!!! note + For objects without a `delete` change record, the original `create` record and most recent `update` record are + exempt from pruning. All other changelog records (including intermediate `update` records and all `delete` records) + remain subject to pruning per `CHANGELOG_RETENTION`. + +--- + ## CHANGELOG_RETENTION !!! tip "Dynamic Configuration Parameter" @@ -106,6 +123,18 @@ The maximum size (in bytes) of an incoming HTTP request (i.e. `GET` or `POST` da --- +## STREAMING_EXPORTS + +!!! note "This parameter was introduced in NetBox v4.6." + +Default: `False` + +When set to `True`, CSV bulk exports are returned as a streaming HTTP response, emitting rows to the client as they are rendered rather than buffering the entire dataset in memory first. This can significantly reduce memory usage and time-to-first-byte for very large exports. + +Because streaming responses do not have a `Content-Length` header and defer errors until after the response has begun, this behavior is opt-in. + +--- + ## ENFORCE_GLOBAL_UNIQUE !!! tip "Dynamic Configuration Parameter" diff --git a/docs/configuration/security.md b/docs/configuration/security.md index f7d7821f3..3fc789f4c 100644 --- a/docs/configuration/security.md +++ b/docs/configuration/security.md @@ -161,6 +161,9 @@ Note that enabling this setting causes NetBox to update a user's session in the ## LOGIN_REQUIRED +!!! warning "Legacy Configuration Parameter" + The `LOGIN_REQUIRED` configuration parameter is deprecated and will be removed in NetBox v5.0. Unauthenticated access to the application will no longer be supported once this configuration parameter is removed. + Default: `True` When enabled, only authenticated users are permitted to access any part of NetBox. Disabling this will allow unauthenticated users to access most areas of NetBox (but not make any changes). diff --git a/docs/configuration/system.md b/docs/configuration/system.md index f0f8d122d..9c40a41d8 100644 --- a/docs/configuration/system.md +++ b/docs/configuration/system.md @@ -105,6 +105,13 @@ A list of IP addresses recognized as internal to the system, used to control the example, the debugging toolbar will be viewable only when a client is accessing NetBox from one of the listed IP addresses (and [`DEBUG`](./development.md#debug) is `True`). +!!! info "New in NetBox v4.6" + Setting this parameter to an empty list will enable the toolbar for all requests provided debugging is enabled: + + ```python + INTERNAL_IPS = [] + ``` + --- ## ISOLATED_DEPLOYMENT diff --git a/docs/customization/custom-fields.md b/docs/customization/custom-fields.md index 4658cc7e6..eae35f3d0 100644 --- a/docs/customization/custom-fields.md +++ b/docs/customization/custom-fields.md @@ -63,6 +63,7 @@ NetBox supports limited custom validation for custom field values. Following are * Text: Regular expression (optional) * Integer: Minimum and/or maximum value (optional) * Selection: Must exactly match one of the prescribed choices +* JSON: Must adhere to the defined validation schema (if any) ### Custom Selection Fields diff --git a/docs/customization/custom-scripts.md b/docs/customization/custom-scripts.md index 0947a2c94..cd6269a7c 100644 --- a/docs/customization/custom-scripts.md +++ b/docs/customization/custom-scripts.md @@ -115,6 +115,20 @@ commit_default = False By default, a script can be scheduled for execution at a later time. Setting `scheduling_enabled` to False disables this ability: Only immediate execution will be possible. (This also disables the ability to set a recurring execution interval.) +### `notifications_default` + +By default, a notification is generated for the requesting user each time a script finishes running. This attribute sets the initial value for the notifications field when running a script. Valid values are `always` (default), `on_failure`, and `never`. + +```python +notifications_default = 'on_failure' +``` + +| Value | Behavior | +|-------|----------| +| `always` | Notify on every completion (default) | +| `on_failure` | Notify only when the job fails or errors | +| `never` | Never send a notification | + ### `job_timeout` Set the maximum allowed runtime for the script. If not set, `RQ_DEFAULT_TIMEOUT` will be used. diff --git a/docs/development/application-registry.md b/docs/development/application-registry.md index 9503f8ff0..7946d9df4 100644 --- a/docs/development/application-registry.md +++ b/docs/development/application-registry.md @@ -32,6 +32,9 @@ Core model features are listed in the [features matrix](./models.md#features-mat ### `models` +!!! warning "Deprecated" + Usage of this key has been deprecated and will be removed in NetBox v4.7. Use `ObjectType.objects.public()` to find registered models. + This key lists all models which have been registered in NetBox which are not designated for private use. (Setting `_netbox_private` to True on a model excludes it from this list.) As with individual features under `model_features`, models are organized by app label. ### `plugins` diff --git a/docs/development/getting-started.md b/docs/development/getting-started.md index 956226760..993229d95 100644 --- a/docs/development/getting-started.md +++ b/docs/development/getting-started.md @@ -97,7 +97,7 @@ NetBox uses [`pre-commit`](https://pre-commit.com/) to automatically validate co * Run the `ruff` Python linter * Run Django's internal system check * Check for missing database migrations -* Validate any changes to the documentation with `mkdocs` +* Validate any changes to the documentation with `zensical` * Validate Typescript & Sass styling with `yarn` * Ensure that any modified static front end assets have been recompiled diff --git a/docs/development/models.md b/docs/development/models.md index 7c11d5521..aabca514d 100644 --- a/docs/development/models.md +++ b/docs/development/models.md @@ -45,6 +45,7 @@ These are considered the "core" application models which are used to model netwo * [core.DataSource](../models/core/datasource.md) * [core.Job](../models/core/job.md) * [dcim.Cable](../models/dcim/cable.md) +* [dcim.CableBundle](../models/dcim/cablebundle.md) * [dcim.Device](../models/dcim/device.md) * [dcim.DeviceType](../models/dcim/devicetype.md) * [dcim.Module](../models/dcim/module.md) @@ -73,6 +74,7 @@ These are considered the "core" application models which are used to model netwo * [tenancy.Tenant](../models/tenancy/tenant.md) * [virtualization.Cluster](../models/virtualization/cluster.md) * [virtualization.VirtualMachine](../models/virtualization/virtualmachine.md) +* [virtualization.VirtualMachineType](../models/virtualization/virtualmachinetype.md) * [vpn.IKEPolicy](../models/vpn/ikepolicy.md) * [vpn.IKEProposal](../models/vpn/ikeproposal.md) * [vpn.IPSecPolicy](../models/vpn/ipsecpolicy.md) @@ -92,6 +94,7 @@ Organization models are used to organize and classify primary models. * [dcim.DeviceRole](../models/dcim/devicerole.md) * [dcim.Manufacturer](../models/dcim/manufacturer.md) * [dcim.Platform](../models/dcim/platform.md) +* [dcim.RackGroup](../models/dcim/rackgroup.md) * [dcim.RackRole](../models/dcim/rackrole.md) * [ipam.ASNRange](../models/ipam/asnrange.md) * [ipam.RIR](../models/ipam/rir.md) diff --git a/docs/development/release-checklist.md b/docs/development/release-checklist.md index eb4001891..0cf75a6b3 100644 --- a/docs/development/release-checklist.md +++ b/docs/development/release-checklist.md @@ -47,7 +47,7 @@ If a new Django release is adopted or other major dependencies (Python, PostgreS Start the documentation server and navigate to the current version of the installation docs: ```no-highlight -mkdocs serve +zensical serve ``` Follow these instructions to perform a new installation of NetBox in a temporary environment. This process must not be automated: The goal of this step is to catch any errors or omissions in the documentation and ensure that it is kept up to date for each release. Make any necessary changes to the documentation before proceeding with the release. diff --git a/docs/extra.css b/docs/extra.css index 4b8cd87fe..e953fa14c 100644 --- a/docs/extra.css +++ b/docs/extra.css @@ -5,10 +5,6 @@ img { margin-right: auto; } -.md-content img { - background-color: rgba(255, 255, 255, 0.64); -} - /* Tables */ table { margin-bottom: 24px; diff --git a/docs/features/virtualization.md b/docs/features/virtualization.md index 8e1bf35ef..4a00c6ced 100644 --- a/docs/features/virtualization.md +++ b/docs/features/virtualization.md @@ -1,26 +1,44 @@ # Virtualization -Virtual machines and clusters can be modeled in NetBox alongside physical infrastructure. IP addresses and other resources are assigned to these objects just like physical objects, providing a seamless integration between physical and virtual networks. +Virtual machines, clusters, and standalone hypervisors can be modeled in NetBox alongside physical infrastructure. IP addresses and other resources are assigned to these objects just like physical objects, providing a seamless integration between physical and virtual networks. ```mermaid flowchart TD ClusterGroup & ClusterType --> Cluster + VirtualMachineType --> VirtualMachine + Device --> VirtualMachine Cluster --> VirtualMachine Platform --> VirtualMachine VirtualMachine --> VMInterface -click Cluster "../../models/virtualization/cluster/" -click ClusterGroup "../../models/virtualization/clustergroup/" -click ClusterType "../../models/virtualization/clustertype/" -click Platform "../../models/dcim/platform/" -click VirtualMachine "../../models/virtualization/virtualmachine/" -click VMInterface "../../models/virtualization/vminterface/" + click Cluster "../../models/virtualization/cluster/" + click ClusterGroup "../../models/virtualization/clustergroup/" + click ClusterType "../../models/virtualization/clustertype/" + click VirtualMachineType "../../models/virtualization/virtualmachinetype/" + click Device "../../models/dcim/device/" + click Platform "../../models/dcim/platform/" + click VirtualMachine "../../models/virtualization/virtualmachine/" + click VMInterface "../../models/virtualization/vminterface/" ``` ## Clusters -A cluster is one or more physical host devices on which virtual machines can run. Each cluster must have a type and operational status, and may be assigned to a group. (Both types and groups are user-defined.) Each cluster may designate one or more devices as hosts, however this is optional. +A cluster is one or more physical host devices on which virtual machines can run. + +Each cluster must have a type and operational status, and may be assigned to a group. (Both types and groups are user-defined.) Each cluster may designate one or more devices as hosts, however this is optional. + +## Virtual Machine Types + +A virtual machine type provides reusable classification for virtual machines and can define create-time defaults for platform, vCPUs, and memory. This is useful when multiple virtual machines share a common sizing or profile while still allowing per-instance overrides after creation. ## Virtual Machines -A virtual machine is a virtualized compute instance. These behave in NetBox very similarly to device objects, but without any physical attributes. For example, a VM may have interfaces assigned to it with IP addresses and VLANs, however its interfaces cannot be connected via cables (because they are virtual). Each VM may also define its compute, memory, and storage resources as well. +A virtual machine is a virtualized compute instance. These behave in NetBox very similarly to device objects, but without any physical attributes. + +For example, a VM may have interfaces assigned to it with IP addresses and VLANs, however its interfaces cannot be connected via cables (because they are virtual). Each VM may define its compute, memory, and storage resources as well. A VM can optionally be assigned a [virtual machine type](../models/virtualization/virtualmachinetype.md) to classify it and provide default values for selected attributes at creation time. + +A VM can be placed in one of three ways: + +- Assigned to a site alone for logical grouping. +- Assigned to a cluster and optionally pinned to a specific host device within that cluster. +- Assigned directly to a standalone device that does not belong to any cluster. diff --git a/docs/integrations/rest-api.md b/docs/integrations/rest-api.md index bac984a08..74b421456 100644 --- a/docs/integrations/rest-api.md +++ b/docs/integrations/rest-api.md @@ -341,7 +341,7 @@ When retrieving devices and virtual machines via the REST API, each will include ## Pagination -API responses which contain a list of many objects will be paginated for efficiency. The root JSON object returned by a list endpoint contains the following attributes: +API responses which contain a list of many objects will be paginated for efficiency. NetBox employs offset-based pagination by default, which forms a page by skipping the number of objects indicated by the `offset` URL parameter. The root JSON object returned by a list endpoint contains the following attributes: * `count`: The total number of all objects matching the query * `next`: A hyperlink to the next page of results (if applicable) @@ -398,6 +398,49 @@ The maximum number of objects that can be returned is limited by the [`MAX_PAGE_ !!! warning Disabling the page size limit introduces a potential for very resource-intensive requests, since one API request can effectively retrieve an entire table from the database. +### Cursor-Based Pagination + +For large datasets, offset-based pagination can become inefficient because the database must scan all rows up to the offset. As an alternative, cursor-based pagination uses the `start` query parameter to filter results by primary key (PK), enabling efficient keyset pagination. + +To use cursor-based pagination, pass `start` (the minimum PK value) and `limit` (the page size): + +``` +http://netbox/api/dcim/devices/?start=0&limit=100 +``` + +This returns objects with an `id` greater than or equal to zero, ordered by PK, limited to 100 results. Below is an example showing an arbitrary `start` value. + +```json +{ + "count": null, + "next": "http://netbox/api/dcim/devices/?start=356&limit=100", + "previous": null, + "results": [ + { + "id": 109, + "name": "dist-router07", + ... + }, + ... + { + "id": 356, + "name": "acc-switch492", + ... + } + ] +} +``` + +To iterate through all results, use the `id` of the last object in each response plus one as the `start` value for the next request. Continue until `next` is null. + +!!! info + Some important differences from offset-based pagination: + + * `start` and `offset` are **mutually exclusive**; specifying both will result in a 400 error. + * Results are always ordered by primary key when using `start`. This is required to ensure deterministic behavior. + * `count` is always `null` in cursor mode, as counting all matching rows would partially negate its performance benefit. + * `previous` is always `null`: cursor-based pagination supports only forward navigation. + ## Interacting with Objects ### Retrieving Multiple Objects diff --git a/docs/integrations/webhooks.md b/docs/integrations/webhooks.md index 840626f77..fe5d15b9b 100644 --- a/docs/integrations/webhooks.md +++ b/docs/integrations/webhooks.md @@ -26,10 +26,20 @@ The following data is available as context for Jinja2 templates: * `event` - The type of event which triggered the webhook: `created`, `updated`, or `deleted`. * `timestamp` - The time at which the event occurred (in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format). * `object_type` - The NetBox model which triggered the change in the form `app_label.model_name`. -* `username` - The name of the user account associated with the change. -* `request_id` - The unique request ID. This may be used to correlate multiple changes associated with a single request. +* `request` - Data about the triggering request (if available). + * `request.id` - The UUID associated with the request + * `request.method` - The HTTP method (e.g. `GET` or `POST`) + * `request.path` - The URL path (ex: `/dcim/sites/123/edit/`) + * `request.user` - The name of the authenticated user who made the request (if available) * `data` - A detailed representation of the object in its current state. This is typically equivalent to the model's representation in NetBox's REST API. * `snapshots` - Minimal "snapshots" of the object state both before and after the change was made; provided as a dictionary with keys named `prechange` and `postchange`. These are not as extensive as the fully serialized representation, but contain enough information to convey what has changed. +* ⚠️ `request_id` - The unique request ID. This may be used to correlate multiple changes associated with a single request. +* ⚠️ `username` - The name of the user account associated with the change. + +!!! warning "Deprecation of legacy keys" + The `request_id` and `username` keys in the webhook payload above are deprecated and should no longer be used. Support for them will be removed in NetBox v4.7.0. + + Use `request.user` and `request.id` from the `request` object included in the callback context instead. ### Default Request Body @@ -56,6 +66,12 @@ If no body template is specified, the request body will be populated with a JSON "region": null, ... }, + "request": { + "id": "17af32f0-852a-46ca-a7d4-33ecd0c13de6", + "method": "POST", + "path": "/dcim/sites/add/", + "user": "jstretch" + }, "snapshots": { "prechange": null, "postchange": { diff --git a/docs/models/dcim/cablebundle.md b/docs/models/dcim/cablebundle.md new file mode 100644 index 000000000..cf585b378 --- /dev/null +++ b/docs/models/dcim/cablebundle.md @@ -0,0 +1,15 @@ +# Cable Bundles + +A cable bundle is a logical grouping of individual [cables](./cable.md). Bundles can be used to organize cables that share a common purpose, route, or physical grouping (such as a conduit or harness). + +Assigning cables to a bundle is optional and does not affect cable tracing or connectivity. Bundles persist independently of their member cables: deleting a cable clears its bundle assignment but does not delete the bundle itself. + +## Fields + +### Name + +A unique name for the cable bundle. + +### Description + +A brief description of the bundle's purpose or contents. diff --git a/docs/models/dcim/devicebay.md b/docs/models/dcim/devicebay.md index 5bbb125f8..9b19aee1e 100644 --- a/docs/models/dcim/devicebay.md +++ b/docs/models/dcim/devicebay.md @@ -23,3 +23,8 @@ The device bay's name. Must be unique to the parent device. ### Label An alternative physical label identifying the device bay. + +### Enabled + +Whether this device bay is enabled. Disabled device bays are not available for installation. + diff --git a/docs/models/dcim/devicetype.md b/docs/models/dcim/devicetype.md index fc177542c..4f75aff09 100644 --- a/docs/models/dcim/devicetype.md +++ b/docs/models/dcim/devicetype.md @@ -7,6 +7,18 @@ Device types are instantiated as devices installed within sites and/or equipment !!! note This parent/child relationship is **not** suitable for modeling chassis-based devices, wherein child members share a common control plane. Instead, line cards and similarly non-autonomous hardware should be modeled as modules or inventory items within a device. +## Automatic Component Renaming + +When adding component templates to a device type, the string `{vc_position}` can be used in component template names to reference the +`vc_position` field of the device being provisioned, when that device is a member of a Virtual Chassis. + +For example, an interface template named `Gi{vc_position}/0/0` installed on a Virtual Chassis +member with position `2` will be rendered as `Gi2/0/0`. + +If the device is not a member of a Virtual Chassis, `{vc_position}` defaults to `0`. A custom +fallback value can be specified using the syntax `{vc_position:X}`, where `X` is the desired default. +For example, `{vc_position:1}` will render as `1` when no Virtual Chassis position is set. + ## Fields ### Manufacturer diff --git a/docs/models/dcim/modulebay.md b/docs/models/dcim/modulebay.md index 494012a7b..d42828b83 100644 --- a/docs/models/dcim/modulebay.md +++ b/docs/models/dcim/modulebay.md @@ -1,6 +1,6 @@ # Module Bays -Module bays represent a space or slot within a device in which a field-replaceable [module](./module.md) may be installed. A common example is that of a chassis-based switch such as the Cisco Nexus 9000 or Juniper EX9200. Modules in turn hold additional components that become available to the parent device. +Module bays represent a space or slot within a device in which a field-replaceable [module](./module.md) may be installed. A common example is that of a chassis-based switch such as the Cisco Nexus 9000 or Juniper EX9200. Modules, in turn, hold additional components that become available to the parent device. !!! note If you need to model child devices rather than modules, use a [device bay](./devicebay.md) instead. @@ -29,3 +29,8 @@ An alternative physical label identifying the module bay. ### Position The numeric position in which this module bay is situated. For example, this would be the number assigned to a slot within a chassis-based switch. + +### Enabled + +Whether this module bay is enabled. Disabled module bays are not available for installation. + diff --git a/docs/models/dcim/moduletype.md b/docs/models/dcim/moduletype.md index 88f04466a..dca2b4af4 100644 --- a/docs/models/dcim/moduletype.md +++ b/docs/models/dcim/moduletype.md @@ -20,8 +20,38 @@ When adding component templates to a module type, the string `{module}` can be u For example, you can create a module type with interface templates named `Gi{module}/0/[1-48]`. When a new module of this type is "installed" to a module bay with a position of "3", NetBox will automatically name these interfaces `Gi3/0/[1-48]`. +Similarly, the string `{vc_position}` can be used in component template names to reference the +`vc_position` field of the device being provisioned, when that device is a member of a Virtual Chassis. + +For example, an interface template named `Gi{vc_position}/{module}/0` installed on a Virtual Chassis +member with position `2` and module bay position `3` will be rendered as `Gi2/3/0`. + +If the device is not a member of a Virtual Chassis, `{vc_position}` defaults to `0`. A custom +fallback value can be specified using the syntax `{vc_position:X}`, where `X` is the desired default. +For example, `{vc_position:1}` will render as `1` when no Virtual Chassis position is set. + Automatic renaming is supported for all modular component types (those listed above). +### Position Inheritance for Nested Modules + +When using nested module bays (modules installed inside other modules), the `{module}` placeholder +can also be used in the **position** field of module bay templates to inherit the parent bay's +position. This allows a single module type to produce correctly named components at any nesting +depth, with a user-controlled separator. + +For example, a line card module type might define sub-bay positions as `{module}/1`, `{module}/2`, +etc. When the line card is installed in a device bay with position `3`, these sub-bay positions +resolve to `3/1`, `3/2`, etc. An SFP module type with interface template `SFP {module}` installed +in sub-bay `3/2` then produces interface `SFP 3/2`. + +The separator between levels is defined by the user in the position field template itself. Using +`{module}-1` produces positions like `3-1`, while `{module}.1` produces `3.1`. This provides +full flexibility without requiring a global separator configuration. + +!!! note + If the position field does not contain `{module}`, no inheritance occurs and behavior is + unchanged from previous versions. + ## Fields ### Manufacturer diff --git a/docs/models/dcim/rack.md b/docs/models/dcim/rack.md index d610f6368..52df4aaf8 100644 --- a/docs/models/dcim/rack.md +++ b/docs/models/dcim/rack.md @@ -1,6 +1,6 @@ # Racks -The rack model represents a physical two- or four-post equipment rack in which [devices](./device.md) can be installed. Each rack must be assigned to a [site](./site.md), and may optionally be assigned to a [location](./location.md) within that site. Racks can also be organized by user-defined functional roles. The name and facility ID of each rack within a location must be unique. +The rack model represents a physical two- or four-post equipment rack in which [devices](./device.md) can be installed. Each rack must be assigned to a [site](./site.md), and may optionally be assigned to a [location](./location.md) within that site. Racks can also be organized by user-defined functional roles or by [rack groups](./rackgroup.md). The name and facility ID of each rack within a location must be unique. Rack height is measured in *rack units* (U); racks are commonly between 42U and 48U tall, but NetBox allows you to define racks of arbitrary height. A toggle is provided to indicate whether rack units are in ascending (from the ground up) or descending order. @@ -16,6 +16,10 @@ The [site](./site.md) to which the rack is assigned. The [location](./location.md) within a site where the rack has been installed (optional). +### Rack Group + +The [group](./rackgroup.md) used to organize racks by physical placement (optional). + ### Name The rack's name or identifier. Must be unique to the rack's location, if assigned. diff --git a/docs/models/dcim/rackgroup.md b/docs/models/dcim/rackgroup.md new file mode 100644 index 000000000..13b3f39ab --- /dev/null +++ b/docs/models/dcim/rackgroup.md @@ -0,0 +1,15 @@ +# Rack Groups + +Racks can optionally be assigned to rack groups to reflect their physical placement. Rack groups provide a secondary means of categorization alongside [locations](./location.md), which is particularly useful for datacenter operators who need to group racks by row, aisle, or similar physical arrangement while keeping them assigned to the same location, such as a cage or room. Rack groups are flat and do not form a hierarchy. + +Rack groups can also be used to scope [VLAN groups](../ipam/vlangroup.md), which can help model L2 domains spanning rows or pairs of racks. + +## Fields + +### Name + +A unique human-friendly name. + +### Slug + +A unique URL-friendly identifier. (This value can be used for filtering.) diff --git a/docs/models/dcim/rackreservation.md b/docs/models/dcim/rackreservation.md index 8eaa11af8..9e15302ee 100644 --- a/docs/models/dcim/rackreservation.md +++ b/docs/models/dcim/rackreservation.md @@ -12,6 +12,10 @@ The [rack](./rack.md) being reserved. The rack unit or units being reserved. Multiple units can be expressed using commas and/or hyphens. For example, `1,3,5-7` specifies units 1, 3, 5, 6, and 7. +### Total U's + +A calculated (read-only) field that reflects the total number of units in the reservation. Can be filtered upon using `unit_count_min` and `unit_count_max` parameters in the UI or API. + ### Status The current status of the reservation. (This is for documentation only: The status of a reservation has no impact on the installation of devices within a reserved rack unit.) diff --git a/docs/models/extras/customfield.md b/docs/models/extras/customfield.md index a5d083492..7aeae7dad 100644 --- a/docs/models/extras/customfield.md +++ b/docs/models/extras/customfield.md @@ -103,6 +103,8 @@ The default value to populate for the custom field when creating new objects (op For selection and multi-select custom fields only, this is the [set of choices](./customfieldchoiceset.md) which are valid for the field. +Choice sets may optionally define colors for individual values. Colored choices are rendered as badges on object detail pages for selection and multiple-selection custom fields. + ### Cloneable If enabled, values from this field will be automatically pre-populated when cloning existing objects. @@ -118,3 +120,7 @@ For numeric custom fields only. The maximum valid value (optional). ### Validation Regex For string-based custom fields only. A regular expression used to validate the field's value (optional). + +### Validation Schema + +For JSON custom fields, users have the option of defining a [validation schema](https://json-schema.org). Any value applied to this custom field on a model will be validated against the provided schema, if any. diff --git a/docs/models/extras/customfieldchoiceset.md b/docs/models/extras/customfieldchoiceset.md index 4f4e134ec..53914499e 100644 --- a/docs/models/extras/customfieldchoiceset.md +++ b/docs/models/extras/customfieldchoiceset.md @@ -22,6 +22,28 @@ The set of pre-defined choices to include. Available sets are listed below. This A set of custom choices that will be appended to the base choice set (if any). +### Choice Colors + +Optional color bindings for individual choice values. Each color is bound to a choice by its value rather than its label. + +When editing a choice set in the UI, enter one mapping per line using the format `value:color`. Supported colors are: + +* `blue` +* `indigo` +* `purple` +* `pink` +* `red` +* `orange` +* `yellow` +* `green` +* `teal` +* `cyan` +* `gray` +* `black` +* `white` + +Colored choices are rendered as badges on object detail pages for selection and multiple-selection custom fields. + ### Order Alphabetically If enabled, the choices list will be automatically ordered alphabetically. If disabled, choices will appear in the order in which they were defined. diff --git a/docs/models/extras/webhook.md b/docs/models/extras/webhook.md index 33cd8a2c9..7f0f322c6 100644 --- a/docs/models/extras/webhook.md +++ b/docs/models/extras/webhook.md @@ -81,10 +81,13 @@ The following context variables are available to the text and link templates. | Variable | Description | |---------------|------------------------------------------------------| -| `event` | The event type (`created`, `updated`, or `deleted`) | +| `event` | The event type (`create`, `update`, or `delete`) | | `timestamp` | The time at which the event occurred | | `object_type` | The type of object impacted (`app_label.model_name`) | | `username` | The name of the user associated with the change | | `request_id` | The unique request ID | | `data` | A complete serialized representation of the object | | `snapshots` | Pre- and post-change snapshots of the object | + +!!! warning "Deprecation of legacy fields" + The `request_id` and `username` fields in the webhook payload above are deprecated and should no longer be used. Support for them will be removed in NetBox v4.7.0. Use `request.user` and `request.id` from the `request` object included in the callback context instead. (Note that `request` is populated in the context only when the webhook is associated with a triggering request.) diff --git a/docs/models/ipam/asn.md b/docs/models/ipam/asn.md index 17fa5ebe3..33f8b13ed 100644 --- a/docs/models/ipam/asn.md +++ b/docs/models/ipam/asn.md @@ -14,6 +14,10 @@ The 16- or 32-bit AS number. The [Regional Internet Registry](./rir.md) or similar authority responsible for the allocation of this particular ASN. +### Role + +The user-defined functional [role](./role.md) assigned to this ASN. + ### Sites The [site(s)](../dcim/site.md) to which this ASN is assigned. diff --git a/docs/models/ipam/vlangroup.md b/docs/models/ipam/vlangroup.md index 67050ab4c..6a4e7bf15 100644 --- a/docs/models/ipam/vlangroup.md +++ b/docs/models/ipam/vlangroup.md @@ -18,6 +18,10 @@ A unique URL-friendly identifier. (This value can be used for filtering.) The set of VLAN IDs which are encompassed by the group. By default, this will be the entire range of valid IEEE 802.1Q VLAN IDs (1 to 4094, inclusive). VLANs created within a group must have a VID that falls within one of these ranges. Ranges may not overlap. +### Total VLAN IDs + +A read-only integer indicating the total count of VLAN IDs available within the group, calculated from the configured VLAN ID Ranges. For example, a group with ranges `100-199` and `300-399` would have a total of 200 VLAN IDs. This value is automatically computed and updated whenever the VLAN ID ranges are modified. + ### Scope The domain covered by a VLAN group, defined as one of the supported object types. This conveys the context in which a VLAN group applies. diff --git a/docs/models/virtualization/virtualmachine.md b/docs/models/virtualization/virtualmachine.md index 189a4ba75..f8786796c 100644 --- a/docs/models/virtualization/virtualmachine.md +++ b/docs/models/virtualization/virtualmachine.md @@ -1,18 +1,27 @@ # Virtual Machines -A virtual machine (VM) represents a virtual compute instance hosted within a [cluster](./cluster.md). Each VM must be assigned to a [site](../dcim/site.md) and/or cluster, and may optionally be assigned to a particular host [device](../dcim/device.md) within a cluster. +A virtual machine (VM) represents a virtual compute instance hosted within a cluster or directly on a device. Each VM must be assigned to at least one of: a [site](../dcim/site.md), a [cluster](./cluster.md), or a [device](../dcim/device.md). -Virtual machines may have virtual [interfaces](./vminterface.md) assigned to them, but do not support any physical component. When a VM has one or more interfaces with IP addresses assigned, a primary IP for the device can be designated, for both IPv4 and IPv6. +Virtual machines may have virtual [interfaces](./vminterface.md) assigned to them, but do not support any physical component. When a VM has one or more interfaces with IP addresses assigned, a primary IP for the VM can be designated, for both IPv4 and IPv6. ## Fields ### Name -The virtual machine's configured name. Must be unique to the assigned cluster and tenant. +The virtual machine's configured name. Must be unique within its scoping context: + +- If assigned to a **cluster**: unique within the cluster and tenant. +- If assigned to a **device** (no cluster): unique within the device and tenant. + +### Type + +The [virtual machine type](./virtualmachinetype.md) assigned to the VM. A type classifies a virtual machine and can provide default values for platform, vCPUs, and memory when the VM is created. + +Changes made to a virtual machine type do **not** apply retroactively to existing virtual machines. ### Role -The functional [role](../dcim/devicerole.md) assigned to the VM. +The functional role assigned to the VM. ### Status @@ -21,24 +30,28 @@ The VM's operational status. !!! tip Additional statuses may be defined by setting `VirtualMachine.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter. -### Start on boot +### Start on Boot The start on boot setting from the hypervisor. !!! tip Additional statuses may be defined by setting `VirtualMachine.start_on_boot` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter. -### Site & Cluster +### Site / Cluster / Device -The [site](../dcim/site.md) and/or [cluster](./cluster.md) to which the VM is assigned. +The location or host for this VM. At least one must be specified: -### Device +- **Site only**: The VM exists at a site but is not assigned to a specific cluster or device. +- **Cluster only**: The VM belongs to a virtualization cluster. The site is automatically inferred from the cluster's scope. +- **Device only**: The VM runs directly on a physical host device without a cluster (e.g. containers). The site is automatically inferred from the device's site. +- **Cluster + Device**: The VM belongs to a cluster and is pinned to a specific host device within that cluster. The device must be a registered host of the assigned cluster. -The physical host [device](../dcim/device.md) within the assigned site/cluster on which this VM resides. +!!! info "New in NetBox v4.6" + Virtual machines can now be assigned directly to a device without requiring a cluster. This is particularly useful for modeling VMs running on standalone hosts outside of a cluster. ### Platform -A VM may be associated with a particular [platform](../dcim/platform.md) to indicate its operating system. +A VM may be associated with a particular [platform](../dcim/platform.md) to indicate its operating system. If a virtual machine type defines a default platform, it will be applied when the VM is created unless an explicit platform is specified. ### Primary IPv4 & IPv6 Addresses @@ -49,11 +62,11 @@ Each VM may designate one primary IPv4 address and/or one primary IPv6 address f ### vCPUs -The number of virtual CPUs provisioned. A VM may be allocated a partial vCPU count (e.g. 1.5 vCPU). +The number of virtual CPUs provisioned. A VM may be allocated a partial vCPU count (e.g. 1.5 vCPU). If a virtual machine type defines a default vCPU allocation, it will be applied when the VM is created unless an explicit value is specified. ### Memory -The amount of running memory provisioned, in megabytes. +The amount of running memory provisioned, in megabytes. If a virtual machine type defines a default memory allocation, it will be applied when the VM is created unless an explicit value is specified. ### Disk @@ -64,4 +77,7 @@ The amount of disk storage provisioned, in megabytes. ### Serial Number -Optional serial number assigned to this virtual machine. Unlike devices, uniqueness is not enforced for virtual machine serial numbers. +Optional serial number assigned to this virtual machine. + +!!! info + Unlike devices, uniqueness is not enforced for virtual machine serial numbers. diff --git a/docs/models/virtualization/virtualmachinetype.md b/docs/models/virtualization/virtualmachinetype.md new file mode 100644 index 000000000..daf213c56 --- /dev/null +++ b/docs/models/virtualization/virtualmachinetype.md @@ -0,0 +1,27 @@ +# Virtual Machine Types + +A virtual machine type defines a reusable classification and default configuration for [virtual machines](./virtualmachine.md). + +A type can optionally provide default values for a VM's [platform](../dcim/platform.md), vCPU allocation, and memory allocation. When a virtual machine is created with an assigned type, any unset values among these fields will inherit their defaults from the type. Changes made to a virtual machine type do **not** apply retroactively to existing virtual machines. + +## Fields + +### Name + +A unique human-friendly name. + +### Slug + +A unique URL-friendly identifier. (This value can be used for filtering.) + +### Default Platform + +If defined, virtual machines instantiated with this type will automatically inherit the selected platform when no explicit platform is provided. + +### Default vCPUs + +The default number of vCPUs to assign when creating a virtual machine from this type. + +### Default Memory + +The default amount of memory, in megabytes, to assign when creating a virtual machine from this type. diff --git a/docs/plugins/development/permissions.md b/docs/plugins/development/permissions.md new file mode 100644 index 000000000..62a1ede1b --- /dev/null +++ b/docs/plugins/development/permissions.md @@ -0,0 +1,24 @@ +# Custom Model Actions + +Plugins can register custom permission actions for their models. These actions appear as checkboxes in the ObjectPermission form, making it easy for administrators to grant or restrict access to plugin-specific functionality without manually entering action names. + +For example, a plugin might define a "sync" action for a model that syncs data from an external source, or a "bypass" action that allows users to bypass certain restrictions. + +## Registering Model Actions + +The preferred way to register custom actions is via Django's `Meta.permissions` on the model class. NetBox will automatically register these as model actions when the app is loaded: + +```python +from netbox.models import NetBoxModel + +class MyModel(NetBoxModel): + # ... + + class Meta: + permissions = [ + ('sync', 'Synchronize data from external source'), + ('export', 'Export data to external system'), + ] +``` + +Once registered, these actions appear as checkboxes in a flat list when creating or editing an ObjectPermission. diff --git a/docs/plugins/development/ui-components.md b/docs/plugins/development/ui-components.md index a8fe2eff0..45766b201 100644 --- a/docs/plugins/development/ui-components.md +++ b/docs/plugins/development/ui-components.md @@ -1,12 +1,9 @@ # UI Components -!!! note "New in NetBox v4.5" - All UI components described here were introduced in NetBox v4.5. Be sure to set the minimum NetBox version to 4.5.0 for your plugin before incorporating any of these resources. +!!! note "New in NetBox v4.6" + All UI components described here were introduced in NetBox v4.6. Be sure to set the minimum NetBox version to 4.6.0 for your plugin before incorporating any of these resources. -!!! danger "Beta Feature" - UI components are considered a beta feature, and are still under active development. Please be aware that the API for resources on this page is subject to change in future releases. - -To simply the process of designing your plugin's user interface, and to encourage a consistent look and feel throughout the entire application, NetBox provides a set of components that enable programmatic UI design. These make it possible to declare complex page layouts with little or no custom HTML. +To simplify the process of designing your plugin's user interface, and to encourage a consistent look and feel throughout the entire application, NetBox provides a set of components that enable programmatic UI design. These make it possible to declare complex page layouts with little or no custom HTML. ## Page Layout @@ -75,9 +72,12 @@ class RecentChangesPanel(Panel): **super().get_context(context), 'changes': get_changes()[:10], } + + def should_render(self, context): + return len(context['changes']) > 0 ``` -NetBox also includes a set of panels suite for specific uses, such as display object details or embedding a table of related objects. These are listed below. +NetBox also includes a set of panels suited for specific uses, such as displaying object details or embedding a table of related objects. These are listed below. ::: netbox.ui.panels.Panel @@ -85,26 +85,6 @@ NetBox also includes a set of panels suite for specific uses, such as display ob ::: netbox.ui.panels.ObjectAttributesPanel -#### Object Attributes - -The following classes are available to represent object attributes within an ObjectAttributesPanel. Additionally, plugins can subclass `netbox.ui.attrs.ObjectAttribute` to create custom classes. - -| Class | Description | -|--------------------------------------|--------------------------------------------------| -| `netbox.ui.attrs.AddressAttr` | A physical or mailing address. | -| `netbox.ui.attrs.BooleanAttr` | A boolean value | -| `netbox.ui.attrs.ColorAttr` | A color expressed in RGB | -| `netbox.ui.attrs.ChoiceAttr` | A selection from a set of choices | -| `netbox.ui.attrs.GPSCoordinatesAttr` | GPS coordinates (latitude and longitude) | -| `netbox.ui.attrs.ImageAttr` | An attached image (displays the image) | -| `netbox.ui.attrs.NestedObjectAttr` | A related nested object | -| `netbox.ui.attrs.NumericAttr` | An integer or float value | -| `netbox.ui.attrs.RelatedObjectAttr` | A related object | -| `netbox.ui.attrs.TemplatedAttr` | Renders an attribute using a custom template | -| `netbox.ui.attrs.TextAttr` | A string (text) value | -| `netbox.ui.attrs.TimezoneAttr` | A timezone with annotated offset | -| `netbox.ui.attrs.UtilizationAttr` | A numeric value expressed as a utilization graph | - ::: netbox.ui.panels.OrganizationalObjectPanel ::: netbox.ui.panels.NestedGroupObjectPanel @@ -119,9 +99,13 @@ The following classes are available to represent object attributes within an Obj ::: netbox.ui.panels.TemplatePanel +::: netbox.ui.panels.TextCodePanel + +::: netbox.ui.panels.ContextTablePanel + ::: netbox.ui.panels.PluginContentPanel -## Panel Actions +### Panel Actions Each panel may have actions associated with it. These render as links or buttons within the panel header, opposite the panel's title. For example, a common use case is to include an "Add" action on a panel which displays a list of objects. Below is an example of this. @@ -146,3 +130,60 @@ panels.ObjectsTablePanel( ::: netbox.ui.actions.AddObject ::: netbox.ui.actions.CopyContent + +## Object Attributes + +The following classes are available to represent object attributes within an ObjectAttributesPanel. Additionally, plugins can subclass `netbox.ui.attrs.ObjectAttribute` to create custom classes. + +| Class | Description | +|------------------------------------------|--------------------------------------------------| +| `netbox.ui.attrs.AddressAttr` | A physical or mailing address. | +| `netbox.ui.attrs.BooleanAttr` | A boolean value | +| `netbox.ui.attrs.ChoiceAttr` | A selection from a set of choices | +| `netbox.ui.attrs.ColorAttr` | A color expressed in RGB | +| `netbox.ui.attrs.DateTimeAttr` | A date or datetime value | +| `netbox.ui.attrs.GenericForeignKeyAttr` | A related object via a generic foreign key | +| `netbox.ui.attrs.GPSCoordinatesAttr` | GPS coordinates (latitude and longitude) | +| `netbox.ui.attrs.ImageAttr` | An attached image (displays the image) | +| `netbox.ui.attrs.NestedObjectAttr` | A related nested object (includes ancestors) | +| `netbox.ui.attrs.NumericAttr` | An integer or float value | +| `netbox.ui.attrs.RelatedObjectAttr` | A related object | +| `netbox.ui.attrs.RelatedObjectListAttr` | A list of related objects | +| `netbox.ui.attrs.TemplatedAttr` | Renders an attribute using a custom template | +| `netbox.ui.attrs.TextAttr` | A string (text) value | +| `netbox.ui.attrs.TimezoneAttr` | A timezone with annotated offset | +| `netbox.ui.attrs.UtilizationAttr` | A numeric value expressed as a utilization graph | + +::: netbox.ui.attrs.ObjectAttribute + +::: netbox.ui.attrs.AddressAttr + +::: netbox.ui.attrs.BooleanAttr + +::: netbox.ui.attrs.ChoiceAttr + +::: netbox.ui.attrs.ColorAttr + +::: netbox.ui.attrs.DateTimeAttr + +::: netbox.ui.attrs.GenericForeignKeyAttr + +::: netbox.ui.attrs.GPSCoordinatesAttr + +::: netbox.ui.attrs.ImageAttr + +::: netbox.ui.attrs.NestedObjectAttr + +::: netbox.ui.attrs.NumericAttr + +::: netbox.ui.attrs.RelatedObjectAttr + +::: netbox.ui.attrs.RelatedObjectListAttr + +::: netbox.ui.attrs.TemplatedAttr + +::: netbox.ui.attrs.TextAttr + +::: netbox.ui.attrs.TimezoneAttr + +::: netbox.ui.attrs.UtilizationAttr diff --git a/docs/plugins/development/webhooks.md b/docs/plugins/development/webhooks.md index 755d19d42..4771e4c3b 100644 --- a/docs/plugins/development/webhooks.md +++ b/docs/plugins/development/webhooks.md @@ -36,6 +36,7 @@ The resulting webhook payload will look like the following: "url": "/api/dcim/sites/2/", ... }, + "request": {...}, "snapshots": {...}, "context": { "foo": 123 @@ -43,6 +44,11 @@ The resulting webhook payload will look like the following: } ``` +!!! warning "Deprecation of legacy keys" + The `request_id` and `username` keys in the webhook payload above are deprecated and should no longer be used. Support for them will be removed in NetBox v4.7.0. + + Use `request.user` and `request.id` from the `request` object included in the callback context instead. + !!! note "Consider namespacing webhook data" The data returned from all webhook callbacks will be compiled into a single `context` dictionary. Any existing keys within this dictionary will be overwritten by subsequent callbacks which include those keys. To avoid collisions with webhook data provided by other plugins, consider namespacing your plugin's data within a nested dictionary as such: diff --git a/docs/release-notes/index.md b/docs/release-notes/index.md index 27cea90f3..ac339b2b4 100644 --- a/docs/release-notes/index.md +++ b/docs/release-notes/index.md @@ -10,6 +10,14 @@ Minor releases are published in April, August, and December of each calendar yea This page contains a history of all major and minor releases since NetBox v2.0. For more detail on a specific patch release, please see the release notes page for that specific minor release. +#### [Version 4.6](./version-4.6.md) (May 2026) + +* Virtual Machine Types ([#5795](https://github.com/netbox-community/netbox/issues/5795)) +* Cable Bundles ([#20151](https://github.com/netbox-community/netbox/issues/20151)) +* Rack Groups ([#20961](https://github.com/netbox-community/netbox/issues/20961)) +* ETag Support for REST API ([#21356](https://github.com/netbox-community/netbox/issues/21356)) +* Cursor-based Pagination for REST API ([#21363](https://github.com/netbox-community/netbox/issues/21363)) + #### [Version 4.5](./version-4.5.md) (January 2026) * Lookup Modifiers in Filter Forms ([#7604](https://github.com/netbox-community/netbox/issues/7604)) diff --git a/docs/release-notes/version-4.6.md b/docs/release-notes/version-4.6.md new file mode 100644 index 000000000..b39bcb497 --- /dev/null +++ b/docs/release-notes/version-4.6.md @@ -0,0 +1,129 @@ +# NetBox v4.6 + +## v4.6.0-beta2 (2026-04-28) + +### New Features + +#### Virtual Machine Types ([#5795](https://github.com/netbox-community/netbox/issues/5795)) + +A new VirtualMachineType model has been introduced to enable categorization of virtual machines by instance type, analogous to how DeviceType categorizes physical hardware. VM types can be defined once and reused across many virtual machines. + +#### Cable Bundles ([#20151](https://github.com/netbox-community/netbox/issues/20151)) + +A new CableBundle model allows individual cables to be grouped together to represent physical cable runs that are managed as a unit; e.g. a bundle of 48 CAT6 cables between two patch panels. (Please note that this feature is _not_ suitable for modeling individual fiber strands within a single cable.) + +#### Rack Groups ([#20961](https://github.com/netbox-community/netbox/issues/20961)) + +A flat RackGroup model has been reintroduced to provide a lightweight secondary axis of rack organization (e.g. by row or aisle) that is independent of the location hierarchy. Racks carry an optional foreign key to a RackGroup, and RackGroup can also serve as a scope for VLANGroup assignments. + +#### ETag Support for REST API ([#21356](https://github.com/netbox-community/netbox/issues/21356)) + +The REST API now returns an `ETag` header on responses for individual objects, derived from the object's last-updated timestamp. Clients can supply an `If-Match` header on PUT/PATCH requests to guard against conflicting concurrent updates; if the object has been modified since the ETag was issued, the server returns a 412 (Precondition Failed) response. + +#### Cursor-based Pagination for REST API ([#21363](https://github.com/netbox-community/netbox/issues/21363)) + +A new `start` query parameter has been introduced as an efficient alternative to the existing `offset` parameter for paginating large result sets. Rather than scanning the table up to a relative offset, the `start` parameter filters for objects with a primary key equal to or greater than the given value, enabling constant-time pagination regardless of result set size. + +### Enhancements + +* [#12024](https://github.com/netbox-community/netbox/issues/12024) - Permit virtual machines to be assigned to devices without a cluster +* [#14329](https://github.com/netbox-community/netbox/issues/14329) - Improve diff highlighting for custom field data in change logs +* [#15513](https://github.com/netbox-community/netbox/issues/15513) - Add bulk creation support for IP prefixes +* [#17654](https://github.com/netbox-community/netbox/issues/17654) - Support role assignment for ASNs +* [#19025](https://github.com/netbox-community/netbox/issues/19025) - Support optional schema validation for JSON custom fields +* [#19034](https://github.com/netbox-community/netbox/issues/19034) - Annotate total reserved unit count on rack reservations +* [#19138](https://github.com/netbox-community/netbox/issues/19138) - Include NAT addresses for primary & out-of-band IP addresses in REST API +* [#19648](https://github.com/netbox-community/netbox/issues/19648) - Add a color custom field type +* [#19796](https://github.com/netbox-community/netbox/issues/19796) - Support `{module}` position inheritance for nested module bays +* [#19953](https://github.com/netbox-community/netbox/issues/19953) - Enable debugging support for ConfigTemplate rendering +* [#20123](https://github.com/netbox-community/netbox/issues/20123) - Introduce options to control adoption/replication of device components via REST API (replicates UI behavior) +* [#20152](https://github.com/netbox-community/netbox/issues/20152) - Support for marking module and device bays as disabled +* [#20162](https://github.com/netbox-community/netbox/issues/20162) - Provide an option to execute as a background job when adding components to devices in bulk +* [#20163](https://github.com/netbox-community/netbox/issues/20163) - Add changelog message support for bulk device component creation +* [#20698](https://github.com/netbox-community/netbox/issues/20698) - Add read-only `total_vlan_ids` attribute on VLAN group representation in REST & GraphQL APIs +* [#20916](https://github.com/netbox-community/netbox/issues/20916) - Include stack trace for unhandled exceptions in job logs +* [#21157](https://github.com/netbox-community/netbox/issues/21157) - Include all public model classes in export template context +* [#21409](https://github.com/netbox-community/netbox/issues/21409) - Introduce `CHANGELOG_RETAIN_CREATE_LAST_UPDATE` configuration parameter to retain creation & most recent update record in change log for each object +* [#21575](https://github.com/netbox-community/netbox/issues/21575) - Introduce `{vc_position}` template variable for device component template name/label +* [#21662](https://github.com/netbox-community/netbox/issues/21662) - Increase `rf_channel_frequency` precision to 3 decimal places +* [#21702](https://github.com/netbox-community/netbox/issues/21702) - Include a serialized representation of the HTTP request in each webhook +* [#21720](https://github.com/netbox-community/netbox/issues/21720) - Align HTTP basic auth regex of `EnhancedURLValidator` with Django's `URLValidator` +* [#21751](https://github.com/netbox-community/netbox/issues/21751) - Disable notifications for scripts running in the background +* [#21770](https://github.com/netbox-community/netbox/issues/21770) - Enable specifying columns to include/exclude on embedded tables +* [#21771](https://github.com/netbox-community/netbox/issues/21771) - Add support for partial tag assignment (`add_tags`) and removal (`remove_tags`) via REST API +* [#21780](https://github.com/netbox-community/netbox/issues/21780) - Add changelog message support to bulk creation of IP addresses +* [#21865](https://github.com/netbox-community/netbox/issues/21865) - Allow setting empty `INTERNAL_IPS` to enable debug toolbar for all clients +* [#21924](https://github.com/netbox-community/netbox/issues/21924) - Improve styling and consistency of floating bulk action controls + +### Performance Improvements + +* [#21455](https://github.com/netbox-community/netbox/issues/21455) - Ensure PostgreSQL indexes exist to support the default ordering of each model +* [#21688](https://github.com/netbox-community/netbox/issues/21688) - Reduce per-position ORM lookups when tracing cable paths +* [#21788](https://github.com/netbox-community/netbox/issues/21788) - Optimize bulk object export to avoid timeout errors on large querysets + +### Plugins + +* [#20924](https://github.com/netbox-community/netbox/issues/20924) - Introduce support for declarative layouts and reusable UI components +* [#21357](https://github.com/netbox-community/netbox/issues/21357) - Provide an API for plugins to register custom model actions (for permission assignment) + +### Deprecations + +* [#21284](https://github.com/netbox-community/netbox/issues/21284) - Deprecate the `username` and `request_id` fields in event data +* [#21304](https://github.com/netbox-community/netbox/issues/21304) - Deprecate the `housekeeping` management command +* [#21331](https://github.com/netbox-community/netbox/issues/21331) - Deprecate NetBox's custom `querystring` template tag +* [#21881](https://github.com/netbox-community/netbox/issues/21881) - Deprecate legacy Sentry configuration parameters +* [#21884](https://github.com/netbox-community/netbox/issues/21884) - Deprecate the obsolete `DEFAULT_ACTION_PERMISSIONS` mapping +* [#21887](https://github.com/netbox-community/netbox/issues/21887) - Deprecate support for legacy view actions +* [#21890](https://github.com/netbox-community/netbox/issues/21890) - Deprecate `models` key in application registry +* [#21936](https://github.com/netbox-community/netbox/issues/21936) - Deprecate the `LOGIN_REQUIRED` configuration parameter + +### Other Changes + +* [#20984](https://github.com/netbox-community/netbox/issues/20984) - Upgrade to Django 6.0 +* [#21635](https://github.com/netbox-community/netbox/issues/21635) - Migrate documentation site from mkdocs to Zensical + +### REST API Changes + +* New features: + * `ETag` response header and `If-Match` request header support for all individual object endpoints + * `start` query parameter for cursor-based pagination on all list endpoints + * `add_tags` and `remove_tags` write-only fields on all taggable model serializers +* New endpoints: + * `GET/POST /api/dcim/cable-bundles/` + * `GET/PUT/PATCH/DELETE /api/dcim/cable-bundles//` + * `GET/POST /api/dcim/rack-groups/` + * `GET/PUT/PATCH/DELETE /api/dcim/rack-groups//` + * `GET/POST /api/virtualization/virtual-machine-types/` + * `GET/PUT/PATCH/DELETE /api/virtualization/virtual-machine-types//` +* `dcim.Cable` + * Add optional foreign key field `bundle` +* `dcim.Device` + * The `primary_ip`, `primary_ip4`, `primary_ip6`, and `oob_ip` nested representations now include `nat_inside` and `nat_outside` +* `dcim.DeviceBay` + * Add boolean field `enabled` + * Add read-only boolean field `_occupied` +* `dcim.DeviceBayTemplate` + * Add boolean field `enabled` +* `dcim.Module` + * Add write-only boolean fields `replicate_components` and `adopt_components` +* `dcim.ModuleBay` + * Add boolean field `enabled` + * Add read-only boolean field `_occupied` +* `dcim.ModuleBayTemplate` + * Add boolean field `enabled` +* `dcim.Rack` + * Add optional foreign key field `group` +* `dcim.RackReservation` + * Add read-only integer field `unit_count` +* `extras.CustomField` + * Add JSON field `validation_schema` +* `ipam.ASN` + * Add optional foreign key field `role` +* `ipam.Role` + * Annotate count of assigned ASNs (`asn_count`) +* `ipam.VLANGroup` + * Add read-only field `total_vlan_ids` +* `virtualization.VirtualMachine` + * Add optional foreign key field `virtual_machine_type` + * The `primary_ip`, `primary_ip4`, and `primary_ip6` nested representations now include `nat_inside` and `nat_outside` + * The `cluster` field is now optional (nullable) diff --git a/mkdocs.yml b/mkdocs.yml index dc758f197..60bc2c643 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,3 +1,4 @@ +# Note: NetBox has migrated from MkDocs to Zensical site_name: NetBox Documentation site_dir: netbox/project-static/docs site_url: https://docs.netbox.dev/ @@ -151,6 +152,7 @@ nav: - Filters & Filter Sets: 'plugins/development/filtersets.md' - Search: 'plugins/development/search.md' - Event Types: 'plugins/development/event-types.md' + - Permissions: 'plugins/development/permissions.md' - Data Backends: 'plugins/development/data-backends.md' - Webhooks: 'plugins/development/webhooks.md' - User Interface: 'plugins/development/user-interface.md' @@ -189,6 +191,7 @@ nav: - Job: 'models/core/job.md' - DCIM: - Cable: 'models/dcim/cable.md' + - CableBundle: 'models/dcim/cablebundle.md' - ConsolePort: 'models/dcim/consoleport.md' - ConsolePortTemplate: 'models/dcim/consoleporttemplate.md' - ConsoleServerPort: 'models/dcim/consoleserverport.md' @@ -221,6 +224,7 @@ nav: - PowerPort: 'models/dcim/powerport.md' - PowerPortTemplate: 'models/dcim/powerporttemplate.md' - Rack: 'models/dcim/rack.md' + - RackGroup: 'models/dcim/rackgroup.md' - RackReservation: 'models/dcim/rackreservation.md' - RackRole: 'models/dcim/rackrole.md' - RackType: 'models/dcim/racktype.md' @@ -285,6 +289,7 @@ nav: - VMInterface: 'models/virtualization/vminterface.md' - VirtualDisk: 'models/virtualization/virtualdisk.md' - VirtualMachine: 'models/virtualization/virtualmachine.md' + - VirtualMachineType: 'models/virtualization/virtualmachinetype.md' - VPN: - IKEPolicy: 'models/vpn/ikepolicy.md' - IKEProposal: 'models/vpn/ikeproposal.md' @@ -322,6 +327,7 @@ nav: - git Cheat Sheet: 'development/git-cheat-sheet.md' - Release Notes: - Summary: 'release-notes/index.md' + - Version 4.6: 'release-notes/version-4.6.md' - Version 4.5: 'release-notes/version-4.5.md' - Version 4.4: 'release-notes/version-4.4.md' - Version 4.3: 'release-notes/version-4.3.md' diff --git a/netbox/account/views.py b/netbox/account/views.py index 98c81642f..1c3d8cade 100644 --- a/netbox/account/views.py +++ b/netbox/account/views.py @@ -356,9 +356,16 @@ class UserTokenView(LoginRequiredMixin, View): def get(self, request, pk): token = get_object_or_404(UserToken.objects.filter(user=request.user), pk=pk) + # Pop a one-time plaintext value (set by UserTokenEditView.post_save when a token is first created) and + # assemble the full HTTP authorization string for display. The plaintext is never persisted; popping + # ensures the banner only renders once. + plaintext = request.session.pop(f'_token_plaintext_{token.pk}', None) + token_auth_string = f'{token.get_auth_header_prefix()}{plaintext}' if plaintext else None + return render(request, 'account/token.html', { 'object': token, 'layout': self.layout, + 'token_auth_string': token_auth_string, }) @@ -366,11 +373,14 @@ class UserTokenView(LoginRequiredMixin, View): class UserTokenEditView(generic.ObjectEditView): queryset = UserToken.objects.all() form = forms.UserTokenForm + template_name = 'account/usertoken_edit.html' default_return_url = 'account:usertoken_list' def alter_object(self, obj, request, url_args, url_kwargs): if not obj.pk: obj.user = request.user + # Attach the request so that UserTokenForm.save() can stash the newly-generated plaintext on the session. + obj._request = request return obj diff --git a/netbox/circuits/migrations/0057_default_ordering_indexes.py b/netbox/circuits/migrations/0057_default_ordering_indexes.py new file mode 100644 index 000000000..ce053c47b --- /dev/null +++ b/netbox/circuits/migrations/0057_default_ordering_indexes.py @@ -0,0 +1,35 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('circuits', '0056_gfk_indexes'), + ('contenttypes', '0002_remove_content_type_name'), + ('dcim', '0231_interface_rf_channel_frequency_precision'), + ('extras', '0136_customfield_validation_schema'), + ('tenancy', '0023_add_mptt_tree_indexes'), + ('users', '0015_owner'), + ] + + operations = [ + migrations.AddIndex( + model_name='circuit', + index=models.Index(fields=['provider', 'provider_account', 'cid'], name='circuits_ci_provide_a0c42c_idx'), + ), + migrations.AddIndex( + model_name='circuitgroupassignment', + index=models.Index( + fields=['group', 'member_type', 'member_id', 'priority', 'id'], name='circuits_ci_group_i_2f8327_idx' + ), + ), + migrations.AddIndex( + model_name='virtualcircuit', + index=models.Index( + fields=['provider_network', 'provider_account', 'cid'], name='circuits_vi_provide_989efa_idx' + ), + ), + migrations.AddIndex( + model_name='virtualcircuittermination', + index=models.Index(fields=['virtual_circuit', 'role', 'id'], name='circuits_vi_virtual_4b5c0c_idx'), + ), + ] diff --git a/netbox/circuits/models/circuits.py b/netbox/circuits/models/circuits.py index c4c6e0853..0e2d808c4 100644 --- a/netbox/circuits/models/circuits.py +++ b/netbox/circuits/models/circuits.py @@ -144,6 +144,9 @@ class Circuit(ContactsMixin, ImageAttachmentsMixin, DistanceMixin, PrimaryModel) name='%(app_label)s_%(class)s_unique_provideraccount_cid' ), ) + indexes = ( + models.Index(fields=('provider', 'provider_account', 'cid')), # Default ordering + ) verbose_name = _('circuit') verbose_name_plural = _('circuits') @@ -221,6 +224,9 @@ class CircuitGroupAssignment(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, name='%(app_label)s_%(class)s_unique_member_group' ), ) + indexes = ( + models.Index(fields=('group', 'member_type', 'member_id', 'priority', 'id')), # Default ordering + ) verbose_name = _('Circuit group assignment') verbose_name_plural = _('Circuit group assignments') diff --git a/netbox/circuits/models/virtual_circuits.py b/netbox/circuits/models/virtual_circuits.py index 30b2c6296..c1ace058f 100644 --- a/netbox/circuits/models/virtual_circuits.py +++ b/netbox/circuits/models/virtual_circuits.py @@ -97,6 +97,9 @@ class VirtualCircuit(ContactsMixin, PrimaryModel): name='%(app_label)s_%(class)s_unique_provideraccount_cid' ), ) + indexes = ( + models.Index(fields=('provider_network', 'provider_account', 'cid')), # Default ordering + ) verbose_name = _('virtual circuit') verbose_name_plural = _('virtual circuits') @@ -150,6 +153,9 @@ class VirtualCircuitTermination( class Meta: ordering = ['virtual_circuit', 'role', 'pk'] + indexes = ( + models.Index(fields=('virtual_circuit', 'role', 'id')), # Default ordering + ) verbose_name = _('virtual circuit termination') verbose_name_plural = _('virtual circuit terminations') diff --git a/netbox/circuits/ui/panels.py b/netbox/circuits/ui/panels.py index d90ccf3d4..3572479cd 100644 --- a/netbox/circuits/ui/panels.py +++ b/netbox/circuits/ui/panels.py @@ -13,13 +13,9 @@ class CircuitCircuitTerminationPanel(panels.ObjectPanel): template_name = 'circuits/panels/circuit_circuit_termination.html' title = _('Termination') - def __init__(self, accessor=None, side=None, **kwargs): - super().__init__(**kwargs) - - if accessor is not None: - self.accessor = accessor - if side is not None: - self.side = side + def __init__(self, side, accessor=None, **kwargs): + super().__init__(accessor=accessor, **kwargs) + self.side = side def get_context(self, context): return { @@ -58,6 +54,26 @@ class CircuitGroupAssignmentsPanel(panels.ObjectsTablePanel): ) +class CircuitTerminationPanel(panels.ObjectAttributesPanel): + title = _('Circuit Termination') + circuit = attrs.RelatedObjectAttr('circuit', linkify=True) + provider = attrs.RelatedObjectAttr('circuit.provider', linkify=True) + termination = attrs.GenericForeignKeyAttr('termination', linkify=True, label=_('Termination point')) + connection = attrs.TemplatedAttr( + 'pk', + template_name='circuits/circuit_termination/attrs/connection.html', + label=_('Connection'), + ) + speed = attrs.TemplatedAttr( + 'port_speed', + template_name='circuits/circuit_termination/attrs/speed.html', + label=_('Speed'), + ) + xconnect_id = attrs.TextAttr('xconnect_id', label=_('Cross-Connect'), style='font-monospace') + pp_info = attrs.TextAttr('pp_info', label=_('Patch Panel/Port')) + description = attrs.TextAttr('description') + + class CircuitGroupPanel(panels.OrganizationalObjectPanel): tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index ddb4ef358..85912e00b 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -8,7 +8,6 @@ from netbox.ui import actions, layout from netbox.ui.panels import ( CommentsPanel, ObjectsTablePanel, - Panel, RelatedObjectsPanel, ) from netbox.views import generic @@ -53,6 +52,7 @@ class ProviderView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='circuits.ProviderAccount', filters={'provider_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['provider'], actions=[ actions.AddObject( 'circuits.ProviderAccount', url_params={'provider': lambda ctx: ctx['object'].pk} @@ -62,6 +62,7 @@ class ProviderView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='circuits.Circuit', filters={'provider_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['provider'], actions=[ actions.AddObject('circuits.Circuit', url_params={'provider': lambda ctx: ctx['object'].pk}), ], @@ -161,6 +162,7 @@ class ProviderAccountView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='circuits.Circuit', filters={'provider_account_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['provider_account'], actions=[ actions.AddObject( 'circuits.Circuit', @@ -257,6 +259,7 @@ class ProviderNetworkView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='circuits.VirtualCircuit', filters={'provider_network_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['provider_network'], actions=[ actions.AddObject( 'circuits.VirtualCircuit', url_params={'provider_network': lambda ctx: ctx['object'].pk} @@ -508,10 +511,7 @@ class CircuitTerminationView(generic.ObjectView): queryset = CircuitTermination.objects.all() layout = layout.SimpleLayout( left_panels=[ - Panel( - template_name='circuits/panels/circuit_termination.html', - title=_('Circuit Termination'), - ) + panels.CircuitTerminationPanel(), ], right_panels=[ CustomFieldsPanel(), @@ -801,6 +801,7 @@ class VirtualCircuitView(generic.ObjectView): model='circuits.VirtualCircuitTermination', title=_('Terminations'), filters={'virtual_circuit_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['virtual_circuit'], actions=[ actions.AddObject( 'circuits.VirtualCircuitTermination', diff --git a/netbox/core/api/serializers_/jobs.py b/netbox/core/api/serializers_/jobs.py index 5693a8099..1d5959608 100644 --- a/netbox/core/api/serializers_/jobs.py +++ b/netbox/core/api/serializers_/jobs.py @@ -26,13 +26,14 @@ class JobSerializer(BaseModelSerializer): object = serializers.SerializerMethodField( read_only=True ) + notifications = ChoiceField(choices=JobNotificationChoices, read_only=True) class Meta: model = Job fields = [ 'id', 'url', 'display_url', 'display', 'object_type', 'object_id', 'object', 'name', 'status', 'created', 'scheduled', 'interval', 'started', 'completed', 'user', 'data', 'error', 'job_id', 'queue_name', - 'log_entries', + 'notifications', 'log_entries', ] brief_fields = ('url', 'created', 'completed', 'user', 'status') diff --git a/netbox/core/choices.py b/netbox/core/choices.py index a35001a78..522eddc31 100644 --- a/netbox/core/choices.py +++ b/netbox/core/choices.py @@ -72,6 +72,18 @@ class JobStatusChoices(ChoiceSet): ) +class JobNotificationChoices(ChoiceSet): + NOTIFICATION_ALWAYS = 'always' + NOTIFICATION_ON_FAILURE = 'on_failure' + NOTIFICATION_NEVER = 'never' + + CHOICES = ( + (NOTIFICATION_ALWAYS, _('Always')), + (NOTIFICATION_ON_FAILURE, _('On failure')), + (NOTIFICATION_NEVER, _('Never')), + ) + + class JobIntervalChoices(ChoiceSet): INTERVAL_MINUTELY = 1 INTERVAL_HOURLY = 60 diff --git a/netbox/core/forms/model_forms.py b/netbox/core/forms/model_forms.py index bcfabe265..074ea8498 100644 --- a/netbox/core/forms/model_forms.py +++ b/netbox/core/forms/model_forms.py @@ -165,9 +165,10 @@ class ConfigRevisionForm(forms.ModelForm, metaclass=ConfigFormMetaclass): FieldSet('PAGINATE_COUNT', 'MAX_PAGE_SIZE', name=_('Pagination')), FieldSet('CUSTOM_VALIDATORS', 'PROTECTION_RULES', name=_('Validation')), FieldSet('DEFAULT_USER_PREFERENCES', name=_('User Preferences')), + FieldSet('CHANGELOG_RETENTION', 'CHANGELOG_RETAIN_CREATE_LAST_UPDATE', name=_('Change Log')), FieldSet( - 'MAINTENANCE_MODE', 'COPILOT_ENABLED', 'GRAPHQL_ENABLED', 'CHANGELOG_RETENTION', 'JOB_RETENTION', - 'MAPS_URL', name=_('Miscellaneous'), + 'MAINTENANCE_MODE', 'COPILOT_ENABLED', 'GRAPHQL_ENABLED', 'JOB_RETENTION', 'MAPS_URL', + name=_('Miscellaneous'), ), FieldSet('comment', name=_('Config Revision')) ) diff --git a/netbox/core/jobs.py b/netbox/core/jobs.py index 5859a9989..46dc10d1c 100644 --- a/netbox/core/jobs.py +++ b/netbox/core/jobs.py @@ -5,6 +5,7 @@ from importlib import import_module import requests from django.conf import settings from django.core.cache import cache +from django.db.models import Exists, OuterRef, Subquery from django.utils import timezone from packaging import version @@ -14,7 +15,7 @@ from netbox.jobs import JobRunner, system_job from netbox.search.backends import search_backend from utilities.proxy import resolve_proxies -from .choices import DataSourceStatusChoices, JobIntervalChoices +from .choices import DataSourceStatusChoices, JobIntervalChoices, ObjectChangeActionChoices from .models import DataSource @@ -126,19 +127,51 @@ class SystemHousekeepingJob(JobRunner): """ Delete any ObjectChange records older than the configured changelog retention time (if any). """ - self.logger.info("Pruning old changelog entries...") + self.logger.info('Pruning old changelog entries...') config = Config() if not config.CHANGELOG_RETENTION: - self.logger.info("No retention period specified; skipping.") + self.logger.info('No retention period specified; skipping.') return cutoff = timezone.now() - timedelta(days=config.CHANGELOG_RETENTION) - self.logger.debug( - f"Changelog retention period: {config.CHANGELOG_RETENTION} days ({cutoff:%Y-%m-%d %H:%M:%S})" - ) + self.logger.debug(f'Changelog retention period: {config.CHANGELOG_RETENTION} days ({cutoff:%Y-%m-%d %H:%M:%S})') - count = ObjectChange.objects.filter(time__lt=cutoff).delete()[0] - self.logger.info(f"Deleted {count} expired changelog records") + expired_qs = ObjectChange.objects.filter(time__lt=cutoff) + + # When enabled, retain each object's original create record and most recent update record while pruning expired + # changelog entries. This applies only to objects without a delete record. + if config.CHANGELOG_RETAIN_CREATE_LAST_UPDATE: + self.logger.debug('Retaining changelog create records and last update records (excluding deleted objects)') + + deleted_exists = ObjectChange.objects.filter( + action=ObjectChangeActionChoices.ACTION_DELETE, + changed_object_type_id=OuterRef('changed_object_type_id'), + changed_object_id=OuterRef('changed_object_id'), + ) + + # Keep create records only where no delete exists for that object + create_pks_to_keep = ( + ObjectChange.objects.filter(action=ObjectChangeActionChoices.ACTION_CREATE) + .annotate(has_delete=Exists(deleted_exists)) + .filter(has_delete=False) + .values('pk') + ) + + # Keep the most recent update per object only where no delete exists for the object + latest_update_pks_to_keep = ( + ObjectChange.objects.filter(action=ObjectChangeActionChoices.ACTION_UPDATE) + .annotate(has_delete=Exists(deleted_exists)) + .filter(has_delete=False) + .order_by('changed_object_type_id', 'changed_object_id', '-time', '-pk') + .distinct('changed_object_type_id', 'changed_object_id') + .values('pk') + ) + + expired_qs = expired_qs.exclude(pk__in=Subquery(create_pks_to_keep)) + expired_qs = expired_qs.exclude(pk__in=Subquery(latest_update_pks_to_keep)) + + count = expired_qs.delete()[0] + self.logger.info(f'Deleted {count} expired changelog records') def delete_expired_jobs(self): """ diff --git a/netbox/core/migrations/0022_default_ordering_indexes.py b/netbox/core/migrations/0022_default_ordering_indexes.py new file mode 100644 index 000000000..3c62d0c19 --- /dev/null +++ b/netbox/core/migrations/0022_default_ordering_indexes.py @@ -0,0 +1,21 @@ +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('core', '0021_job_queue_name'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddIndex( + model_name='configrevision', + index=models.Index(fields=['-created'], name='core_config_created_ef9552_idx'), + ), + migrations.AddIndex( + model_name='job', + index=models.Index(fields=['-created'], name='core_job_created_efa7cb_idx'), + ), + ] diff --git a/netbox/core/migrations/0023_datasource_sync_permission.py b/netbox/core/migrations/0023_datasource_sync_permission.py new file mode 100644 index 000000000..3516e3020 --- /dev/null +++ b/netbox/core/migrations/0023_datasource_sync_permission.py @@ -0,0 +1,15 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0022_default_ordering_indexes'), + ] + + operations = [ + migrations.AlterModelOptions( + name='datasource', + options={'ordering': ('name',), 'permissions': [('sync', 'Synchronize data from remote source')]}, + ), + ] diff --git a/netbox/core/migrations/0024_job_notifications.py b/netbox/core/migrations/0024_job_notifications.py new file mode 100644 index 000000000..b5316b3b3 --- /dev/null +++ b/netbox/core/migrations/0024_job_notifications.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0023_datasource_sync_permission'), + ] + + operations = [ + migrations.AddField( + model_name='job', + name='notifications', + field=models.CharField(default='always', max_length=30), + ), + ] diff --git a/netbox/core/models/change_logging.py b/netbox/core/models/change_logging.py index c5aab1838..4409994da 100644 --- a/netbox/core/models/change_logging.py +++ b/netbox/core/models/change_logging.py @@ -11,7 +11,7 @@ from mptt.models import MPTTModel from core.choices import ObjectChangeActionChoices from core.querysets import ObjectChangeQuerySet from netbox.models.features import ChangeLoggingMixin, has_feature -from utilities.data import shallow_compare_dict +from utilities.data import deep_compare_dict __all__ = ( 'ObjectChange', @@ -199,18 +199,18 @@ class ObjectChange(models.Model): # Determine which attributes have changed if self.action == ObjectChangeActionChoices.ACTION_CREATE: changed_attrs = sorted(postchange_data.keys()) - elif self.action == ObjectChangeActionChoices.ACTION_DELETE: + return { + 'pre': {k: prechange_data.get(k) for k in changed_attrs}, + 'post': {k: postchange_data.get(k) for k in changed_attrs}, + } + if self.action == ObjectChangeActionChoices.ACTION_DELETE: changed_attrs = sorted(prechange_data.keys()) - else: - # TODO: Support deep (recursive) comparison - changed_data = shallow_compare_dict(prechange_data, postchange_data) - changed_attrs = sorted(changed_data.keys()) - + return { + 'pre': {k: prechange_data.get(k) for k in changed_attrs}, + 'post': {k: postchange_data.get(k) for k in changed_attrs}, + } + diff_added, diff_removed = deep_compare_dict(prechange_data, postchange_data) return { - 'pre': { - k: prechange_data.get(k) for k in changed_attrs - }, - 'post': { - k: postchange_data.get(k) for k in changed_attrs - }, + 'pre': dict(sorted(diff_removed.items())), + 'post': dict(sorted(diff_added.items())), } diff --git a/netbox/core/models/config.py b/netbox/core/models/config.py index ac793d089..7ffadc667 100644 --- a/netbox/core/models/config.py +++ b/netbox/core/models/config.py @@ -37,6 +37,9 @@ class ConfigRevision(models.Model): class Meta: ordering = ['-created'] + indexes = ( + models.Index(fields=('-created',)), # Default ordering + ) verbose_name = _('config revision') verbose_name_plural = _('config revisions') constraints = [ diff --git a/netbox/core/models/data.py b/netbox/core/models/data.py index 5555c655e..845317ee7 100644 --- a/netbox/core/models/data.py +++ b/netbox/core/models/data.py @@ -87,6 +87,9 @@ class DataSource(JobsMixin, PrimaryModel): ordering = ('name',) verbose_name = _('data source') verbose_name_plural = _('data sources') + permissions = [ + ('sync', 'Synchronize data from remote source'), + ] def __str__(self): return f'{self.name}' diff --git a/netbox/core/models/jobs.py b/netbox/core/models/jobs.py index cc759c952..be9b6f55c 100644 --- a/netbox/core/models/jobs.py +++ b/netbox/core/models/jobs.py @@ -16,7 +16,7 @@ from django.utils import timezone from django.utils.translation import gettext as _ from rq.exceptions import InvalidJobOperation -from core.choices import JobStatusChoices +from core.choices import JobNotificationChoices, JobStatusChoices from core.dataclasses import JobLogEntry from core.events import JOB_COMPLETED, JOB_ERRORED, JOB_FAILED from core.models import ObjectType @@ -118,6 +118,12 @@ class Job(models.Model): blank=True, help_text=_('Name of the queue in which this job was enqueued') ) + notifications = models.CharField( + verbose_name=_('notifications'), + max_length=30, + choices=JobNotificationChoices, + default=JobNotificationChoices.NOTIFICATION_ALWAYS + ) log_entries = ArrayField( verbose_name=_('log entries'), base_field=models.JSONField( @@ -133,6 +139,7 @@ class Job(models.Model): class Meta: ordering = ['-created'] indexes = ( + models.Index(fields=('-created',)), # Default ordering models.Index(fields=('object_type', 'object_id')), ) verbose_name = _('job') @@ -237,12 +244,16 @@ class Job(models.Model): self.save() # Notify the user (if any) of completion - if self.user: - Notification( - user=self.user, - object=self, - event_type=self.get_event_type(), - ).save() + if self.user and self.notifications != JobNotificationChoices.NOTIFICATION_NEVER: + if ( + self.notifications == JobNotificationChoices.NOTIFICATION_ALWAYS or + status != JobStatusChoices.STATUS_COMPLETED + ): + Notification( + user=self.user, + object=self, + event_type=self.get_event_type(), + ).save() # Send signal job_end.send(self) @@ -266,6 +277,7 @@ class Job(models.Model): interval=None, immediate=False, queue_name=None, + notifications=None, **kwargs ): """ @@ -280,6 +292,7 @@ class Job(models.Model): interval: Recurrence interval (in minutes) immediate: Run the job immediately without scheduling it in the background. Should be used for interactive management commands only. + notifications: Notification behavior on job completion (always, on_failure, or never) """ if schedule_at and immediate: raise ValueError(_("enqueue() cannot be called with values for both schedule_at and immediate.")) @@ -301,7 +314,8 @@ class Job(models.Model): interval=interval, user=user, job_id=uuid.uuid4(), - queue_name=rq_queue_name + queue_name=rq_queue_name, + notifications=notifications if notifications is not None else JobNotificationChoices.NOTIFICATION_ALWAYS ) job.full_clean() job.save() diff --git a/netbox/core/models/object_types.py b/netbox/core/models/object_types.py index bb18f50b6..78847f912 100644 --- a/netbox/core/models/object_types.py +++ b/netbox/core/models/object_types.py @@ -51,7 +51,6 @@ class ObjectTypeManager(models.Manager): """ return self.get(app_label=app_label, model=model) - # TODO: Remove in NetBox v4.5 def get_for_id(self, id): """ Retrieve an ObjectType by its primary key (numeric ID). diff --git a/netbox/core/tables/jobs.py b/netbox/core/tables/jobs.py index 2406101a1..e97fb0ac4 100644 --- a/netbox/core/tables/jobs.py +++ b/netbox/core/tables/jobs.py @@ -1,4 +1,6 @@ import django_tables2 as tables +from django.utils.html import conditional_escape +from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ from core.constants import JOB_LOG_ENTRY_LEVELS @@ -82,3 +84,9 @@ class JobLogEntryTable(BaseTable): class Meta(BaseTable.Meta): empty_text = _('No log entries') fields = ('timestamp', 'level', 'message') + + def render_message(self, record, value): + if record.get('level') == 'error' and '\n' in value: + value = conditional_escape(value) + return mark_safe(f'
{value}
') + return value diff --git a/netbox/core/tests/test_changelog.py b/netbox/core/tests/test_changelog.py index c3924bccb..dc9dbb2dc 100644 --- a/netbox/core/tests/test_changelog.py +++ b/netbox/core/tests/test_changelog.py @@ -1,9 +1,16 @@ +import logging +import uuid +from datetime import timedelta +from unittest.mock import patch + from django.contrib.contenttypes.models import ContentType -from django.test import override_settings +from django.test import TestCase, override_settings from django.urls import reverse +from django.utils import timezone from rest_framework import status from core.choices import ObjectChangeActionChoices +from core.jobs import SystemHousekeepingJob from core.models import ObjectChange, ObjectType from dcim.choices import InterfaceTypeChoices, ModuleStatusChoices, SiteStatusChoices from dcim.models import ( @@ -694,3 +701,99 @@ class ChangeLogAPITest(APITestCase): self.assertEqual(changes[3].changed_object_type, ContentType.objects.get_for_model(Module)) self.assertEqual(changes[3].changed_object_id, module.pk) self.assertEqual(changes[3].action, ObjectChangeActionChoices.ACTION_DELETE) + + +class ChangelogPruneRetentionTest(TestCase): + """Test suite for Changelog pruning retention settings.""" + + @staticmethod + def _make_oc(*, ct, obj_id, action, ts): + oc = ObjectChange.objects.create( + changed_object_type=ct, + changed_object_id=obj_id, + action=action, + user_name='test', + request_id=uuid.uuid4(), + object_repr=f'Object {obj_id}', + ) + ObjectChange.objects.filter(pk=oc.pk).update(time=ts) + return oc.pk + + @staticmethod + def _run_prune(*, retention_days, retain_create_last_update): + job = SystemHousekeepingJob.__new__(SystemHousekeepingJob) + job.logger = logging.getLogger('netbox.tests.changelog_prune') + + with patch('core.jobs.Config') as MockConfig: + cfg = MockConfig.return_value + cfg.CHANGELOG_RETENTION = retention_days + cfg.CHANGELOG_RETAIN_CREATE_LAST_UPDATE = retain_create_last_update + job.prune_changelog() + + def test_prune_retain_create_last_update_excludes_deleted_objects(self): + ct = ContentType.objects.get_for_model(Site) + + retention_days = 90 + now = timezone.now() + cutoff = now - timedelta(days=retention_days) + + expired_old = cutoff - timedelta(days=10) + expired_newer = cutoff - timedelta(days=1) + not_expired = cutoff + timedelta(days=1) + + # A) Not deleted: should keep CREATE + latest UPDATE, prune intermediate UPDATEs + a_create = self._make_oc(ct=ct, obj_id=1, action=ObjectChangeActionChoices.ACTION_CREATE, ts=expired_old) + a_update1 = self._make_oc(ct=ct, obj_id=1, action=ObjectChangeActionChoices.ACTION_UPDATE, ts=expired_old) + a_update2 = self._make_oc(ct=ct, obj_id=1, action=ObjectChangeActionChoices.ACTION_UPDATE, ts=expired_newer) + + # B) Deleted (all expired): should keep NOTHING + b_create = self._make_oc(ct=ct, obj_id=2, action=ObjectChangeActionChoices.ACTION_CREATE, ts=expired_old) + b_update = self._make_oc(ct=ct, obj_id=2, action=ObjectChangeActionChoices.ACTION_UPDATE, ts=expired_newer) + b_delete = self._make_oc(ct=ct, obj_id=2, action=ObjectChangeActionChoices.ACTION_DELETE, ts=expired_newer) + + # C) Deleted but delete is not expired: create/update expired should be pruned; delete remains + c_create = self._make_oc(ct=ct, obj_id=3, action=ObjectChangeActionChoices.ACTION_CREATE, ts=expired_old) + c_update = self._make_oc(ct=ct, obj_id=3, action=ObjectChangeActionChoices.ACTION_UPDATE, ts=expired_newer) + c_delete = self._make_oc(ct=ct, obj_id=3, action=ObjectChangeActionChoices.ACTION_DELETE, ts=not_expired) + + self._run_prune(retention_days=retention_days, retain_create_last_update=True) + + remaining = set(ObjectChange.objects.values_list('pk', flat=True)) + + # A) Not deleted -> create + latest update remain + self.assertIn(a_create, remaining) + self.assertIn(a_update2, remaining) + self.assertNotIn(a_update1, remaining) + + # B) Deleted (all expired) -> nothing remains + self.assertNotIn(b_create, remaining) + self.assertNotIn(b_update, remaining) + self.assertNotIn(b_delete, remaining) + + # C) Deleted, delete not expired -> delete remains, but create/update are pruned + self.assertNotIn(c_create, remaining) + self.assertNotIn(c_update, remaining) + self.assertIn(c_delete, remaining) + + def test_prune_disabled_deletes_all_expired(self): + ct = ContentType.objects.get_for_model(Site) + + retention_days = 90 + now = timezone.now() + cutoff = now - timedelta(days=retention_days) + expired = cutoff - timedelta(days=1) + not_expired = cutoff + timedelta(days=1) + + # expired create/update should be deleted when feature disabled + x_create = self._make_oc(ct=ct, obj_id=10, action=ObjectChangeActionChoices.ACTION_CREATE, ts=expired) + x_update = self._make_oc(ct=ct, obj_id=10, action=ObjectChangeActionChoices.ACTION_UPDATE, ts=expired) + + # non-expired delete should remain regardless + y_delete = self._make_oc(ct=ct, obj_id=11, action=ObjectChangeActionChoices.ACTION_DELETE, ts=not_expired) + + self._run_prune(retention_days=retention_days, retain_create_last_update=False) + + remaining = set(ObjectChange.objects.values_list('pk', flat=True)) + self.assertNotIn(x_create, remaining) + self.assertNotIn(x_update, remaining) + self.assertIn(y_delete, remaining) diff --git a/netbox/core/tests/test_models.py b/netbox/core/tests/test_models.py index 2d7dce923..af342324f 100644 --- a/netbox/core/tests/test_models.py +++ b/netbox/core/tests/test_models.py @@ -1,13 +1,16 @@ +import uuid from unittest.mock import MagicMock, patch from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist from django.test import TestCase -from core.choices import ObjectChangeActionChoices +from core.choices import JobNotificationChoices, JobStatusChoices, ObjectChangeActionChoices from core.models import DataSource, Job, ObjectType from dcim.models import Device, Location, Site +from extras.models import Notification from netbox.constants import CENSOR_TOKEN, CENSOR_TOKEN_CHANGED +from users.models import User class DataSourceIgnoreRulesTestCase(TestCase): @@ -226,6 +229,18 @@ class ObjectTypeTest(TestCase): class JobTest(TestCase): + def _make_job(self, user, notifications): + """ + Create and return a persisted Job with the given user and notifications setting. + """ + return Job.objects.create( + name='Test Job', + job_id=uuid.uuid4(), + user=user, + notifications=notifications, + status=JobStatusChoices.STATUS_RUNNING, + ) + @patch('core.models.jobs.django_rq.get_queue') def test_delete_cancels_job_from_correct_queue(self, mock_get_queue): """ @@ -257,3 +272,75 @@ class JobTest(TestCase): mock_get_queue.assert_called_with(custom_queue) mock_queue.fetch_job.assert_called_with(str(job.job_id)) mock_rq_job.cancel.assert_called_once() + + @patch('core.models.jobs.job_end') + def test_terminate_notification_always(self, mock_job_end): + """ + With notifications=always, a Notification should be created for every + terminal status (completed, failed, errored). + """ + user = User.objects.create_user(username='notification-always') + + for status in ( + JobStatusChoices.STATUS_COMPLETED, + JobStatusChoices.STATUS_FAILED, + JobStatusChoices.STATUS_ERRORED, + ): + with self.subTest(status=status): + job = self._make_job(user, JobNotificationChoices.NOTIFICATION_ALWAYS) + job.terminate(status=status) + self.assertEqual( + Notification.objects.filter(user=user, object_id=job.pk).count(), + 1, + msg=f"Expected a notification for status={status} with notifications=always", + ) + + @patch('core.models.jobs.job_end') + def test_terminate_notification_on_failure(self, mock_job_end): + """ + With notifications=on_failure, a Notification should be created only for + non-completed terminal statuses (failed, errored), not for completed. + """ + user = User.objects.create_user(username='notification-on-failure') + + # No notification on successful completion + job = self._make_job(user, JobNotificationChoices.NOTIFICATION_ON_FAILURE) + job.terminate(status=JobStatusChoices.STATUS_COMPLETED) + self.assertEqual( + Notification.objects.filter(user=user, object_id=job.pk).count(), + 0, + msg="Expected no notification for status=completed with notifications=on_failure", + ) + + # Notification on failure/error + for status in (JobStatusChoices.STATUS_FAILED, JobStatusChoices.STATUS_ERRORED): + with self.subTest(status=status): + job = self._make_job(user, JobNotificationChoices.NOTIFICATION_ON_FAILURE) + job.terminate(status=status) + self.assertEqual( + Notification.objects.filter(user=user, object_id=job.pk).count(), + 1, + msg=f"Expected a notification for status={status} with notifications=on_failure", + ) + + @patch('core.models.jobs.job_end') + def test_terminate_notification_never(self, mock_job_end): + """ + With notifications=never, no Notification should be created regardless + of terminal status. + """ + user = User.objects.create_user(username='notification-never') + + for status in ( + JobStatusChoices.STATUS_COMPLETED, + JobStatusChoices.STATUS_FAILED, + JobStatusChoices.STATUS_ERRORED, + ): + with self.subTest(status=status): + job = self._make_job(user, JobNotificationChoices.NOTIFICATION_NEVER) + job.terminate(status=status) + self.assertEqual( + Notification.objects.filter(user=user, object_id=job.pk).count(), + 0, + msg=f"Expected no notification for status={status} with notifications=never", + ) diff --git a/netbox/core/views.py b/netbox/core/views.py index e419abb1a..fe663cdaf 100644 --- a/netbox/core/views.py +++ b/netbox/core/views.py @@ -50,7 +50,7 @@ from netbox.views import generic from netbox.views.generic.base import BaseObjectView from netbox.views.generic.mixins import TableMixin from utilities.apps import get_installed_apps -from utilities.data import shallow_compare_dict +from utilities.data import deep_compare_dict from utilities.forms import ConfirmationForm from utilities.htmx import htmx_partial from utilities.json import ConfigJSONEncoder @@ -103,6 +103,7 @@ class DataSourceView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='core.DataFile', filters={'source_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['source'], ), ], ) @@ -370,17 +371,14 @@ class ObjectChangeView(generic.ObjectView): prechange_data = instance.prechange_data_clean if prechange_data and instance.postchange_data: - diff_added = shallow_compare_dict( - prechange_data or dict(), - instance.postchange_data_clean or dict(), + diff_added, diff_removed = deep_compare_dict( + prechange_data, + instance.postchange_data_clean, exclude=['last_updated'], ) - diff_removed = { - x: prechange_data.get(x) for x in diff_added - } if prechange_data else {} else: - diff_added = None - diff_removed = None + diff_added = {} + diff_removed = {} return { 'diff_added': diff_added, diff --git a/netbox/dcim/api/serializers_/cables.py b/netbox/dcim/api/serializers_/cables.py index 1f23815ed..1f2eb6b5d 100644 --- a/netbox/dcim/api/serializers_/cables.py +++ b/netbox/dcim/api/serializers_/cables.py @@ -3,7 +3,7 @@ from drf_spectacular.utils import extend_schema_field from rest_framework import serializers from dcim.choices import * -from dcim.models import Cable, CablePath, CableTermination +from dcim.models import Cable, CableBundle, CablePath, CableTermination from netbox.api.fields import ChoiceField, ContentTypeField from netbox.api.gfk_fields import GFKSerializerField from netbox.api.serializers import ( @@ -16,6 +16,7 @@ from tenancy.api.serializers_.tenants import TenantSerializer from utilities.api import get_serializer_for_model __all__ = ( + 'CableBundleSerializer', 'CablePathSerializer', 'CableSerializer', 'CableTerminationSerializer', @@ -24,6 +25,18 @@ __all__ = ( ) +class CableBundleSerializer(PrimaryModelSerializer): + cable_count = serializers.IntegerField(read_only=True, default=0) + + class Meta: + model = CableBundle + fields = [ + 'id', 'url', 'display_url', 'display', 'name', 'description', 'owner', 'comments', 'tags', + 'custom_fields', 'created', 'last_updated', 'cable_count', + ] + brief_fields = ('id', 'url', 'display', 'name', 'description') + + class CableSerializer(PrimaryModelSerializer): a_terminations = GenericObjectSerializer(many=True, required=False) b_terminations = GenericObjectSerializer(many=True, required=False) @@ -31,12 +44,13 @@ class CableSerializer(PrimaryModelSerializer): profile = ChoiceField(choices=CableProfileChoices, required=False) tenant = TenantSerializer(nested=True, required=False, allow_null=True) length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False, allow_null=True) + bundle = CableBundleSerializer(nested=True, required=False, allow_null=True, default=None) class Meta: model = Cable fields = [ 'id', 'url', 'display_url', 'display', 'type', 'a_terminations', 'b_terminations', 'status', 'profile', - 'tenant', 'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags', + 'tenant', 'bundle', 'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'label', 'description') diff --git a/netbox/dcim/api/serializers_/device_components.py b/netbox/dcim/api/serializers_/device_components.py index 09a850ad5..60e624a71 100644 --- a/netbox/dcim/api/serializers_/device_components.py +++ b/netbox/dcim/api/serializers_/device_components.py @@ -423,27 +423,29 @@ class ModuleBaySerializer(OwnerMixin, NetBoxModelSerializer): required=False, allow_null=True ) + _occupied = serializers.BooleanField(required=False, read_only=True) class Meta: model = ModuleBay fields = [ - 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'installed_module', 'label', 'position', - 'description', 'owner', 'tags', 'custom_fields', 'created', 'last_updated', + 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'position', 'enabled', + 'description', 'installed_module', 'owner', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', ] - brief_fields = ('id', 'url', 'display', 'installed_module', 'name', 'description') + brief_fields = ('id', 'url', 'display', 'installed_module', 'name', 'enabled', 'description', '_occupied') class DeviceBaySerializer(OwnerMixin, NetBoxModelSerializer): device = DeviceSerializer(nested=True) installed_device = DeviceSerializer(nested=True, required=False, allow_null=True) + _occupied = serializers.BooleanField(required=False, read_only=True) class Meta: model = DeviceBay fields = [ - 'id', 'url', 'display_url', 'display', 'device', 'name', 'label', 'description', 'installed_device', - 'owner', 'tags', 'custom_fields', 'created', 'last_updated', + 'id', 'url', 'display_url', 'display', 'device', 'name', 'label', 'enabled', 'description', + 'installed_device', 'owner', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', ] - brief_fields = ('id', 'url', 'display', 'device', 'name', 'description') + brief_fields = ('id', 'url', 'display', 'device', 'name', 'enabled', 'description', '_occupied',) class InventoryItemSerializer(OwnerMixin, NetBoxModelSerializer): diff --git a/netbox/dcim/api/serializers_/devices.py b/netbox/dcim/api/serializers_/devices.py index 1154374c1..51cb6c3f2 100644 --- a/netbox/dcim/api/serializers_/devices.py +++ b/netbox/dcim/api/serializers_/devices.py @@ -58,10 +58,30 @@ class DeviceSerializer(PrimaryModelSerializer): ) status = ChoiceField(choices=DeviceStatusChoices, required=False) airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False) - primary_ip = IPAddressSerializer(nested=True, read_only=True, allow_null=True) - primary_ip4 = IPAddressSerializer(nested=True, required=False, allow_null=True) - primary_ip6 = IPAddressSerializer(nested=True, required=False, allow_null=True) - oob_ip = IPAddressSerializer(nested=True, required=False, allow_null=True) + primary_ip = IPAddressSerializer( + nested=True, + read_only=True, + allow_null=True, + fields=[*IPAddressSerializer.Meta.brief_fields, 'nat_inside', 'nat_outside'], + ) + primary_ip4 = IPAddressSerializer( + nested=True, + required=False, + allow_null=True, + fields=[*IPAddressSerializer.Meta.brief_fields, 'nat_inside', 'nat_outside'], + ) + primary_ip6 = IPAddressSerializer( + nested=True, + required=False, + allow_null=True, + fields=[*IPAddressSerializer.Meta.brief_fields, 'nat_inside', 'nat_outside'], + ) + oob_ip = IPAddressSerializer( + nested=True, + required=False, + allow_null=True, + fields=[*IPAddressSerializer.Meta.brief_fields, 'nat_inside', 'nat_outside'], + ) parent_device = serializers.SerializerMethodField() cluster = ClusterSerializer(nested=True, required=False, allow_null=True) virtual_chassis = VirtualChassisSerializer(nested=True, required=False, allow_null=True, default=None) @@ -151,48 +171,80 @@ class ModuleSerializer(PrimaryModelSerializer): module_bay = NestedModuleBaySerializer() module_type = ModuleTypeSerializer(nested=True) status = ChoiceField(choices=ModuleStatusChoices, required=False) + replicate_components = serializers.BooleanField( + required=False, + default=True, + write_only=True, + label=_('Replicate components'), + help_text=_('Automatically populate components associated with this module type (default: true)') + ) + adopt_components = serializers.BooleanField( + required=False, + default=False, + write_only=True, + label=_('Adopt components'), + help_text=_('Adopt already existing components') + ) class Meta: model = Module fields = [ 'id', 'url', 'display_url', 'display', 'device', 'module_bay', 'module_type', 'status', 'serial', 'asset_tag', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', + 'replicate_components', 'adopt_components', ] brief_fields = ('id', 'url', 'display', 'device', 'module_bay', 'module_type', 'description') def validate(self, data): + # When used as a nested serializer (e.g. as the `module` field on device component + # serializers), `data` is already a resolved Module instance — skip our custom logic. + if self.nested: + return super().validate(data) + + # Pop write-only transient fields before ValidatedModelSerializer tries to + # construct a Module instance for full_clean(); restore them afterwards. + replicate_components = data.pop('replicate_components', True) + adopt_components = data.pop('adopt_components', False) data = super().validate(data) - if self.nested: + # For updates these fields are not meaningful; omit them from validated_data so that + # ModelSerializer.update() does not set unexpected attributes on the instance. + if self.instance: return data - # Skip validation for existing modules (updates) - if self.instance is not None: + # Always pass the flags to create() so it can set the correct private attributes. + data['replicate_components'] = replicate_components + data['adopt_components'] = adopt_components + + # Skip conflict checks when no component operations are requested. + if not replicate_components and not adopt_components: return data - module_bay = data.get('module_bay') - module_type = data.get('module_type') device = data.get('device') + module_type = data.get('module_type') + module_bay = data.get('module_bay') - if not all((module_bay, module_type, device)): + # Required-field validation fires separately; skip here if any are missing. + if not all([device, module_type, module_bay]): return data positions = get_module_bay_positions(module_bay) - for templates, component_attribute in [ - ("consoleporttemplates", "consoleports"), - ("consoleserverporttemplates", "consoleserverports"), - ("interfacetemplates", "interfaces"), - ("powerporttemplates", "powerports"), - ("poweroutlettemplates", "poweroutlets"), - ("rearporttemplates", "rearports"), - ("frontporttemplates", "frontports"), + for templates_attr, component_attr in [ + ('consoleporttemplates', 'consoleports'), + ('consoleserverporttemplates', 'consoleserverports'), + ('interfacetemplates', 'interfaces'), + ('powerporttemplates', 'powerports'), + ('poweroutlettemplates', 'poweroutlets'), + ('rearporttemplates', 'rearports'), + ('frontporttemplates', 'frontports'), ]: installed_components = { - component.name: component for component in getattr(device, component_attribute).all() + component.name: component + for component in getattr(device, component_attr).all() } - for template in getattr(module_type, templates).all(): + for template in getattr(module_type, templates_attr).all(): resolved_name = template.name if MODULE_TOKEN in template.name: if not module_bay.position: @@ -204,7 +256,17 @@ class ModuleSerializer(PrimaryModelSerializer): except ValueError as e: raise serializers.ValidationError(str(e)) - if resolved_name in installed_components: + existing_item = installed_components.get(resolved_name) + + if adopt_components and existing_item and existing_item.module: + raise serializers.ValidationError( + _("Cannot adopt {model} {name} as it already belongs to a module").format( + model=template.component_model.__name__, + name=resolved_name + ) + ) + + if not adopt_components and resolved_name in installed_components: raise serializers.ValidationError( _("A {model} named {name} already exists").format( model=template.component_model.__name__, @@ -214,6 +276,27 @@ class ModuleSerializer(PrimaryModelSerializer): return data + def create(self, validated_data): + replicate_components = validated_data.pop('replicate_components', True) + adopt_components = validated_data.pop('adopt_components', False) + + # Tags are handled after save; pop them here to pass to _save_tags() + tags = validated_data.pop('tags', None) + + # _adopt_components and _disable_replication must be set on the instance before + # save() is called, so we cannot delegate to super().create() here. + instance = self.Meta.model(**validated_data) + if adopt_components: + instance._adopt_components = True + if not replicate_components: + instance._disable_replication = True + instance.save() + + if tags is not None: + self._save_tags(instance, tags) + + return instance + class MACAddressSerializer(PrimaryModelSerializer): assigned_object_type = ContentTypeField( diff --git a/netbox/dcim/api/serializers_/devicetype_components.py b/netbox/dcim/api/serializers_/devicetype_components.py index 147bc1701..b9b9c95ee 100644 --- a/netbox/dcim/api/serializers_/devicetype_components.py +++ b/netbox/dcim/api/serializers_/devicetype_components.py @@ -317,10 +317,10 @@ class ModuleBayTemplateSerializer(ComponentTemplateSerializer): class Meta: model = ModuleBayTemplate fields = [ - 'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'position', 'description', + 'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'position', 'enabled', 'description', 'created', 'last_updated', ] - brief_fields = ('id', 'url', 'display', 'name', 'description') + brief_fields = ('id', 'url', 'display', 'name', 'enabled', 'description') class DeviceBayTemplateSerializer(ComponentTemplateSerializer): @@ -331,10 +331,10 @@ class DeviceBayTemplateSerializer(ComponentTemplateSerializer): class Meta: model = DeviceBayTemplate fields = [ - 'id', 'url', 'display', 'device_type', 'name', 'label', 'description', + 'id', 'url', 'display', 'device_type', 'name', 'label', 'enabled', 'description', 'created', 'last_updated' ] - brief_fields = ('id', 'url', 'display', 'name', 'description') + brief_fields = ('id', 'url', 'display', 'name', 'enabled', 'description') class InventoryItemTemplateSerializer(ComponentTemplateSerializer): diff --git a/netbox/dcim/api/serializers_/racks.py b/netbox/dcim/api/serializers_/racks.py index b87140bed..2a4bd0834 100644 --- a/netbox/dcim/api/serializers_/racks.py +++ b/netbox/dcim/api/serializers_/racks.py @@ -1,9 +1,11 @@ from django.utils.translation import gettext as _ +from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import extend_schema_field from rest_framework import serializers from dcim.choices import * from dcim.constants import * -from dcim.models import Rack, RackReservation, RackRole, RackType +from dcim.models import Rack, RackGroup, RackReservation, RackRole, RackType from netbox.api.fields import ChoiceField, RelatedObjectCountField from netbox.api.serializers import OrganizationalModelSerializer, PrimaryModelSerializer from netbox.choices import * @@ -16,6 +18,7 @@ from .sites import LocationSerializer, SiteSerializer __all__ = ( 'RackElevationDetailFilterSerializer', + 'RackGroupSerializer', 'RackReservationSerializer', 'RackRoleSerializer', 'RackSerializer', @@ -23,6 +26,20 @@ __all__ = ( ) +class RackGroupSerializer(OrganizationalModelSerializer): + + # Related object counts + rack_count = RelatedObjectCountField('racks') + + class Meta: + model = RackGroup + fields = [ + 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'owner', 'comments', 'tags', + 'custom_fields', 'created', 'last_updated', 'rack_count', + ] + brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count') + + class RackRoleSerializer(OrganizationalModelSerializer): # Related object counts @@ -87,6 +104,11 @@ class RackSerializer(RackBaseSerializer): allow_null=True, default=None ) + group = RackGroupSerializer( + nested=True, + required=False, + allow_null=True + ) tenant = TenantSerializer( nested=True, required=False, @@ -127,11 +149,11 @@ class RackSerializer(RackBaseSerializer): class Meta: model = Rack fields = [ - 'id', 'url', 'display_url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status', - 'role', 'serial', 'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'weight', - 'max_weight', 'weight_unit', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', - 'mounting_depth', 'airflow', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', - 'last_updated', 'device_count', 'powerfeed_count', + 'id', 'url', 'display_url', 'display', 'name', 'facility_id', 'site', 'location', 'group', 'tenant', + 'status', 'role', 'serial', 'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', + 'weight', 'max_weight', 'weight_unit', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', + 'outer_unit', 'mounting_depth', 'airflow', 'description', 'owner', 'comments', 'tags', 'custom_fields', + 'created', 'last_updated', 'device_count', 'powerfeed_count', ] brief_fields = ('id', 'url', 'display', 'name', 'description', 'device_count') @@ -153,11 +175,17 @@ class RackReservationSerializer(PrimaryModelSerializer): allow_null=True, ) + unit_count = serializers.SerializerMethodField() + + @extend_schema_field(OpenApiTypes.INT32) + def get_unit_count(self, obj): + return len(obj.units) + class Meta: model = RackReservation fields = [ - 'id', 'url', 'display_url', 'display', 'rack', 'units', 'status', 'created', 'last_updated', 'user', - 'tenant', 'description', 'owner', 'comments', 'tags', 'custom_fields', + 'id', 'url', 'display_url', 'display', 'rack', 'units', 'unit_count', 'status', 'created', 'last_updated', + 'user', 'tenant', 'description', 'owner', 'comments', 'tags', 'custom_fields', ] brief_fields = ('id', 'url', 'display', 'status', 'user', 'description', 'units') diff --git a/netbox/dcim/api/urls.py b/netbox/dcim/api/urls.py index e20e36c2f..852a871bb 100644 --- a/netbox/dcim/api/urls.py +++ b/netbox/dcim/api/urls.py @@ -12,6 +12,7 @@ router.register('sites', views.SiteViewSet) # Racks router.register('locations', views.LocationViewSet) +router.register('rack-groups', views.RackGroupViewSet) router.register('rack-types', views.RackTypeViewSet) router.register('rack-roles', views.RackRoleViewSet) router.register('racks', views.RackViewSet) @@ -63,6 +64,7 @@ router.register('mac-addresses', views.MACAddressViewSet) # Cables router.register('cables', views.CableViewSet) router.register('cable-terminations', views.CableTerminationViewSet) +router.register('cable-bundles', views.CableBundleViewSet) # Virtual chassis router.register('virtual-chassis', views.VirtualChassisViewSet) diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index 6cf8e7a3d..3efa63acb 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -19,6 +19,7 @@ from netbox.api.pagination import StripCountAnnotationsPaginator from netbox.api.viewsets import MPTTLockedMixin, NetBoxModelViewSet, NetBoxReadOnlyModelViewSet from netbox.api.viewsets.mixins import SequentialBulkCreatesMixin from utilities.api import get_serializer_for_model +from utilities.query import count_related from utilities.query_functions import CollateAsChar from virtualization.models import VirtualMachine @@ -154,6 +155,17 @@ class LocationViewSet(MPTTLockedMixin, NetBoxModelViewSet): filterset_class = filtersets.LocationFilterSet +# +# Rack groups +# + + +class RackGroupViewSet(NetBoxModelViewSet): + queryset = RackGroup.objects.all() + serializer_class = serializers.RackGroupSerializer + filterset_class = filtersets.RackGroupFilterSet + + # # Rack roles # @@ -574,6 +586,14 @@ class CableTerminationViewSet(NetBoxReadOnlyModelViewSet): filterset_class = filtersets.CableTerminationFilterSet +class CableBundleViewSet(NetBoxModelViewSet): + queryset = CableBundle.objects.annotate( + cable_count=count_related(Cable, 'bundle') + ) + serializer_class = serializers.CableBundleSerializer + filterset_class = filtersets.CableBundleFilterSet + + # # Virtual chassis # diff --git a/netbox/dcim/cable_profiles.py b/netbox/dcim/cable_profiles.py index 8945370f2..921392dc0 100644 --- a/netbox/dcim/cable_profiles.py +++ b/netbox/dcim/cable_profiles.py @@ -1,9 +1,14 @@ +from collections import defaultdict, namedtuple + from django.core.exceptions import ValidationError +from django.db.models import Q from django.utils.translation import gettext_lazy as _ from dcim.choices import CableEndChoices from dcim.models import CableTermination +PeerLookupKey = namedtuple('PeerLookupKey', ['cable_id', 'cable_end', 'connector', 'position']) + class BaseCableProfile: """Base class for representing a cable profile.""" @@ -55,9 +60,14 @@ class BaseCableProfile: return self._mapping.get((connector, position)) return connector, position - def get_peer_termination(self, termination, position): + # + # Peer termination resolution + # + + def _get_peer_lookup_key(self, termination, position): """ - Given a terminating object, return the peer terminating object (if any) on the opposite end of the cable. + Given a cabled object and a local position, return the (cable_id, cable_end, connector, position) + tuple identifying the corresponding peer CableTermination on the opposite end of the cable. """ try: connector, position = self.get_mapped_position( @@ -70,10 +80,17 @@ class BaseCableProfile: f"Could not map connector {termination.cable_connector} position {position} on side " f"{termination.cable_end}" ) + return PeerLookupKey(termination.cable_id, termination.opposite_cable_end, connector, position) + + def get_peer_termination(self, termination, position): + """ + Given a terminating object, return the peer terminating object (if any) on the opposite end of the cable. + """ + cable_id, cable_end, connector, position = self._get_peer_lookup_key(termination, position) try: ct = CableTermination.objects.get( - cable=termination.cable, - cable_end=termination.opposite_cable_end, + cable_id=cable_id, + cable_end=cable_end, connector=connector, positions__contains=[position], ) @@ -81,6 +98,81 @@ class BaseCableProfile: except CableTermination.DoesNotExist: return None, None + def get_peer_terminations(self, term_position_pairs): + """ + Resolve a batch of (termination, position) pairs to peer terminations. + """ + if not term_position_pairs: + return [] + + # Fast path: a single pair doesn't benefit from batching + if len(term_position_pairs) == 1: + return [self.get_peer_termination(*term_position_pairs[0])] + + lookup_keys = [ + self._get_peer_lookup_key(termination, position) for termination, position in term_position_pairs + ] + + # Group requested positions by (cable_id, cable_end, connector) so we can + # build one overlap clause per group instead of one clause per position. + positions_by_group = defaultdict(set) + for key in lookup_keys: + positions_by_group[(key.cable_id, key.cable_end, key.connector)].add(key.position) + + q_filter = Q() + for (cable_id, cable_end, connector), positions in positions_by_group.items(): + q_filter |= Q( + cable_id=cable_id, + cable_end=cable_end, + connector=connector, + positions__overlap=list(positions), + ) + + peer_ct_by_key = {} + term_ids_by_type = defaultdict(set) + model_by_type = {} + + for ct in CableTermination.objects.filter(q_filter).select_related('termination_type'): + model_by_type[ct.termination_type_id] = ct.termination_type.model_class() + term_ids_by_type[ct.termination_type_id].add(ct.termination_id) + + group_key = (ct.cable_id, ct.cable_end, ct.connector) + requested_positions = positions_by_group.get(group_key, ()) + + # The overlap query may return a termination carrying additional + # positions, so only index the positions we actually requested. + for pos in ct.positions or []: + if pos not in requested_positions: + continue + + key = PeerLookupKey(ct.cable_id, ct.cable_end, ct.connector, pos) + if key in peer_ct_by_key and peer_ct_by_key[key].pk != ct.pk: + raise CableTermination.MultipleObjectsReturned( + f"Multiple peer terminations found for cable {ct.cable_id} end {ct.cable_end} " + f"connector {ct.connector} position {pos}" + ) + peer_ct_by_key[key] = ct + + # Use _base_manager to ensure all termination objects are loaded + # regardless of any custom default manager filters (e.g. soft-delete). + # Note: Django's GenericForeignKey / ContentType.get_object_for_this_type() + # uses _default_manager, but _base_manager is safer for bulk resolution + # during path tracing. + term_by_type = { + type_id: model_by_type[type_id]._base_manager.in_bulk(ids) for type_id, ids in term_ids_by_type.items() + } + + results = [] + for key in lookup_keys: + ct = peer_ct_by_key.get(key) + if ct is None: + results.append((None, None)) + else: + termination = term_by_type[ct.termination_type_id].get(ct.termination_id) + results.append((termination, key.position if termination is not None else None)) + + return results + @staticmethod def get_position_list(n): """Return a list of integers from 1 to n, inclusive.""" diff --git a/netbox/dcim/constants.py b/netbox/dcim/constants.py index 669345d7c..87f8844e0 100644 --- a/netbox/dcim/constants.py +++ b/netbox/dcim/constants.py @@ -1,3 +1,5 @@ +import re + from django.db.models import Q from .choices import InterfaceTypeChoices @@ -79,6 +81,7 @@ NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES # MODULE_TOKEN = '{module}' +VC_POSITION_RE = re.compile(r'\{vc_position(?::([^}]*))?\}') MODULAR_COMPONENT_TEMPLATE_MODELS = Q( app_label='dcim', diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 5e8bf6481..05a9751dc 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -1,6 +1,7 @@ import django_filters import netaddr from django.contrib.contenttypes.models import ContentType +from django.db.models import Func, IntegerField from django.utils.translation import gettext as _ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import extend_schema_field @@ -46,6 +47,7 @@ from .constants import * from .models import * __all__ = ( + 'CableBundleFilterSet', 'CableFilterSet', 'CableTerminationFilterSet', 'CabledObjectFilterSet', @@ -86,6 +88,7 @@ __all__ = ( 'PowerPortFilterSet', 'PowerPortTemplateFilterSet', 'RackFilterSet', + 'RackGroupFilterSet', 'RackReservationFilterSet', 'RackRoleFilterSet', 'RackTypeFilterSet', @@ -313,6 +316,14 @@ class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, NestedGroupMode return queryset +@register_filterset +class RackGroupFilterSet(OrganizationalModelFilterSet): + + class Meta: + model = RackGroup + fields = ('id', 'name', 'slug', 'description') + + @register_filterset class RackRoleFilterSet(OrganizationalModelFilterSet): @@ -417,6 +428,18 @@ class RackFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterS to_field_name='slug', label=_('Location (slug)'), ) + group_id = django_filters.ModelMultipleChoiceFilter( + queryset=RackGroup.objects.all(), + distinct=False, + label=_('Group (ID)'), + ) + group = django_filters.ModelMultipleChoiceFilter( + field_name='group__slug', + queryset=RackGroup.objects.all(), + distinct=False, + to_field_name='slug', + label=_('Group (slug)'), + ) manufacturer_id = django_filters.ModelMultipleChoiceFilter( field_name='rack_type__manufacturer', queryset=Manufacturer.objects.all(), @@ -551,6 +574,19 @@ class RackReservationFilterSet(PrimaryModelFilterSet, TenancyFilterSet): to_field_name='slug', label=_('Location (slug)'), ) + group_id = django_filters.ModelMultipleChoiceFilter( + queryset=RackGroup.objects.all(), + field_name='rack__group', + distinct=False, + label=_('Group (ID)'), + ) + group = django_filters.ModelMultipleChoiceFilter( + field_name='rack__group__slug', + queryset=RackGroup.objects.all(), + distinct=False, + to_field_name='slug', + label=_('Group (slug)'), + ) status = django_filters.MultipleChoiceFilter( choices=RackReservationStatusChoices, distinct=False, @@ -572,11 +608,30 @@ class RackReservationFilterSet(PrimaryModelFilterSet, TenancyFilterSet): field_name='units', lookup_expr='contains' ) + unit_count_min = django_filters.NumberFilter( + field_name='unit_count', + lookup_expr='gte', + label=_('Minimum unit count'), + ) + unit_count_max = django_filters.NumberFilter( + field_name='unit_count', + lookup_expr='lte', + label=_('Maximum unit count'), + ) class Meta: model = RackReservation fields = ('id', 'created', 'description') + def filter_queryset(self, queryset): + # Annotate unit_count here so unit_count_min/unit_count_max filters can reference it. + # When called from the list view the queryset is already annotated; Django silently + # overwrites a duplicate annotation with the same expression, so this is safe. + queryset = queryset.annotate( + unit_count=Func('units', function='CARDINALITY', output_field=IntegerField()) + ) + return super().filter_queryset(queryset) + def search(self, queryset, name, value): if not value.strip(): return queryset @@ -995,7 +1050,7 @@ class ModuleBayTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeCo class Meta: model = ModuleBayTemplate - fields = ('id', 'name', 'label', 'position', 'description') + fields = ('id', 'name', 'label', 'position', 'enabled', 'description') @register_filterset @@ -1003,7 +1058,7 @@ class DeviceBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponent class Meta: model = DeviceBayTemplate - fields = ('id', 'name', 'label', 'description') + fields = ('id', 'name', 'label', 'enabled', 'description') @register_filterset @@ -2373,7 +2428,7 @@ class ModuleBayFilterSet(ModularDeviceComponentFilterSet): class Meta: model = ModuleBay - fields = ('id', 'name', 'label', 'position', 'description') + fields = ('id', 'name', 'label', 'position', 'enabled', 'description') @register_filterset @@ -2393,7 +2448,7 @@ class DeviceBayFilterSet(DeviceComponentFilterSet): class Meta: model = DeviceBay - fields = ('id', 'name', 'label', 'description') + fields = ('id', 'name', 'label', 'enabled', 'description') @register_filterset @@ -2546,6 +2601,23 @@ class VirtualChassisFilterSet(PrimaryModelFilterSet): return queryset.filter(qs_filter).distinct() +@register_filterset +class CableBundleFilterSet(PrimaryModelFilterSet): + + class Meta: + model = CableBundle + fields = ('id', 'name', 'description') + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(name__icontains=value) | + Q(description__icontains=value) | + Q(comments__icontains=value) + ) + + @register_filterset class CableFilterSet(TenancyFilterSet, PrimaryModelFilterSet): termination_a_type = MultiValueContentTypeFilter( @@ -2566,6 +2638,16 @@ class CableFilterSet(TenancyFilterSet, PrimaryModelFilterSet): method='_unterminated', label=_('Unterminated'), ) + bundle_id = django_filters.ModelMultipleChoiceFilter( + queryset=CableBundle.objects.all(), + label=_('Cable bundle (ID)'), + ) + bundle = django_filters.ModelMultipleChoiceFilter( + field_name='bundle__name', + queryset=CableBundle.objects.all(), + to_field_name='name', + label=_('Cable bundle (name)'), + ) type = django_filters.MultipleChoiceFilter( choices=CableTypeChoices, distinct=False, diff --git a/netbox/dcim/forms/bulk_create.py b/netbox/dcim/forms/bulk_create.py index a95a80f63..e5546e08f 100644 --- a/netbox/dcim/forms/bulk_create.py +++ b/netbox/dcim/forms/bulk_create.py @@ -3,9 +3,10 @@ from django.utils.translation import gettext_lazy as _ from dcim.models import * from extras.models import Tag -from netbox.forms.mixins import CustomFieldsMixin +from netbox.forms.mixins import ChangelogMessageMixin, CustomFieldsMixin from utilities.forms import form_from_model from utilities.forms.fields import DynamicModelMultipleChoiceField, ExpandableNameField +from utilities.forms.mixins import BackgroundJobMixin from .object_create import ComponentCreateForm @@ -27,7 +28,7 @@ __all__ = ( # Device components # -class DeviceBulkAddComponentForm(CustomFieldsMixin, ComponentCreateForm): +class DeviceBulkAddComponentForm(BackgroundJobMixin, ChangelogMessageMixin, CustomFieldsMixin, ComponentCreateForm): pk = forms.ModelMultipleChoiceField( queryset=Device.objects.all(), widget=forms.MultipleHiddenInput() @@ -108,10 +109,13 @@ class RearPortBulkCreateForm( field_order = ('name', 'label', 'type', 'positions', 'mark_connected', 'description', 'tags') -class ModuleBayBulkCreateForm(DeviceBulkAddComponentForm): +class ModuleBayBulkCreateForm( + form_from_model(ModuleBay, ['enabled']), + DeviceBulkAddComponentForm +): model = ModuleBay - field_order = ('name', 'label', 'position', 'description', 'tags') - replication_fields = ('name', 'label', 'position') + field_order = ('name', 'label', 'position', 'enabled', 'description', 'tags') + replication_fields = ('name', 'label', 'position', 'enabled') position = ExpandableNameField( label=_('Position'), required=False, @@ -119,9 +123,12 @@ class ModuleBayBulkCreateForm(DeviceBulkAddComponentForm): ) -class DeviceBayBulkCreateForm(DeviceBulkAddComponentForm): +class DeviceBayBulkCreateForm( + form_from_model(DeviceBay, ['enabled']), + DeviceBulkAddComponentForm +): model = DeviceBay - field_order = ('name', 'label', 'description', 'tags') + field_order = ('name', 'label', 'enabled', 'description', 'tags') class InventoryItemBulkCreateForm( diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py index 1282639fb..d9ef8600f 100644 --- a/netbox/dcim/forms/bulk_edit.py +++ b/netbox/dcim/forms/bulk_edit.py @@ -35,6 +35,7 @@ from wireless.models import WirelessLAN, WirelessLANGroup __all__ = ( 'CableBulkEditForm', + 'CableBundleBulkEditForm', 'ConsolePortBulkEditForm', 'ConsolePortTemplateBulkEditForm', 'ConsoleServerPortBulkEditForm', @@ -67,6 +68,7 @@ __all__ = ( 'PowerPortBulkEditForm', 'PowerPortTemplateBulkEditForm', 'RackBulkEditForm', + 'RackGroupBulkEditForm', 'RackReservationBulkEditForm', 'RackRoleBulkEditForm', 'RackTypeBulkEditForm', @@ -207,6 +209,14 @@ class LocationBulkEditForm(NestedGroupModelBulkEditForm): nullable_fields = ('parent', 'tenant', 'facility', 'description', 'comments') +class RackGroupBulkEditForm(OrganizationalModelBulkEditForm): + model = RackGroup + fieldsets = ( + FieldSet('description'), + ) + nullable_fields = ('description', 'comments') + + class RackRoleBulkEditForm(OrganizationalModelBulkEditForm): color = ColorField( label=_('Color'), @@ -342,6 +352,11 @@ class RackBulkEditForm(PrimaryModelBulkEditForm): 'site_id': '$site' } ) + group = DynamicModelChoiceField( + label=_('Group'), + queryset=RackGroup.objects.all(), + required=False + ) tenant = DynamicModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), @@ -441,14 +456,16 @@ class RackBulkEditForm(PrimaryModelBulkEditForm): model = Rack fieldsets = ( - FieldSet('status', 'role', 'tenant', 'serial', 'asset_tag', 'rack_type', 'description', name=_('Rack')), + FieldSet( + 'status', 'group', 'role', 'tenant', 'serial', 'asset_tag', 'rack_type', 'description', name=_('Rack') + ), FieldSet('region', 'site_group', 'site', 'location', name=_('Location')), FieldSet('outer_width', 'outer_height', 'outer_depth', 'outer_unit', name=_('Outer Dimensions')), FieldSet('form_factor', 'width', 'u_height', 'desc_units', 'airflow', 'mounting_depth', name=_('Hardware')), FieldSet('weight', 'max_weight', 'weight_unit', name=_('Weight')), ) nullable_fields = ( - 'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_height', 'outer_depth', + 'location', 'group', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', ) @@ -776,6 +793,24 @@ class ModuleBulkEditForm(PrimaryModelBulkEditForm): nullable_fields = ('serial', 'description', 'comments') +class CableBundleBulkEditForm(PrimaryModelBulkEditForm): + pk = forms.ModelMultipleChoiceField( + queryset=CableBundle.objects.all(), + widget=forms.MultipleHiddenInput + ) + description = forms.CharField( + label=_('Description'), + max_length=200, + required=False, + ) + + model = CableBundle + fieldsets = ( + FieldSet('description',), + ) + nullable_fields = ('description', 'comments') + + class CableBulkEditForm(PrimaryModelBulkEditForm): type = forms.ChoiceField( label=_('Type'), @@ -800,6 +835,11 @@ class CableBulkEditForm(PrimaryModelBulkEditForm): queryset=Tenant.objects.all(), required=False ) + bundle = DynamicModelChoiceField( + label=_('Bundle'), + queryset=CableBundle.objects.all(), + required=False, + ) label = forms.CharField( label=_('Label'), max_length=100, @@ -823,11 +863,11 @@ class CableBulkEditForm(PrimaryModelBulkEditForm): model = Cable fieldsets = ( - FieldSet('type', 'status', 'profile', 'tenant', 'label', 'description'), + FieldSet('type', 'status', 'profile', 'tenant', 'bundle', 'label', 'description'), FieldSet('color', 'length', 'length_unit', name=_('Attributes')), ) nullable_fields = ( - 'type', 'status', 'profile', 'tenant', 'label', 'color', 'length', 'description', 'comments', + 'type', 'status', 'profile', 'tenant', 'bundle', 'label', 'color', 'length', 'description', 'comments', ) @@ -1211,6 +1251,11 @@ class ModuleBayTemplateBulkEditForm(ComponentTemplateBulkEditForm): label=_('Description'), required=False ) + enabled = forms.NullBooleanField( + label=_('Enabled'), + required=False, + widget=BulkEditNullBooleanSelect, + ) nullable_fields = ('label', 'position', 'description') @@ -1229,6 +1274,11 @@ class DeviceBayTemplateBulkEditForm(ComponentTemplateBulkEditForm): label=_('Description'), required=False ) + enabled = forms.NullBooleanField( + label=_('Enabled'), + required=False, + widget=BulkEditNullBooleanSelect, + ) nullable_fields = ('label', 'description') @@ -1653,23 +1703,23 @@ class RearPortBulkEditForm( class ModuleBayBulkEditForm( - form_from_model(ModuleBay, ['label', 'position', 'description']), + form_from_model(ModuleBay, ['label', 'position', 'enabled', 'description']), NetBoxModelBulkEditForm ): model = ModuleBay fieldsets = ( - FieldSet('label', 'position', 'description'), + FieldSet('label', 'position', 'enabled', 'description'), ) nullable_fields = ('label', 'position', 'description') class DeviceBayBulkEditForm( - form_from_model(DeviceBay, ['label', 'description']), + form_from_model(DeviceBay, ['label', 'enabled', 'description']), NetBoxModelBulkEditForm ): model = DeviceBay fieldsets = ( - FieldSet('label', 'description'), + FieldSet('label', 'enabled', 'description'), ) nullable_fields = ('label', 'description') diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index 1cfc58983..6f3197919 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -34,6 +34,7 @@ from wireless.choices import WirelessRoleChoices from .common import ModuleCommonForm __all__ = ( + 'CableBundleImportForm', 'CableImportForm', 'ConsolePortImportForm', 'ConsoleServerPortImportForm', @@ -57,6 +58,7 @@ __all__ = ( 'PowerOutletImportForm', 'PowerPanelImportForm', 'PowerPortImportForm', + 'RackGroupImportForm', 'RackImportForm', 'RackReservationImportForm', 'RackRoleImportForm', @@ -187,6 +189,13 @@ class LocationImportForm(NestedGroupModelImportForm): self.fields['parent'].queryset = self.fields['parent'].queryset.filter(**params) +class RackGroupImportForm(OrganizationalModelImportForm): + + class Meta: + model = RackGroup + fields = ('name', 'slug', 'description', 'owner', 'comments', 'tags') + + class RackRoleImportForm(OrganizationalModelImportForm): class Meta: @@ -261,6 +270,13 @@ class RackImportForm(PrimaryModelImportForm): to_field_name='name', help_text=_('Name of assigned tenant') ) + group = CSVModelChoiceField( + label=_('Rack group'), + queryset=RackGroup.objects.all(), + required=False, + to_field_name='name', + help_text=_('Name of assigned group') + ) status = CSVChoiceField( label=_('Status'), choices=RackStatusChoices, @@ -318,10 +334,10 @@ class RackImportForm(PrimaryModelImportForm): class Meta: model = Rack fields = ( - 'site', 'location', 'name', 'facility_id', 'tenant', 'status', 'role', 'rack_type', 'form_factor', 'serial', - 'asset_tag', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', - 'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', 'description', 'owner', 'comments', - 'tags', + 'site', 'location', 'group', 'name', 'facility_id', 'tenant', 'status', 'role', 'rack_type', 'form_factor', + 'serial', 'asset_tag', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', + 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', 'description', 'owner', + 'comments', 'tags', ) def __init__(self, data=None, *args, **kwargs): @@ -1138,7 +1154,13 @@ class ModuleBayImportForm(OwnerCSVMixin, NetBoxModelImportForm): class Meta: model = ModuleBay - fields = ('device', 'name', 'label', 'position', 'description', 'owner', 'tags') + fields = ('device', 'name', 'label', 'position', 'enabled', 'description', 'owner', 'tags') + + def clean_enabled(self): + # Make sure enabled is True when it's not included in the uploaded data + if 'enabled' not in self.data: + return True + return self.cleaned_data['enabled'] class DeviceBayImportForm(OwnerCSVMixin, NetBoxModelImportForm): @@ -1160,7 +1182,7 @@ class DeviceBayImportForm(OwnerCSVMixin, NetBoxModelImportForm): class Meta: model = DeviceBay - fields = ('device', 'name', 'label', 'installed_device', 'description', 'owner', 'tags') + fields = ('device', 'name', 'label', 'enabled', 'installed_device', 'description', 'owner', 'tags') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -1188,6 +1210,12 @@ class DeviceBayImportForm(OwnerCSVMixin, NetBoxModelImportForm): else: self.fields['installed_device'].queryset = Device.objects.none() + def clean_enabled(self): + # Make sure enabled is True when it's not included in the uploaded data + if 'enabled' not in self.data: + return True + return self.cleaned_data['enabled'] + class InventoryItemImportForm(OwnerCSVMixin, NetBoxModelImportForm): device = CSVModelChoiceField( @@ -1397,6 +1425,12 @@ class MACAddressImportForm(PrimaryModelImportForm): # Cables # +class CableBundleImportForm(PrimaryModelImportForm): + class Meta: + model = CableBundle + fields = ('name', 'description', 'owner', 'comments', 'tags') + + class CableImportForm(PrimaryModelImportForm): # Termination A side_a_site = CSVModelChoiceField( @@ -1490,6 +1524,13 @@ class CableImportForm(PrimaryModelImportForm): to_field_name='name', help_text=_('Assigned tenant') ) + bundle = CSVModelChoiceField( + label=_('Bundle'), + queryset=CableBundle.objects.all(), + required=False, + to_field_name='name', + help_text=_('Cable bundle name'), + ) length_unit = CSVChoiceField( label=_('Length unit'), choices=CableLengthUnitChoices, @@ -1508,7 +1549,7 @@ class CableImportForm(PrimaryModelImportForm): fields = [ 'side_a_site', 'side_a_device', 'side_a_power_panel', 'side_a_type', 'side_a_name', 'side_b_site', 'side_b_device', 'side_b_power_panel', 'side_b_type', 'side_b_name', - 'type', 'status', 'profile', 'tenant', 'label', 'color', 'length', 'length_unit', + 'type', 'status', 'profile', 'tenant', 'bundle', 'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags', ] diff --git a/netbox/dcim/forms/common.py b/netbox/dcim/forms/common.py index 162fd6213..f3df113b4 100644 --- a/netbox/dcim/forms/common.py +++ b/netbox/dcim/forms/common.py @@ -113,7 +113,6 @@ class ModuleCommonForm(forms.Form): raise forms.ValidationError( _("Cannot install module with placeholder values in a module bay with no position defined.") ) - try: resolved_name = resolve_module_placeholder(template.name, positions) except ValueError as e: diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 82ee655b8..9759a5df4 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -27,6 +27,7 @@ from vpn.models import L2VPN from wireless.choices import * __all__ = ( + 'CableBundleFilterForm', 'CableFilterForm', 'ConsoleConnectionFilterForm', 'ConsolePortFilterForm', @@ -64,6 +65,7 @@ __all__ = ( 'PowerPortTemplateFilterForm', 'RackElevationFilterForm', 'RackFilterForm', + 'RackGroupFilterForm', 'RackReservationFilterForm', 'RackRoleFilterForm', 'RackTypeFilterForm', @@ -276,6 +278,15 @@ class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, NestedGroupM tag = TagFilterField(model) +class RackGroupFilterForm(OrganizationalModelFilterSetForm): + model = RackGroup + fieldsets = ( + FieldSet('q', 'filter_id', 'tag'), + FieldSet('owner_group_id', 'owner_id', name=_('Ownership')), + ) + tag = TagFilterField(model) + + class RackRoleFilterForm(OrganizationalModelFilterSetForm): model = RackRole fieldsets = ( @@ -355,7 +366,7 @@ class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, RackBaseFilterFo model = Rack fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Location')), + FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'group_id', name=_('Location')), FieldSet('status', 'role_id', 'manufacturer_id', 'rack_type_id', 'serial', 'asset_tag', name=_('Rack')), FieldSet('form_factor', 'width', 'u_height', 'airflow', name=_('Hardware')), FieldSet('starting_unit', 'desc_units', name=_('Numbering')), @@ -392,6 +403,12 @@ class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, RackBaseFilterFo }, label=_('Location') ) + group_id = DynamicModelMultipleChoiceField( + queryset=RackGroup.objects.all(), + required=False, + null_option='None', + label=_('Rack group') + ) status = forms.MultipleChoiceField( label=_('Status'), choices=RackStatusChoices, @@ -435,7 +452,7 @@ class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, RackBaseFilterFo class RackElevationFilterForm(RackFilterForm): fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'id', name=_('Location')), + FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'group_id', 'id', name=_('Location')), FieldSet('status', 'role_id', name=_('Function')), FieldSet('type', 'width', 'serial', 'asset_tag', name=_('Hardware')), FieldSet('weight', 'max_weight', 'weight_unit', name=_('Weight')), @@ -458,8 +475,8 @@ class RackReservationFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm): model = RackReservation fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('status', 'user_id', name=_('Reservation')), - FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Rack')), + FieldSet('status', 'user_id', 'unit_count_min', 'unit_count_max', name=_('Reservation')), + FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'group_id', 'rack_id', name=_('Rack')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), FieldSet('owner_group_id', 'owner_id', name=_('Ownership')), ) @@ -491,10 +508,17 @@ class RackReservationFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm): label=_('Location'), null_option='None' ) + group_id = DynamicModelMultipleChoiceField( + queryset=RackGroup.objects.all(), + required=False, + null_option='None', + label=_('Rack group') + ) rack_id = DynamicModelMultipleChoiceField( queryset=Rack.objects.all(), required=False, query_params={ + 'group_id': '$group_id', 'site_id': '$site_id', 'location_id': '$location_id', }, @@ -510,6 +534,14 @@ class RackReservationFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm): required=False, label=_('User') ) + unit_count_min = forms.IntegerField( + required=False, + label=_("Minimum U's") + ) + unit_count_max = forms.IntegerField( + required=False, + label=_("Maximum U's") + ) tag = TagFilterField(model) @@ -1161,12 +1193,24 @@ class VirtualChassisFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm): tag = TagFilterField(model) +class CableBundleFilterForm(PrimaryModelFilterSetForm): + model = CableBundle + fieldsets = ( + FieldSet('q', 'filter_id', 'tag'), + FieldSet('name', name=_('Attributes')), + ) + tag = TagFilterField(model) + + class CableFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm): model = Cable fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('site_id', 'location_id', 'rack_id', 'device_id', name=_('Location')), - FieldSet('type', 'status', 'profile', 'color', 'length', 'length_unit', 'unterminated', name=_('Attributes')), + FieldSet( + 'type', 'status', 'profile', 'color', 'length', 'length_unit', 'unterminated', 'bundle_id', + name=_('Attributes'), + ), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), FieldSet('owner_group_id', 'owner_id', name=_('Ownership')), ) @@ -1248,6 +1292,11 @@ class CableFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm): choices=BOOLEAN_WITH_BLANK_CHOICES ) ) + bundle_id = DynamicModelMultipleChoiceField( + queryset=CableBundle.objects.all(), + required=False, + label=_('Bundle'), + ) tag = TagFilterField(model) @@ -1841,7 +1890,7 @@ class ModuleBayFilterForm(DeviceComponentFilterForm): model = ModuleBay fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('name', 'label', 'position', name=_('Attributes')), + FieldSet('name', 'label', 'position', 'enabled', name=_('Attributes')), FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')), FieldSet( 'tenant_id', 'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id', @@ -1849,31 +1898,41 @@ class ModuleBayFilterForm(DeviceComponentFilterForm): ), FieldSet('owner_group_id', 'owner_id', name=_('Ownership')), ) - tag = TagFilterField(model) position = forms.CharField( label=_('Position'), required=False ) + enabled = forms.NullBooleanField( + label=_('Enabled'), + required=False, + widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES), + ) + tag = TagFilterField(model) class ModuleBayTemplateFilterForm(ModularDeviceComponentTemplateFilterForm): model = ModuleBayTemplate fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('name', 'label', 'position', name=_('Attributes')), + FieldSet('name', 'label', 'position', 'enabled', name=_('Attributes')), FieldSet('device_type_id', 'module_type_id', name=_('Device')), ) position = forms.CharField( label=_('Position'), required=False, ) + enabled = forms.NullBooleanField( + label=_('Enabled'), + required=False, + widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES), + ) class DeviceBayFilterForm(DeviceComponentFilterForm): model = DeviceBay fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('name', 'label', name=_('Attributes')), + FieldSet('name', 'label', 'enabled', name=_('Attributes')), FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')), FieldSet( 'tenant_id', 'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id', @@ -1881,6 +1940,11 @@ class DeviceBayFilterForm(DeviceComponentFilterForm): ), FieldSet('owner_group_id', 'owner_id', name=_('Ownership')), ) + enabled = forms.NullBooleanField( + label=_('Enabled'), + required=False, + widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES), + ) tag = TagFilterField(model) @@ -1888,9 +1952,14 @@ class DeviceBayTemplateFilterForm(DeviceComponentTemplateFilterForm): model = DeviceBayTemplate fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('name', 'label', name=_('Attributes')), + FieldSet('name', 'label', 'enabled', name=_('Attributes')), FieldSet('device_type_id', name=_('Device')), ) + enabled = forms.NullBooleanField( + label=_('Enabled'), + required=False, + widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES), + ) class InventoryItemFilterForm(DeviceComponentFilterForm): diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 9f3a6de63..9672cc54b 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -39,6 +39,7 @@ from wireless.models import WirelessLAN, WirelessLANGroup from .common import InterfaceCommonForm, ModuleCommonForm __all__ = ( + 'CableBundleForm', 'CableForm', 'ConsolePortForm', 'ConsolePortTemplateForm', @@ -74,6 +75,7 @@ __all__ = ( 'PowerPortForm', 'PowerPortTemplateForm', 'RackForm', + 'RackGroupForm', 'RackReservationForm', 'RackRoleForm', 'RackTypeForm', @@ -232,6 +234,18 @@ class LocationForm(TenancyForm, NestedGroupModelForm): ) +class RackGroupForm(OrganizationalModelForm): + fieldsets = ( + FieldSet('name', 'slug', 'description', 'tags', name=_('Rack Group')), + ) + + class Meta: + model = RackGroup + fields = [ + 'name', 'slug', 'description', 'owner', 'comments', 'tags', + ] + + class RackRoleForm(OrganizationalModelForm): fieldsets = ( FieldSet('name', 'slug', 'color', 'description', 'tags', name=_('Rack Role')), @@ -289,6 +303,11 @@ class RackForm(TenancyForm, PrimaryModelForm): 'site_id': '$site' } ) + group = DynamicModelChoiceField( + label=_('Rack Group'), + queryset=RackGroup.objects.all(), + required=False + ) role = DynamicModelChoiceField( label=_('Role'), queryset=RackRole.objects.all(), @@ -304,7 +323,7 @@ class RackForm(TenancyForm, PrimaryModelForm): fieldsets = ( FieldSet( - 'site', 'location', 'name', 'status', 'role', 'rack_type', 'description', 'airflow', 'tags', + 'site', 'location', 'group', 'name', 'status', 'role', 'rack_type', 'description', 'airflow', 'tags', name=_('Rack') ), FieldSet('facility_id', 'serial', 'asset_tag', name=_('Inventory Control')), @@ -314,7 +333,7 @@ class RackForm(TenancyForm, PrimaryModelForm): class Meta: model = Rack fields = [ - 'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial', + 'site', 'location', 'group', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial', 'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', 'description', 'owner', 'comments', 'tags', @@ -784,7 +803,7 @@ class ModuleForm(ModuleCommonForm, PrimaryModelForm): 'device_id': '$device', }, context={ - 'disabled': 'installed_module', + 'disabled': '_occupied', }, ) module_type = DynamicModelChoiceField( @@ -838,6 +857,17 @@ def get_termination_type_choices(): ]) +class CableBundleForm(PrimaryModelForm): + + fieldsets = ( + FieldSet('name', 'description', 'tags', name=_('Cable Bundle')), + ) + + class Meta: + model = CableBundle + fields = ['name', 'description', 'owner', 'comments', 'tags'] + + class CableForm(TenancyForm, PrimaryModelForm): a_terminations_type = forms.ChoiceField( choices=get_termination_type_choices, @@ -851,12 +881,17 @@ class CableForm(TenancyForm, PrimaryModelForm): widget=HTMXSelect(), label=_('Type') ) + bundle = DynamicModelChoiceField( + queryset=CableBundle.objects.all(), + required=False, + label=_('Bundle'), + ) class Meta: model = Cable fields = [ 'a_terminations_type', 'b_terminations_type', 'type', 'status', 'profile', 'tenant_group', 'tenant', - 'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags', + 'bundle', 'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags', ] @@ -1063,7 +1098,9 @@ class ModularComponentTemplateForm(ComponentTemplateForm): self.fields['name'].help_text = _( "Alphanumeric ranges are supported for bulk creation. Mixed cases and types within a single range are not " "supported (example: [ge,xe]-0/0/[0-9]). The token {module}, if present, will be " - "automatically replaced with the position value when creating a new module." + "automatically replaced with the position value when creating a new module. " + "The token {vc_position} will be replaced with the device's Virtual Chassis position " + "(use {vc_position:1} to specify a fallback (default is 0))" ) @@ -1224,26 +1261,26 @@ class ModuleBayTemplateForm(ModularComponentTemplateForm): FieldSet('device_type', name=_('Device Type')), FieldSet('module_type', name=_('Module Type')), ), - 'name', 'label', 'position', 'description', + 'name', 'label', 'position', 'enabled', 'description', ), ) class Meta: model = ModuleBayTemplate fields = [ - 'device_type', 'module_type', 'name', 'label', 'position', 'description', + 'device_type', 'module_type', 'name', 'label', 'position', 'enabled', 'description', ] class DeviceBayTemplateForm(ComponentTemplateForm): fieldsets = ( - FieldSet('device_type', 'name', 'label', 'description'), + FieldSet('device_type', 'name', 'label', 'enabled', 'description'), ) class Meta: model = DeviceBayTemplate fields = [ - 'device_type', 'name', 'label', 'description', + 'device_type', 'name', 'label', 'enabled', 'description', ] @@ -1689,25 +1726,25 @@ class RearPortForm(ModularDeviceComponentForm): class ModuleBayForm(ModularDeviceComponentForm): fieldsets = ( - FieldSet('device', 'module', 'name', 'label', 'position', 'description', 'tags',), + FieldSet('device', 'module', 'name', 'label', 'position', 'enabled', 'description', 'tags',), ) class Meta: model = ModuleBay fields = [ - 'device', 'module', 'name', 'label', 'position', 'description', 'owner', 'tags', + 'device', 'module', 'name', 'label', 'position', 'enabled', 'description', 'owner', 'tags', ] class DeviceBayForm(DeviceComponentForm): fieldsets = ( - FieldSet('device', 'name', 'label', 'description', 'tags',), + FieldSet('device', 'name', 'label', 'enabled', 'description', 'tags',), ) class Meta: model = DeviceBay fields = [ - 'device', 'name', 'label', 'description', 'owner', 'tags', + 'device', 'name', 'label', 'enabled', 'description', 'owner', 'tags', ] diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py index c5aa04236..02fb3ef64 100644 --- a/netbox/dcim/graphql/filters.py +++ b/netbox/dcim/graphql/filters.py @@ -63,6 +63,7 @@ if TYPE_CHECKING: from .enums import * __all__ = ( + 'CableBundleFilter', 'CableFilter', 'CableTerminationFilter', 'ConsolePortFilter', @@ -99,6 +100,7 @@ __all__ = ( 'PowerPortFilter', 'PowerPortTemplateFilter', 'RackFilter', + 'RackGroupFilter', 'RackReservationFilter', 'RackRoleFilter', 'RackTypeFilter', @@ -112,6 +114,11 @@ __all__ = ( ) +@strawberry_django.filter_type(models.CableBundle, lookups=True) +class CableBundleFilter(PrimaryModelFilter): + name: StrFilterLookup[str] | None = strawberry_django.filter_field() + + @strawberry_django.filter_type(models.Cable, lookups=True) class CableFilter(TenancyFilterMixin, PrimaryModelFilter): type: BaseFilterLookup[Annotated['CableTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = ( @@ -317,6 +324,7 @@ class DeviceFilter( @strawberry_django.filter_type(models.DeviceBay, lookups=True) class DeviceBayFilter(ComponentModelFilterMixin, NetBoxModelFilter): + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() installed_device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -325,7 +333,7 @@ class DeviceBayFilter(ComponentModelFilterMixin, NetBoxModelFilter): @strawberry_django.filter_type(models.DeviceBayTemplate, lookups=True) class DeviceBayTemplateFilter(ComponentTemplateFilterMixin, ChangeLoggedModelFilter): - pass + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.InventoryItemTemplate, lookups=True) @@ -741,11 +749,13 @@ class ModuleBayFilter(ModularComponentFilterMixin, NetBoxModelFilter): ) parent_id: ID | None = strawberry_django.filter_field() position: StrFilterLookup[str] | None = strawberry_django.filter_field() + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.ModuleBayTemplate, lookups=True) class ModuleBayTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter): position: StrFilterLookup[str] | None = strawberry_django.filter_field() + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.ModuleTypeProfile, lookups=True) @@ -962,6 +972,10 @@ class RackFilter( location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) + group: Annotated['RackGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() status: BaseFilterLookup[Annotated['RackStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = ( strawberry_django.filter_field() ) @@ -977,6 +991,11 @@ class RackFilter( ) +@strawberry_django.filter_type(models.RackGroup, lookups=True) +class RackGroupFilter(OrganizationalModelFilter): + pass + + @strawberry_django.filter_type(models.RackReservation, lookups=True) class RackReservationFilter(TenancyFilterMixin, PrimaryModelFilter): rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() @@ -984,6 +1003,7 @@ class RackReservationFilter(TenancyFilterMixin, PrimaryModelFilter): units: Annotated['IntegerArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) + unit_count: ComparisonFilterLookup[int] | None = strawberry_django.filter_field() user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() user_id: ID | None = strawberry_django.filter_field() description: StrFilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/dcim/graphql/schema.py b/netbox/dcim/graphql/schema.py index fcf1e828f..710254120 100644 --- a/netbox/dcim/graphql/schema.py +++ b/netbox/dcim/graphql/schema.py @@ -9,6 +9,9 @@ class DCIMQuery: cable: CableType = strawberry_django.field() cable_list: list[CableType] = strawberry_django.field() + cable_bundle: CableBundleType = strawberry_django.field() + cable_bundle_list: list[CableBundleType] = strawberry_django.field() + console_port: ConsolePortType = strawberry_django.field() console_port_list: list[ConsolePortType] = strawberry_django.field() @@ -102,6 +105,9 @@ class DCIMQuery: power_port_template: PowerPortTemplateType = strawberry_django.field() power_port_template_list: list[PowerPortTemplateType] = strawberry_django.field() + rack_group: RackGroupType = strawberry_django.field() + rack_group_list: list[RackGroupType] = strawberry_django.field() + rack_type: RackTypeType = strawberry_django.field() rack_type_list: list[RackTypeType] = strawberry_django.field() diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index 2a6c3750a..6cbab28f2 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Annotated import strawberry import strawberry_django +from django.db.models import Func, IntegerField from core.graphql.mixins import ChangelogMixin from dcim import models @@ -39,6 +40,7 @@ if TYPE_CHECKING: from wireless.graphql.types import WirelessLANType, WirelessLinkType __all__ = ( + 'CableBundleType', 'CableType', 'ComponentType', 'ConsolePortTemplateType', @@ -73,6 +75,7 @@ __all__ = ( 'PowerPanelType', 'PowerPortTemplateType', 'PowerPortType', + 'RackGroupType', 'RackReservationType', 'RackRoleType', 'RackType', @@ -126,6 +129,16 @@ class ModularComponentTemplateType(ComponentTemplateType): # +@strawberry_django.type( + models.CableBundle, + fields='__all__', + filters=CableBundleFilter, + pagination=True +) +class CableBundleType(PrimaryObjectType): + cables: list[Annotated['CableType', strawberry.lazy('dcim.graphql.types')]] + + @strawberry_django.type( models.CableTermination, exclude=['termination_type', 'termination_id', '_device', '_rack', '_location', '_site'], @@ -157,6 +170,7 @@ class CableTerminationType(NetBoxObjectType): class CableType(PrimaryObjectType): color: str tenant: Annotated['TenantType', strawberry.lazy('tenancy.graphql.types')] | None + bundle: Annotated['CableBundleType', strawberry.lazy('dcim.graphql.types')] | None terminations: list[CableTerminationType] @@ -737,6 +751,17 @@ class PowerPortTemplateType(ModularComponentTemplateType): poweroutlet_templates: list[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]] +@strawberry_django.type( + models.RackGroup, + fields='__all__', + filters=RackGroupFilter, + pagination=True +) +class RackGroupType(OrganizationalObjectType): + + racks: list[Annotated["RackType", strawberry.lazy('dcim.graphql.types')]] + + @strawberry_django.type( models.RackType, fields='__all__', @@ -757,6 +782,7 @@ class RackTypeType(ImageAttachmentsMixin, PrimaryObjectType): class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObjectType): site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] location: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None + group: Annotated["RackGroupType", strawberry.lazy('dcim.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None role: Annotated["RackRoleType", strawberry.lazy('dcim.graphql.types')] | None @@ -779,6 +805,17 @@ class RackReservationType(PrimaryObjectType): tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None user: Annotated["UserType", strawberry.lazy('users.graphql.types')] + @classmethod + def get_queryset(cls, queryset, info, **kwargs): + queryset = super().get_queryset(queryset, info, **kwargs) + return queryset.annotate( + unit_count=Func('units', function='CARDINALITY', output_field=IntegerField()) + ) + + @strawberry.field + def unit_count(self) -> int: + return len(self.units) + @strawberry_django.type( models.RackRole, diff --git a/netbox/dcim/migrations/0206_load_module_type_profiles.py b/netbox/dcim/migrations/0206_load_module_type_profiles.py index 87661a8ac..1b9873faf 100644 --- a/netbox/dcim/migrations/0206_load_module_type_profiles.py +++ b/netbox/dcim/migrations/0206_load_module_type_profiles.py @@ -22,17 +22,21 @@ def load_initial_data(apps, schema_editor): 'power_supply', 'expansion_card' ) + profile_objects = [] for name in initial_profiles: file_path = DATA_FILES_PATH / f'{name}.json' with file_path.open('r') as f: data = json.load(f) try: - ModuleTypeProfile.objects.using(db_alias).create(**data) + profile = ModuleTypeProfile(**data) + profile_objects.append(profile) except Exception as e: print(f"Error loading data from {file_path}") raise e + ModuleTypeProfile.objects.using(db_alias).bulk_create(profile_objects) + class Migration(migrations.Migration): diff --git a/netbox/dcim/migrations/0228_rack_group.py b/netbox/dcim/migrations/0228_rack_group.py new file mode 100644 index 000000000..27ffd97d7 --- /dev/null +++ b/netbox/dcim/migrations/0228_rack_group.py @@ -0,0 +1,109 @@ +import django.db.models.deletion +import taggit.managers +from django.db import migrations, models + +import netbox.models.deletion +import utilities.json + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0227_alter_interface_speed_bigint'), + ('extras', '0134_owner'), + ('users', '0015_owner'), + ] + + operations = [ + # Rename legacy database objects left over from when the RackGroup model was renamed + # to Location (v2.11) and Rack.group was renamed to Rack.location. Old installations + # retained the original names, which conflict with the new dcim_rackgroup table and + # dcim_rack.group_id column created by this migration. No-op on fresh installs. + migrations.RunSQL( + sql=[ + "ALTER INDEX IF EXISTS dcim_rackgroup_pkey RENAME TO dcim_location_pkey", + "ALTER INDEX IF EXISTS dcim_rackgroup_parent_id_cc315105 RENAME TO dcim_location_parent_id_d77f3318", + "ALTER INDEX IF EXISTS dcim_rackgroup_site_id_13520e89 RENAME TO dcim_location_site_id_b55e975f", + "ALTER INDEX IF EXISTS dcim_rackgroup_slug_3f4582a7 RENAME TO dcim_location_slug_352c5472", + "ALTER INDEX IF EXISTS dcim_rackgroup_slug_3f4582a7_like RENAME TO dcim_location_slug_352c5472_like", + "ALTER INDEX IF EXISTS dcim_rackgroup_tree_id_9c2ad6f4 RENAME TO dcim_location_tree_id_5089ef14", + "ALTER SEQUENCE IF EXISTS dcim_rackgroup_id_seq RENAME TO dcim_location_id_seq", + # Rename the legacy index on dcim_rack from when Rack.group was renamed to + # Rack.location. The column was renamed but the index was not, so it still + # carries the old "group_id" name while indexing location_id. Its name + # collides with the new index Django creates for the new Rack.group FK. + "ALTER INDEX IF EXISTS dcim_rack_group_id_44e90ea9 RENAME TO dcim_rack_location_id_5f63ec31", + ], + reverse_sql=migrations.RunSQL.noop, + ), + # PostgreSQL does not support IF EXISTS on RENAME CONSTRAINT, so use a DO block. + # Target names match what a fresh v4.5.9 install produces (Django generates the FK + # constraint name as ___fk__, where the hash is + # md5(table + col)[:8] computed against the new dcim_location table name). + migrations.RunSQL( + sql=""" + DO $$ + DECLARE + r RECORD; + BEGIN + FOR r IN ( + SELECT old_name, new_name FROM (VALUES + ('dcim_rackgroup_level_check', 'dcim_location_level_check'), + ('dcim_rackgroup_lft_check', 'dcim_location_lft_check'), + ('dcim_rackgroup_rght_check', 'dcim_location_rght_check'), + ('dcim_rackgroup_tree_id_check', 'dcim_location_tree_id_check'), + ('dcim_rackgroup_site_id_13520e89_fk', + 'dcim_location_site_id_b55e975f_fk_dcim_site_id') + ) AS m(old_name, new_name) + WHERE EXISTS ( + SELECT 1 FROM pg_constraint + WHERE conrelid = to_regclass('dcim_location') AND conname = m.old_name + ) + ) LOOP + EXECUTE format('ALTER TABLE dcim_location RENAME CONSTRAINT %I TO %I', + r.old_name, r.new_name); + END LOOP; + END $$; + """, + reverse_sql=migrations.RunSQL.noop, + ), + migrations.CreateModel( + name='RackGroup', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), + ('name', models.CharField(max_length=100, unique=True)), + ('slug', models.SlugField(max_length=100, unique=True)), + ('description', models.CharField(blank=True, max_length=200)), + ('comments', models.TextField(blank=True)), + ( + 'owner', + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner' + ), + ), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'verbose_name': 'rack group', + 'verbose_name_plural': 'rack groups', + 'ordering': ('name',), + }, + bases=(netbox.models.deletion.DeleteMixin, models.Model), + ), + migrations.AddField( + model_name='rack', + name='group', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='racks', + to='dcim.rackgroup', + ), + ), + ] diff --git a/netbox/dcim/migrations/0229_cable_bundle.py b/netbox/dcim/migrations/0229_cable_bundle.py new file mode 100644 index 000000000..b2c555005 --- /dev/null +++ b/netbox/dcim/migrations/0229_cable_bundle.py @@ -0,0 +1,54 @@ +import django.db.models.deletion +import taggit.managers +from django.db import migrations, models + +import netbox.models.deletion +import utilities.json + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0228_rack_group'), + ('extras', '0134_owner'), + ('users', '0015_owner'), + ] + + operations = [ + migrations.CreateModel( + name='CableBundle', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('custom_field_data', models.JSONField( + blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder) + ), + ('description', models.CharField(blank=True, max_length=200)), + ('comments', models.TextField(blank=True)), + ('name', models.CharField(max_length=100, unique=True)), + ('owner', models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner') + ), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'verbose_name': 'cable bundle', + 'verbose_name_plural': 'cable bundles', + 'ordering': ('name',), + }, + bases=(netbox.models.deletion.DeleteMixin, models.Model), + ), + migrations.AddField( + model_name='cable', + name='bundle', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='cables', + to='dcim.cablebundle', + verbose_name='bundle', + ), + ), + ] diff --git a/netbox/dcim/migrations/0230_devicebay_modulebay_enabled.py b/netbox/dcim/migrations/0230_devicebay_modulebay_enabled.py new file mode 100644 index 000000000..494b5d999 --- /dev/null +++ b/netbox/dcim/migrations/0230_devicebay_modulebay_enabled.py @@ -0,0 +1,30 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0229_cable_bundle'), + ] + + operations = [ + migrations.AddField( + model_name='devicebay', + name='enabled', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='devicebaytemplate', + name='enabled', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='modulebay', + name='enabled', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='modulebaytemplate', + name='enabled', + field=models.BooleanField(default=True), + ), + ] diff --git a/netbox/dcim/migrations/0231_interface_rf_channel_frequency_precision.py b/netbox/dcim/migrations/0231_interface_rf_channel_frequency_precision.py new file mode 100644 index 000000000..1d7574961 --- /dev/null +++ b/netbox/dcim/migrations/0231_interface_rf_channel_frequency_precision.py @@ -0,0 +1,23 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0230_devicebay_modulebay_enabled'), + ] + + operations = [ + migrations.AlterField( + model_name='interface', + name='rf_channel_frequency', + field=models.DecimalField( + blank=True, + decimal_places=3, + help_text='Populated by selected channel (if set)', + max_digits=8, + null=True, + verbose_name='channel frequency (MHz)', + ), + ), + ] diff --git a/netbox/dcim/migrations/0232_default_ordering_indexes.py b/netbox/dcim/migrations/0232_default_ordering_indexes.py new file mode 100644 index 000000000..da70f6e67 --- /dev/null +++ b/netbox/dcim/migrations/0232_default_ordering_indexes.py @@ -0,0 +1,78 @@ +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('dcim', '0231_interface_rf_channel_frequency_precision'), + ('extras', '0136_customfield_validation_schema'), + ('ipam', '0088_rename_vlangroup_total_vlan_ids'), + ('tenancy', '0023_add_mptt_tree_indexes'), + ('users', '0015_owner'), + ('virtualization', '0054_virtualmachinetype'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddIndex( + model_name='consoleporttemplate', + index=models.Index(fields=['device_type', 'module_type', 'name'], name='dcim_consol_device__101ed5_idx'), + ), + migrations.AddIndex( + model_name='consoleserverporttemplate', + index=models.Index(fields=['device_type', 'module_type', 'name'], name='dcim_consol_device__a901e6_idx'), + ), + migrations.AddIndex( + model_name='device', + index=models.Index(fields=['name', 'id'], name='dcim_device_name_c27913_idx'), + ), + migrations.AddIndex( + model_name='frontporttemplate', + index=models.Index(fields=['device_type', 'module_type', 'name'], name='dcim_frontp_device__ec2ffb_idx'), + ), + migrations.AddIndex( + model_name='interfacetemplate', + index=models.Index(fields=['device_type', 'module_type', 'name'], name='dcim_interf_device__601012_idx'), + ), + migrations.AddIndex( + model_name='macaddress', + index=models.Index(fields=['mac_address', 'id'], name='dcim_macadd_mac_add_f2662a_idx'), + ), + migrations.AddIndex( + model_name='modulebaytemplate', + index=models.Index(fields=['device_type', 'module_type', 'name'], name='dcim_module_device__9eabad_idx'), + ), + migrations.AddIndex( + model_name='moduletype', + index=models.Index(fields=['profile', 'manufacturer', 'model'], name='dcim_module_profile_868277_idx'), + ), + migrations.AddIndex( + model_name='poweroutlettemplate', + index=models.Index(fields=['device_type', 'module_type', 'name'], name='dcim_powero_device__b83a8f_idx'), + ), + migrations.AddIndex( + model_name='powerporttemplate', + index=models.Index(fields=['device_type', 'module_type', 'name'], name='dcim_powerp_device__6c25da_idx'), + ), + migrations.AddIndex( + model_name='rack', + index=models.Index(fields=['site', 'location', 'name', 'id'], name='dcim_rack_site_id_715040_idx'), + ), + migrations.AddIndex( + model_name='rackreservation', + index=models.Index(fields=['created', 'id'], name='dcim_rackre_created_84f02e_idx'), + ), + migrations.AddIndex( + model_name='rearporttemplate', + index=models.Index(fields=['device_type', 'module_type', 'name'], name='dcim_rearpo_device__27f194_idx'), + ), + migrations.AddIndex( + model_name='virtualchassis', + index=models.Index(fields=['name'], name='dcim_virtua_name_2dc5cd_idx'), + ), + migrations.AddIndex( + model_name='virtualdevicecontext', + index=models.Index(fields=['name'], name='dcim_virtua_name_079d4d_idx'), + ), + ] diff --git a/netbox/dcim/migrations/0233_device_render_config_permission.py b/netbox/dcim/migrations/0233_device_render_config_permission.py new file mode 100644 index 000000000..e95e250dc --- /dev/null +++ b/netbox/dcim/migrations/0233_device_render_config_permission.py @@ -0,0 +1,15 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0232_default_ordering_indexes'), + ] + + operations = [ + migrations.AlterModelOptions( + name='device', + options={'ordering': ('name', 'pk'), 'permissions': [('render_config', 'Render configuration')]}, + ), + ] diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 8ceb36c85..06b5606bc 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -1,5 +1,6 @@ import itertools import logging +from collections import Counter from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType @@ -8,6 +9,7 @@ from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from django.dispatch import Signal +from django.urls import reverse from django.utils.translation import gettext_lazy as _ from core.models import ObjectType @@ -29,6 +31,7 @@ from .device_components import FrontPort, PathEndpoint, PortMapping, RearPort __all__ = ( 'Cable', + 'CableBundle', 'CablePath', 'CableTermination', ) @@ -38,6 +41,32 @@ logger = logging.getLogger(f'netbox.{__name__}') trace_paths = Signal() +# +# Cable bundles +# + +class CableBundle(PrimaryModel): + """ + A logical grouping of individual cables. + """ + name = models.CharField( + verbose_name=_('name'), + max_length=100, + unique=True, + ) + + class Meta: + ordering = ('name',) + verbose_name = _('cable bundle') + verbose_name_plural = _('cable bundles') + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse('dcim:cablebundle', args=[self.pk]) + + # # Cables # @@ -102,8 +131,16 @@ class Cable(PrimaryModel): blank=True, null=True ) + bundle = models.ForeignKey( + to='dcim.CableBundle', + on_delete=models.SET_NULL, + related_name='cables', + blank=True, + null=True, + verbose_name=_('bundle'), + ) - clone_fields = ('tenant', 'type', 'profile') + clone_fields = ('tenant', 'type', 'profile', 'bundle') class Meta: ordering = ('pk',) @@ -871,25 +908,37 @@ class CablePath(models.Model): # Build (termination, position) pairs by matching stacked positions # to each termination's cable_positions. This correctly handles # multiple terminations on different connectors of the same cable. - remaining = list(positions) + remaining = Counter(positions) term_position_pairs = [] for term in terminations: if term.cable_positions: for cp in term.cable_positions: - if cp in remaining: + if remaining[cp]: term_position_pairs.append((term, cp)) - remaining.remove(cp) + remaining[cp] -= 1 # Fallback for when positions don't match cable_positions # (e.g., empty position stack yielding [None]) if not term_position_pairs: term_position_pairs = [(terminations[0], pos) for pos in positions] - for term, pos in term_position_pairs: - peer, new_pos = cable_profile.get_peer_termination(term, pos) - if peer not in remote_terminations: + peer_results = cable_profile.get_peer_terminations(term_position_pairs) + seen = set() + for peer, new_pos in peer_results: + # Deduplicate peer terminations by model type & PK. + key = None if peer is None else (peer._meta.concrete_model, peer.pk) + if key not in seen: + seen.add(key) remote_terminations.append(peer) new_positions.append(new_pos) + + # If all peers resolved to None (no far-end terminations exist), + # treat as an empty result so the path is recorded as incomplete + # rather than falling through to the endpoint check with a stale + # None entry. + if remote_terminations and all(peer is None for peer in remote_terminations): + remote_terminations = [] + position_stack.append(new_positions) # Legacy (positionless) behavior @@ -909,7 +958,9 @@ class CablePath(models.Model): if not q_filter: break - remote_cable_terminations = CableTermination.objects.filter(q_filter) + remote_cable_terminations = CableTermination.objects.filter(q_filter).prefetch_related( + 'termination' + ) remote_terminations = [ct.termination for ct in remote_cable_terminations] else: # WirelessLink diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index 1799e1265..dec8758a9 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -144,6 +144,9 @@ class ModularComponentTemplateModel(ComponentTemplateModel): name='%(app_label)s_%(class)s_unique_module_type_name' ), ) + indexes = ( + models.Index(fields=('device_type', 'module_type', 'name')), # Default ordering + ) def to_objectchange(self, action): objectchange = super().to_objectchange(action) @@ -166,15 +169,47 @@ class ModularComponentTemplateModel(ComponentTemplateModel): _("A component template must be associated with either a device type or a module type.") ) - def resolve_name(self, module): - if MODULE_TOKEN not in self.name or not module: - return self.name - return resolve_module_placeholder(self.name, get_module_bay_positions(module.module_bay)) + @staticmethod + def _resolve_vc_position(value: str, device) -> str: + """ + Resolves {vc_position} and {vc_position:X} tokens. - def resolve_label(self, module): - if MODULE_TOKEN not in self.label or not module: - return self.label - return resolve_module_placeholder(self.label, get_module_bay_positions(module.module_bay)) + If the device has a vc_position, replaces the token with that value. + Otherwise uses the explicit fallback X if given, else '0'. + """ + def replacer(match): + explicit_fallback = match.group(1) + if ( + device is not None + and device.virtual_chassis is not None + and device.vc_position is not None + ): + return str(device.vc_position) + return explicit_fallback if explicit_fallback is not None else '0' + + return VC_POSITION_RE.sub(replacer, value) + + def _resolve_all_placeholders(self, value, module=None, device=None): + has_module = MODULE_TOKEN in value + has_vc = VC_POSITION_RE.search(value) is not None + if not has_module and not has_vc: + return value + if has_module and module: + positions = get_module_bay_positions(module.module_bay) + value = resolve_module_placeholder(value, positions) + if has_vc: + resolved_device = (module.device if module else None) or device + value = self._resolve_vc_position(value, resolved_device) + return value + + def resolve_name(self, module=None, device=None): + return self._resolve_all_placeholders(self.name, module, device) + + def resolve_label(self, module=None, device=None): + return self._resolve_all_placeholders(self.label, module, device) + + def resolve_position(self, module=None, device=None): + return self._resolve_all_placeholders(self.position, module, device) class ConsolePortTemplate(ModularComponentTemplateModel): @@ -197,8 +232,8 @@ class ConsolePortTemplate(ModularComponentTemplateModel): def instantiate(self, **kwargs): return self.component_model( - name=self.resolve_name(kwargs.get('module')), - label=self.resolve_label(kwargs.get('module')), + name=self.resolve_name(kwargs.get('module'), kwargs.get('device')), + label=self.resolve_label(kwargs.get('module'), kwargs.get('device')), type=self.type, **kwargs ) @@ -232,8 +267,8 @@ class ConsoleServerPortTemplate(ModularComponentTemplateModel): def instantiate(self, **kwargs): return self.component_model( - name=self.resolve_name(kwargs.get('module')), - label=self.resolve_label(kwargs.get('module')), + name=self.resolve_name(kwargs.get('module'), kwargs.get('device')), + label=self.resolve_label(kwargs.get('module'), kwargs.get('device')), type=self.type, **kwargs ) @@ -282,8 +317,8 @@ class PowerPortTemplate(ModularComponentTemplateModel): def instantiate(self, **kwargs): return self.component_model( - name=self.resolve_name(kwargs.get('module')), - label=self.resolve_label(kwargs.get('module')), + name=self.resolve_name(kwargs.get('module'), kwargs.get('device')), + label=self.resolve_label(kwargs.get('module'), kwargs.get('device')), type=self.type, maximum_draw=self.maximum_draw, allocated_draw=self.allocated_draw, @@ -370,13 +405,13 @@ class PowerOutletTemplate(ModularComponentTemplateModel): def instantiate(self, **kwargs): if self.power_port: - power_port_name = self.power_port.resolve_name(kwargs.get('module')) + power_port_name = self.power_port.resolve_name(kwargs.get('module'), kwargs.get('device')) power_port = PowerPort.objects.get(name=power_port_name, **kwargs) else: power_port = None return self.component_model( - name=self.resolve_name(kwargs.get('module')), - label=self.resolve_label(kwargs.get('module')), + name=self.resolve_name(kwargs.get('module'), kwargs.get('device')), + label=self.resolve_label(kwargs.get('module'), kwargs.get('device')), type=self.type, color=self.color, power_port=power_port, @@ -476,8 +511,8 @@ class InterfaceTemplate(InterfaceValidationMixin, ModularComponentTemplateModel) def instantiate(self, **kwargs): return self.component_model( - name=self.resolve_name(kwargs.get('module')), - label=self.resolve_label(kwargs.get('module')), + name=self.resolve_name(kwargs.get('module'), kwargs.get('device')), + label=self.resolve_label(kwargs.get('module'), kwargs.get('device')), type=self.type, enabled=self.enabled, mgmt_only=self.mgmt_only, @@ -611,8 +646,8 @@ class FrontPortTemplate(ModularComponentTemplateModel): def instantiate(self, **kwargs): return self.component_model( - name=self.resolve_name(kwargs.get('module')), - label=self.resolve_label(kwargs.get('module')), + name=self.resolve_name(kwargs.get('module'), kwargs.get('device')), + label=self.resolve_label(kwargs.get('module'), kwargs.get('device')), type=self.type, color=self.color, positions=self.positions, @@ -675,8 +710,8 @@ class RearPortTemplate(ModularComponentTemplateModel): def instantiate(self, **kwargs): return self.component_model( - name=self.resolve_name(kwargs.get('module')), - label=self.resolve_label(kwargs.get('module')), + name=self.resolve_name(kwargs.get('module'), kwargs.get('device')), + label=self.resolve_label(kwargs.get('module'), kwargs.get('device')), type=self.type, color=self.color, positions=self.positions, @@ -705,6 +740,10 @@ class ModuleBayTemplate(ModularComponentTemplateModel): blank=True, help_text=_('Identifier to reference when renaming installed components') ) + enabled = models.BooleanField( + verbose_name=_('enabled'), + default=True, + ) component_model = ModuleBay @@ -712,16 +751,12 @@ class ModuleBayTemplate(ModularComponentTemplateModel): verbose_name = _('module bay template') verbose_name_plural = _('module bay templates') - def resolve_position(self, module): - if MODULE_TOKEN not in self.position or not module: - return self.position - return resolve_module_placeholder(self.position, get_module_bay_positions(module.module_bay)) - def instantiate(self, **kwargs): return self.component_model( - name=self.resolve_name(kwargs.get('module')), - label=self.resolve_label(kwargs.get('module')), - position=self.resolve_position(kwargs.get('module')), + name=self.resolve_name(kwargs.get('module'), kwargs.get('device')), + label=self.resolve_label(kwargs.get('module'), kwargs.get('device')), + position=self.resolve_position(kwargs.get('module'), kwargs.get('device')), + enabled=self.enabled, **kwargs ) instantiate.do_not_call_in_templates = True @@ -731,6 +766,7 @@ class ModuleBayTemplate(ModularComponentTemplateModel): 'name': self.name, 'label': self.label, 'position': self.position, + 'enabled': self.enabled, 'description': self.description, } @@ -739,6 +775,11 @@ class DeviceBayTemplate(ComponentTemplateModel): """ A template for a DeviceBay to be created for a new parent Device. """ + enabled = models.BooleanField( + verbose_name=_('enabled'), + default=True, + ) + component_model = DeviceBay class Meta(ComponentTemplateModel.Meta): @@ -749,7 +790,8 @@ class DeviceBayTemplate(ComponentTemplateModel): return self.component_model( device=device, name=self.name, - label=self.label + label=self.label, + enabled=self.enabled, ) instantiate.do_not_call_in_templates = True @@ -765,6 +807,7 @@ class DeviceBayTemplate(ComponentTemplateModel): return { 'name': self.name, 'label': self.label, + 'enabled': self.enabled, 'description': self.description, } diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 8889dbc67..e3691fd6c 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -882,8 +882,8 @@ class Interface( verbose_name=_('wireless channel') ) rf_channel_frequency = models.DecimalField( - max_digits=7, - decimal_places=2, + max_digits=8, + decimal_places=3, blank=True, null=True, verbose_name=_('channel frequency (MHz)'), @@ -1332,10 +1332,14 @@ class ModuleBay(ModularComponentModel, TrackingModelMixin, MPTTModel): blank=True, help_text=_('Identifier to reference when renaming installed components') ) + enabled = models.BooleanField( + verbose_name=_('enabled'), + default=True, + ) objects = TreeManager() - clone_fields = ('device',) + clone_fields = ('device', 'enabled') class Meta(ModularComponentModel.Meta): # Empty tuple triggers Django migration detection for MPTT indexes @@ -1374,6 +1378,13 @@ class ModuleBay(ModularComponentModel, TrackingModelMixin, MPTTModel): self.parent = None super().save(*args, **kwargs) + @property + def _occupied(self): + """ + Indicates whether the module bay is occupied by a module. + """ + return bool(not self.enabled or hasattr(self, 'installed_module')) + class DeviceBay(ComponentModel, TrackingModelMixin): """ @@ -1386,8 +1397,12 @@ class DeviceBay(ComponentModel, TrackingModelMixin): blank=True, null=True ) + enabled = models.BooleanField( + verbose_name=_('enabled'), + default=True, + ) - clone_fields = ('device',) + clone_fields = ('device', 'enabled') class Meta(ComponentModel.Meta): verbose_name = _('device bay') @@ -1402,6 +1417,16 @@ class DeviceBay(ComponentModel, TrackingModelMixin): device_type=self.device.device_type )) + # Prevent installing a device into a disabled bay + if self.installed_device and not self.enabled: + current_installed_device_id = ( + DeviceBay.objects.filter(pk=self.pk).values_list('installed_device_id', flat=True).first() + ) + if self.pk is None or current_installed_device_id != self.installed_device_id: + raise ValidationError({ + 'installed_device': _("Cannot install a device in a disabled device bay.") + }) + # Cannot install a device into itself, obviously if self.installed_device and getattr(self, 'device', None) == self.installed_device: raise ValidationError(_("Cannot install a device into itself.")) @@ -1416,6 +1441,13 @@ class DeviceBay(ComponentModel, TrackingModelMixin): ).format(bay=current_bay) }) + @property + def _occupied(self): + """ + Indicates whether the device bay is occupied by a child device. + """ + return bool(not self.enabled or self.installed_device_id) + # # Inventory items diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index aa4a794e2..88d094b97 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -26,6 +26,7 @@ from netbox.config import ConfigItem from netbox.models import NestedGroupModel, OrganizationalModel, PrimaryModel from netbox.models.features import ContactsMixin, ImageAttachmentsMixin from netbox.models.mixins import WeightMixin +from utilities.exceptions import AbortRequest from utilities.fields import ColorField, CounterCacheField from utilities.prefetch import get_prefetchable_fields from utilities.tracking import TrackingModelMixin @@ -745,6 +746,9 @@ class Device( class Meta: ordering = ('name', 'pk') # Name may be null + indexes = ( + models.Index(fields=('name', 'id')), # Default ordering + ) constraints = ( models.UniqueConstraint( Lower('name'), 'site', 'tenant', @@ -767,6 +771,9 @@ class Device( ) verbose_name = _('device') verbose_name_plural = _('devices') + permissions = [ + ('render_config', 'Render configuration'), + ] def __str__(self): if self.label and self.asset_tag: @@ -957,6 +964,20 @@ class Device( ).format(virtual_chassis=self.vc_master_for) }) + def _check_duplicate_component_names(self, components): + """ + Check for duplicate component names after resolving {vc_position} placeholders. + Raises AbortRequest if duplicates are found. + """ + names = [c.name for c in components] + duplicates = {n for n in names if names.count(n) > 1} + if duplicates: + raise AbortRequest( + _("Component name conflict after resolving {{vc_position}}: {names}").format( + names=', '.join(duplicates) + ) + ) + def _instantiate_components(self, queryset, bulk_create=True): """ Instantiate components for the device from the specified component templates. @@ -971,6 +992,10 @@ class Device( components = [obj.instantiate(device=self) for obj in queryset] if not components: return + + # Check for duplicate names after resolution {vc_position} + self._check_duplicate_component_names(components) + # Set default values for any applicable custom fields if cf_defaults := CustomField.objects.get_defaults_for_model(model): for component in components: @@ -995,8 +1020,14 @@ class Device( update_fields=None ) else: - for obj in queryset: - component = obj.instantiate(device=self) + components = [obj.instantiate(device=self) for obj in queryset] + if not components: + return + + # Check for duplicate names after resolution {vc_position} + self._check_duplicate_component_names(components) + + for component in components: # Set default values for any applicable custom fields if cf_defaults := CustomField.objects.get_defaults_for_model(model): component.custom_field_data = cf_defaults @@ -1168,6 +1199,9 @@ class VirtualChassis(PrimaryModel): class Meta: ordering = ['name'] + indexes = ( + models.Index(fields=('name',)), # Default ordering + ) verbose_name = _('virtual chassis') verbose_name_plural = _('virtual chassis') @@ -1274,6 +1308,9 @@ class VirtualDeviceContext(PrimaryModel): name='%(app_label)s_%(class)s_device_name' ), ) + indexes = ( + models.Index(fields=('name',)), # Default ordering + ) verbose_name = _('virtual device context') verbose_name_plural = _('virtual device contexts') @@ -1340,6 +1377,7 @@ class MACAddress(PrimaryModel): class Meta: ordering = ('mac_address', 'pk') indexes = ( + models.Index(fields=('mac_address', 'id')), # Default ordering models.Index(fields=('assigned_object_type', 'assigned_object_id')), ) verbose_name = _('MAC address') diff --git a/netbox/dcim/models/modules.py b/netbox/dcim/models/modules.py index 56cc333ff..1d678deb0 100644 --- a/netbox/dcim/models/modules.py +++ b/netbox/dcim/models/modules.py @@ -113,6 +113,9 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): name='%(app_label)s_%(class)s_unique_manufacturer_model' ), ) + indexes = ( + models.Index(fields=('profile', 'manufacturer', 'model')), # Default ordering + ) verbose_name = _('module type') verbose_name_plural = _('module types') @@ -266,6 +269,14 @@ class Module(TrackingModelMixin, PrimaryModel, ConfigContextModel): ) ) + # Prevent module from being installed in a disabled bay + if hasattr(self, 'module_bay') and self.module_bay and not self.module_bay.enabled: + current_module_bay_id = Module.objects.filter(pk=self.pk).values_list('module_bay_id', flat=True).first() + if self.pk is None or current_module_bay_id != self.module_bay_id: + raise ValidationError({ + 'module_bay': _("Cannot install a module in a disabled module bay.") + }) + # Check for recursion module = self module_bays = [] diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index 60707cb06..7a226e0c6 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -29,14 +29,30 @@ from .power import PowerFeed __all__ = ( 'Rack', + 'RackGroup', 'RackReservation', 'RackRole', 'RackType', ) +# +# Rack Organization +# + + +class RackGroup(OrganizationalModel): + """ + Racks can be grouped by physical placement within a Location. + """ + + class Meta: + ordering = ('name',) + verbose_name = _('rack group') + verbose_name_plural = _('rack groups') + # -# Rack Types +# Rack Base # class RackBase(WeightMixin, PrimaryModel): @@ -123,6 +139,10 @@ class RackBase(WeightMixin, PrimaryModel): abstract = True +# +# Rack Types +# + class RackType(ImageAttachmentsMixin, RackBase): """ Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face. @@ -290,6 +310,14 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, TrackingModelMixin, RackBase): blank=True, null=True ) + group = models.ForeignKey( + to='dcim.RackGroup', + on_delete=models.PROTECT, + related_name='racks', + blank=True, + null=True, + help_text=_('physical grouping') + ) tenant = models.ForeignKey( to='tenancy.Tenant', on_delete=models.PROTECT, @@ -362,6 +390,9 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, TrackingModelMixin, RackBase): name='%(app_label)s_%(class)s_unique_location_facility_id' ), ) + indexes = ( + models.Index(fields=('site', 'location', 'name', 'id')), # Default ordering + ) verbose_name = _('rack') verbose_name_plural = _('racks') @@ -710,6 +741,9 @@ class RackReservation(PrimaryModel): class Meta: ordering = ['created', 'pk'] + indexes = ( + models.Index(fields=('created', 'id')), # Default ordering + ) verbose_name = _('rack reservation') verbose_name_plural = _('rack reservations') diff --git a/netbox/dcim/search.py b/netbox/dcim/search.py index f956786c6..3eebc6952 100644 --- a/netbox/dcim/search.py +++ b/netbox/dcim/search.py @@ -3,6 +3,17 @@ from netbox.search import SearchIndex, register_search from . import models +@register_search +class CableBundleIndex(SearchIndex): + model = models.CableBundle + fields = ( + ('name', 100), + ('description', 500), + ('comments', 5000), + ) + display_attrs = ('description',) + + @register_search class CableIndex(SearchIndex): model = models.Cable @@ -315,6 +326,18 @@ class RackReservationIndex(SearchIndex): display_attrs = ('rack', 'tenant', 'user', 'description') +@register_search +class RackGroupIndex(SearchIndex): + model = models.RackGroup + fields = ( + ('name', 100), + ('slug', 110), + ('description', 500), + ('comments', 5000), + ) + display_attrs = ('description',) + + @register_search class RackRoleIndex(SearchIndex): model = models.RackRole diff --git a/netbox/dcim/tables/cables.py b/netbox/dcim/tables/cables.py index 06c1b3056..29c01ff48 100644 --- a/netbox/dcim/tables/cables.py +++ b/netbox/dcim/tables/cables.py @@ -4,13 +4,14 @@ from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ from django_tables2.utils import Accessor -from dcim.models import Cable +from dcim.models import Cable, CableBundle from netbox.tables import PrimaryModelTable, columns from tenancy.tables import TenancyColumnsMixin from .template_code import CABLE_LENGTH __all__ = ( + 'CableBundleTable', 'CableTable', ) @@ -119,6 +120,10 @@ class CableTable(TenancyColumnsMixin, PrimaryModelTable): verbose_name=_('Color Name'), orderable=False ) + bundle = tables.Column( + verbose_name=_('Bundle'), + linkify=True, + ) tags = columns.TagColumn( url_name='dcim:cable_list' ) @@ -128,8 +133,30 @@ class CableTable(TenancyColumnsMixin, PrimaryModelTable): fields = ( 'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'device_a', 'device_b', 'rack_a', 'rack_b', 'location_a', 'location_b', 'site_a', 'site_b', 'status', 'profile', 'type', 'tenant', 'tenant_group', - 'color', 'color_name', 'length', 'description', 'comments', 'tags', 'created', 'last_updated', + 'color', 'color_name', 'bundle', 'length', 'description', 'comments', 'tags', 'created', 'last_updated', ) default_columns = ( 'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'status', 'type', ) + + +class CableBundleTable(PrimaryModelTable): + name = tables.Column( + verbose_name=_('Name'), + linkify=True, + ) + cable_count = tables.Column( + verbose_name=_('Cables'), + ) + tags = columns.TagColumn( + url_name='dcim:cablebundle_list' + ) + + class Meta(PrimaryModelTable.Meta): + model = CableBundle + fields = ( + 'pk', 'id', 'name', 'cable_count', 'description', 'tags', 'created', 'last_updated', + ) + default_columns = ( + 'pk', 'id', 'name', 'cable_count', 'description', + ) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index b0de5e35d..e824f2683 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -908,6 +908,9 @@ class DeviceBayTable(DeviceComponentTable): 'args': [Accessor('device_id')], } ) + enabled = columns.BooleanColumn( + verbose_name=_('Enabled'), + ) status = tables.TemplateColumn( verbose_name=_('Status'), template_code=DEVICEBAY_STATUS, @@ -945,12 +948,12 @@ class DeviceBayTable(DeviceComponentTable): class Meta(DeviceComponentTable.Meta): model = models.DeviceBay fields = ( - 'pk', 'id', 'name', 'device', 'label', 'status', 'description', 'installed_device', 'installed_role', - 'installed_device_type', 'installed_description', 'installed_serial', 'installed_asset_tag', 'tags', - 'created', 'last_updated', + 'pk', 'id', 'name', 'device', 'label', 'enabled', 'status', 'description', 'installed_device', + 'installed_role', 'installed_device_type', 'installed_description', 'installed_serial', + 'installed_asset_tag', 'tags', 'created', 'last_updated', ) - default_columns = ('pk', 'name', 'device', 'label', 'status', 'installed_device', 'description') + default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'status', 'installed_device', 'description') class DeviceDeviceBayTable(DeviceBayTable): @@ -960,6 +963,9 @@ class DeviceDeviceBayTable(DeviceBayTable): '"> {{ value }}', attrs={'td': {'class': 'text-nowrap'}} ) + enabled = columns.BooleanColumn( + verbose_name=_('Enabled'), + ) actions = columns.ActionsColumn( extra_buttons=DEVICEBAY_BUTTONS ) @@ -967,9 +973,9 @@ class DeviceDeviceBayTable(DeviceBayTable): class Meta(DeviceComponentTable.Meta): model = models.DeviceBay fields = ( - 'pk', 'id', 'name', 'label', 'status', 'installed_device', 'description', 'tags', 'actions', + 'pk', 'id', 'name', 'label', 'enabled', 'status', 'installed_device', 'description', 'tags', 'actions', ) - default_columns = ('pk', 'name', 'label', 'status', 'installed_device', 'description') + default_columns = ('pk', 'name', 'label', 'enabled', 'status', 'installed_device', 'description') class ModuleBayTable(ModularDeviceComponentTable): @@ -980,6 +986,9 @@ class ModuleBayTable(ModularDeviceComponentTable): 'args': [Accessor('device_id')], } ) + enabled = columns.BooleanColumn( + verbose_name=_('Enabled'), + ) parent = tables.Column( linkify=True, verbose_name=_('Parent'), @@ -1008,11 +1017,11 @@ class ModuleBayTable(ModularDeviceComponentTable): class Meta(ModularDeviceComponentTable.Meta): model = models.ModuleBay fields = ( - 'pk', 'id', 'name', 'device', 'parent', 'label', 'position', 'installed_module', 'module_status', + 'pk', 'id', 'name', 'device', 'enabled', 'parent', 'label', 'position', 'installed_module', 'module_status', 'module_serial', 'module_asset_tag', 'description', 'tags', ) default_columns = ( - 'pk', 'name', 'device', 'parent', 'label', 'installed_module', 'module_status', 'description', + 'pk', 'name', 'device', 'enabled', 'parent', 'label', 'installed_module', 'module_status', 'description', ) def render_parent_bay(self, value): @@ -1027,6 +1036,9 @@ class DeviceModuleBayTable(ModuleBayTable): verbose_name=_('Name'), linkify=True, ) + enabled = columns.BooleanColumn( + verbose_name=_('Enabled'), + ) actions = columns.ActionsColumn( extra_buttons=MODULEBAY_BUTTONS ) @@ -1034,10 +1046,10 @@ class DeviceModuleBayTable(ModuleBayTable): class Meta(ModuleBayTable.Meta): model = models.ModuleBay fields = ( - 'pk', 'id', 'parent', 'name', 'label', 'position', 'installed_module', 'module_status', 'module_serial', - 'module_asset_tag', 'description', 'tags', 'actions', + 'pk', 'id', 'parent', 'name', 'label', 'enabled', 'position', 'installed_module', 'module_status', + 'module_serial', 'module_asset_tag', 'description', 'tags', 'actions', ) - default_columns = ('pk', 'name', 'label', 'installed_module', 'module_status', 'description') + default_columns = ('pk', 'name', 'label', 'enabled', 'installed_module', 'module_status', 'description') class InventoryItemTable(DeviceComponentTable): diff --git a/netbox/dcim/tables/devicetypes.py b/netbox/dcim/tables/devicetypes.py index 652ff9d45..f952c2d4a 100644 --- a/netbox/dcim/tables/devicetypes.py +++ b/netbox/dcim/tables/devicetypes.py @@ -289,24 +289,30 @@ class RearPortTemplateTable(ComponentTemplateTable): class ModuleBayTemplateTable(ComponentTemplateTable): + enabled = columns.BooleanColumn( + verbose_name=_('Enabled'), + ) actions = columns.ActionsColumn( actions=('edit', 'delete') ) class Meta(ComponentTemplateTable.Meta): model = models.ModuleBayTemplate - fields = ('pk', 'name', 'label', 'position', 'description', 'actions') + fields = ('pk', 'name', 'label', 'position', 'enabled', 'description', 'actions') empty_text = "None" class DeviceBayTemplateTable(ComponentTemplateTable): + enabled = columns.BooleanColumn( + verbose_name=_('Enabled'), + ) actions = columns.ActionsColumn( actions=('edit', 'delete') ) class Meta(ComponentTemplateTable.Meta): model = models.DeviceBayTemplate - fields = ('pk', 'name', 'label', 'description', 'actions') + fields = ('pk', 'name', 'label', 'enabled', 'description', 'actions') empty_text = "None" diff --git a/netbox/dcim/tables/racks.py b/netbox/dcim/tables/racks.py index 186565e86..2ef1bd72c 100644 --- a/netbox/dcim/tables/racks.py +++ b/netbox/dcim/tables/racks.py @@ -2,13 +2,14 @@ import django_tables2 as tables from django.utils.translation import gettext_lazy as _ from django_tables2.utils import Accessor -from dcim.models import Rack, RackReservation, RackRole, RackType +from dcim.models import Rack, RackGroup, RackReservation, RackRole, RackType from netbox.tables import OrganizationalModelTable, PrimaryModelTable, columns from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin from .template_code import OUTER_UNIT, WEIGHT __all__ = ( + 'RackGroupTable', 'RackReservationTable', 'RackRoleTable', 'RackTable', @@ -16,6 +17,29 @@ __all__ = ( ) +class RackGroupTable(OrganizationalModelTable): + name = tables.Column( + verbose_name=_('Name'), + linkify=True, + ) + rack_count = columns.LinkedCountColumn( + viewname='dcim:rack_list', + url_params={'group_id': 'pk'}, + verbose_name=_('Racks'), + ) + tags = columns.TagColumn( + url_name='dcim:rackgroup_list', + ) + + class Meta(OrganizationalModelTable.Meta): + model = RackGroup + fields = ( + 'pk', 'id', 'name', 'rack_count', 'description', 'slug', 'comments', 'tags', 'actions', 'created', + 'last_updated', + ) + default_columns = ('pk', 'name', 'rack_count', 'description') + + class RackRoleTable(OrganizationalModelTable): name = tables.Column( verbose_name=_('Name'), @@ -111,6 +135,10 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable): verbose_name=_('Site'), linkify=True ) + group = tables.Column( + verbose_name=_('Group'), + linkify=True, + ) status = columns.ChoiceFieldColumn( verbose_name=_('Status'), ) @@ -172,15 +200,15 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable): class Meta(PrimaryModelTable.Meta): model = Rack fields = ( - 'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'tenant_group', 'role', + 'pk', 'id', 'name', 'site', 'location', 'group', 'status', 'facility_id', 'tenant', 'tenant_group', 'role', 'rack_type', 'serial', 'asset_tag', 'form_factor', 'u_height', 'starting_unit', 'width', 'outer_width', 'outer_height', 'outer_depth', 'mounting_depth', 'airflow', 'weight', 'max_weight', 'comments', 'device_count', 'get_utilization', 'get_power_utilization', 'description', 'contacts', 'tags', 'created', 'last_updated', ) default_columns = ( - 'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'rack_type', 'u_height', - 'device_count', 'get_utilization', + 'pk', 'name', 'site', 'location', 'group', 'status', 'facility_id', 'tenant', 'role', 'rack_type', + 'u_height', 'device_count', 'get_utilization', ) @@ -200,6 +228,11 @@ class RackReservationTable(TenancyColumnsMixin, PrimaryModelTable): accessor=Accessor('rack__location'), linkify=True ) + group = tables.Column( + verbose_name=_('Group'), + accessor=Accessor('rack__group'), + linkify=True + ) rack = tables.Column( verbose_name=_('Rack'), linkify=True @@ -208,6 +241,9 @@ class RackReservationTable(TenancyColumnsMixin, PrimaryModelTable): orderable=False, verbose_name=_('Units') ) + unit_count = tables.Column( + verbose_name=_("Total U's") + ) status = columns.ChoiceFieldColumn( verbose_name=_('Status'), ) @@ -218,7 +254,9 @@ class RackReservationTable(TenancyColumnsMixin, PrimaryModelTable): class Meta(PrimaryModelTable.Meta): model = RackReservation fields = ( - 'pk', 'id', 'reservation', 'site', 'location', 'rack', 'unit_list', 'status', 'user', 'tenant', - 'tenant_group', 'description', 'comments', 'tags', 'actions', 'created', 'last_updated', + 'pk', 'id', 'reservation', 'site', 'location', 'group', 'rack', 'unit_list', 'unit_count', 'status', + 'user', 'tenant', 'tenant_group', 'description', 'comments', 'tags', 'actions', 'created', 'last_updated', + ) + default_columns = ( + 'pk', 'reservation', 'site', 'rack', 'unit_list', 'unit_count', 'status', 'user', 'description', ) - default_columns = ('pk', 'reservation', 'site', 'rack', 'unit_list', 'status', 'user', 'description') diff --git a/netbox/dcim/tables/template_code.py b/netbox/dcim/tables/template_code.py index 3675a18cc..2b786aec5 100644 --- a/netbox/dcim/tables/template_code.py +++ b/netbox/dcim/tables/template_code.py @@ -565,7 +565,7 @@ DEVICEBAY_BUTTONS = """ - {% else %} + {% elif record.enabled %} @@ -579,7 +579,7 @@ MODULEBAY_BUTTONS = """ - {% else %} + {% elif record.enabled %} diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index 0089f6f51..16edb205d 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -6,17 +6,24 @@ from django.urls import reverse from django.utils.translation import gettext as _ from rest_framework import status +from core.models import ObjectType from dcim.choices import * from dcim.constants import * from dcim.models import * -from extras.models import ConfigTemplate +from extras.models import ConfigTemplate, Tag from ipam.choices import VLANQinQRoleChoices from ipam.models import ASN, RIR, VLAN, VRF from netbox.api.serializers import GenericObjectSerializer from tenancy.models import Tenant from users.constants import TOKEN_PREFIX -from users.models import Token, User -from utilities.testing import APITestCase, APIViewTestCases, create_test_device, disable_logging +from users.models import ObjectPermission, Token, User +from utilities.testing import ( + APITestCase, + APIViewTestCases, + create_test_device, + create_test_nat_ip_pair, + disable_logging, +) from virtualization.models import Cluster, ClusterType from wireless.choices import WirelessChannelChoices from wireless.models import WirelessLAN @@ -196,6 +203,222 @@ class SiteTest(APIViewTestCases.APIViewTestCase): }, ] + def test_add_tags(self): + """ + Add tags to an existing object via the add_tags field. + """ + site = Site.objects.first() + tags = Tag.objects.bulk_create(( + Tag(name='Alpha', slug='alpha'), + Tag(name='Bravo', slug='bravo'), + Tag(name='Charlie', slug='charlie'), + )) + site.tags.set([tags[0], tags[1]]) + + # Grant change permission + obj_perm = ObjectPermission(name='Test permission', actions=['change']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) + + url = self._get_detail_url(site) + data = { + 'add_tags': [{'name': 'Charlie'}], + } + response = self.client.patch(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + + # Verify all three tags are now assigned + tag_names = sorted(site.tags.values_list('name', flat=True)) + self.assertEqual(tag_names, ['Alpha', 'Bravo', 'Charlie']) + + # Verify add_tags and remove_tags are not in the response + self.assertNotIn('add_tags', response.data) + self.assertNotIn('remove_tags', response.data) + self.assertIn('tags', response.data) + + def test_remove_tags(self): + """ + Remove tags from an existing object via the remove_tags field. + """ + site = Site.objects.first() + tags = Tag.objects.bulk_create(( + Tag(name='Alpha', slug='alpha'), + Tag(name='Bravo', slug='bravo'), + Tag(name='Charlie', slug='charlie'), + )) + site.tags.set(tags) + + # Grant change permission + obj_perm = ObjectPermission(name='Test permission', actions=['change']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) + + url = self._get_detail_url(site) + data = { + 'remove_tags': [{'name': 'Charlie'}], + } + response = self.client.patch(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + + # Verify only Alpha and Bravo remain + tag_names = sorted(site.tags.values_list('name', flat=True)) + self.assertEqual(tag_names, ['Alpha', 'Bravo']) + + def test_remove_tags_not_assigned(self): + """ + Removing a tag that is not assigned should not raise an error. + """ + site = Site.objects.first() + tags = Tag.objects.bulk_create(( + Tag(name='Alpha', slug='alpha'), + Tag(name='Bravo', slug='bravo'), + Tag(name='Charlie', slug='charlie'), + )) + site.tags.set([tags[0], tags[1]]) + + # Grant change permission + obj_perm = ObjectPermission(name='Test permission', actions=['change']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) + + url = self._get_detail_url(site) + data = { + 'remove_tags': [{'name': 'Charlie'}], + } + response = self.client.patch(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + + # Tags should be unchanged + tag_names = sorted(site.tags.values_list('name', flat=True)) + self.assertEqual(tag_names, ['Alpha', 'Bravo']) + + def test_add_and_remove_tags(self): + """ + Add and remove tags in the same request. + """ + site = Site.objects.first() + tags = Tag.objects.bulk_create(( + Tag(name='Alpha', slug='alpha'), + Tag(name='Bravo', slug='bravo'), + Tag(name='Charlie', slug='charlie'), + )) + site.tags.set([tags[0], tags[1]]) + + # Grant change permission + obj_perm = ObjectPermission(name='Test permission', actions=['change']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) + + url = self._get_detail_url(site) + data = { + 'add_tags': [{'name': 'Charlie'}], + 'remove_tags': [{'name': 'Alpha'}], + } + response = self.client.patch(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + + # Verify Bravo and Charlie remain + tag_names = sorted(site.tags.values_list('name', flat=True)) + self.assertEqual(tag_names, ['Bravo', 'Charlie']) + + def test_tags_with_add_tags_error(self): + """ + Specifying tags together with add_tags or remove_tags should raise a validation error. + """ + site = Site.objects.first() + Tag.objects.bulk_create(( + Tag(name='Alpha', slug='alpha'), + Tag(name='Bravo', slug='bravo'), + )) + + # Grant change permission + obj_perm = ObjectPermission(name='Test permission', actions=['change']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) + + url = self._get_detail_url(site) + data = { + 'tags': [{'name': 'Alpha'}], + 'add_tags': [{'name': 'Bravo'}], + } + response = self.client.patch(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + + def test_create_with_add_tags(self): + """ + Create a new object using add_tags. + """ + Tag.objects.bulk_create(( + Tag(name='Alpha', slug='alpha'), + Tag(name='Bravo', slug='bravo'), + )) + + obj_perm = ObjectPermission(name='Test permission', actions=['add']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) + + data = { + 'name': 'Site 10', + 'slug': 'site-10', + 'add_tags': [{'name': 'Alpha'}, {'name': 'Bravo'}], + } + response = self.client.post(self._get_list_url(), data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_201_CREATED) + + site = Site.objects.get(pk=response.data['id']) + tag_names = sorted(site.tags.values_list('name', flat=True)) + self.assertEqual(tag_names, ['Alpha', 'Bravo']) + + def test_create_with_remove_tags_error(self): + """ + Using remove_tags when creating a new object should raise a validation error. + """ + Tag.objects.bulk_create(( + Tag(name='Alpha', slug='alpha'), + )) + + obj_perm = ObjectPermission(name='Test permission', actions=['add']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) + + data = { + 'name': 'Site 10', + 'slug': 'site-10', + 'remove_tags': [{'name': 'Alpha'}], + } + response = self.client.post(self._get_list_url(), data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + + def test_add_and_remove_same_tag_error(self): + """ + Including the same tag in both add_tags and remove_tags should raise a validation error. + """ + site = Site.objects.first() + Tag.objects.bulk_create(( + Tag(name='Alpha', slug='alpha'), + Tag(name='Bravo', slug='bravo'), + )) + + obj_perm = ObjectPermission(name='Test permission', actions=['change']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) + + url = self._get_detail_url(site) + data = { + 'add_tags': [{'name': 'Alpha'}, {'name': 'Bravo'}], + 'remove_tags': [{'name': 'Alpha'}], + } + response = self.client.patch(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + class LocationTest(APIViewTestCases.APIViewTestCase): model = Location @@ -281,6 +504,38 @@ class LocationTest(APIViewTestCases.APIViewTestCase): ] +class RackGroupTest(APIViewTestCases.APIViewTestCase): + model = RackGroup + brief_fields = ['description', 'display', 'id', 'name', 'rack_count', 'slug', 'url'] + create_data = [ + { + 'name': 'Rack Group 4', + 'slug': 'rack-group-4', + }, + { + 'name': 'Rack Group 5', + 'slug': 'rack-group-5', + }, + { + 'name': 'Rack Group 6', + 'slug': 'rack-group-6', + }, + ] + bulk_update_data = { + 'description': 'New description', + } + + @classmethod + def setUpTestData(cls): + + rack_groups = ( + RackGroup(name='Rack Group 1', slug='rack-group-1'), + RackGroup(name='Rack Group 2', slug='rack-group-2'), + RackGroup(name='Rack Group 3', slug='rack-group-3'), + ) + RackGroup.objects.bulk_create(rack_groups) + + class RackRoleTest(APIViewTestCases.APIViewTestCase): model = RackRole brief_fields = ['description', 'display', 'id', 'name', 'rack_count', 'slug', 'url'] @@ -398,6 +653,12 @@ class RackTest(APIViewTestCases.APIViewTestCase): Location.objects.create(site=sites[1], name='Location 2', slug='location-2'), ) + rack_groups = ( + RackGroup(name='Rack Group 1', slug='rack-group-1'), + RackGroup(name='Rack Group 2', slug='rack-group-2'), + ) + RackGroup.objects.bulk_create(rack_groups) + rack_roles = ( RackRole(name='Rack Role 1', slug='rack-role-1', color='ff0000'), RackRole(name='Rack Role 2', slug='rack-role-2', color='00ff00'), @@ -405,9 +666,9 @@ class RackTest(APIViewTestCases.APIViewTestCase): RackRole.objects.bulk_create(rack_roles) racks = ( - Rack(site=sites[0], location=locations[0], role=rack_roles[0], name='Rack 1'), - Rack(site=sites[0], location=locations[0], role=rack_roles[0], name='Rack 2'), - Rack(site=sites[0], location=locations[0], role=rack_roles[0], name='Rack 3'), + Rack(site=sites[0], location=locations[0], group=rack_groups[0], role=rack_roles[0], name='Rack 1'), + Rack(site=sites[0], location=locations[0], group=rack_groups[0], role=rack_roles[0], name='Rack 2'), + Rack(site=sites[0], location=locations[0], group=rack_groups[0], role=rack_roles[0], name='Rack 3'), ) Rack.objects.bulk_create(racks) @@ -416,18 +677,21 @@ class RackTest(APIViewTestCases.APIViewTestCase): 'name': 'Test Rack 4', 'site': sites[1].pk, 'location': locations[1].pk, + 'group': rack_groups[1].pk, 'role': rack_roles[1].pk, }, { 'name': 'Test Rack 5', 'site': sites[1].pk, 'location': locations[1].pk, + 'group': rack_groups[1].pk, 'role': rack_roles[1].pk, }, { 'name': 'Test Rack 6', 'site': sites[1].pk, 'location': locations[1].pk, + 'group': rack_groups[1].pk, 'role': rack_roles[1].pk, }, ] @@ -530,6 +794,15 @@ class RackReservationTest(APIViewTestCases.APIViewTestCase): }, ] + def test_unit_count(self): + """unit_count should reflect the number of units in the reservation.""" + url = reverse('dcim-api:rackreservation-list') + self.add_permissions('dcim.view_rackreservation') + response = self.client.get(url, **self.header) + self.assertHttpStatus(response, 200) + for result in response.data['results']: + self.assertEqual(result['unit_count'], len(result['units'])) + class ManufacturerTest(APIViewTestCases.APIViewTestCase): model = Manufacturer @@ -1186,7 +1459,7 @@ class RearPortTemplateTest(APIViewTestCases.APIViewTestCase): class ModuleBayTemplateTest(APIViewTestCases.APIViewTestCase): model = ModuleBayTemplate - brief_fields = ['description', 'display', 'id', 'name', 'url'] + brief_fields = ['description', 'display', 'enabled', 'id', 'name', 'url'] bulk_update_data = { 'description': 'New description', } @@ -1203,9 +1476,9 @@ class ModuleBayTemplateTest(APIViewTestCases.APIViewTestCase): ) module_bay_templates = ( - ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 1'), - ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 2'), - ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 3'), + ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 1', enabled=True), + ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 2', enabled=False), + ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 3', enabled=True), ) ModuleBayTemplate.objects.bulk_create(module_bay_templates) @@ -1213,6 +1486,7 @@ class ModuleBayTemplateTest(APIViewTestCases.APIViewTestCase): { 'device_type': devicetype.pk, 'name': 'Module Bay Template 4', + 'enabled': False, }, { 'device_type': devicetype.pk, @@ -1227,7 +1501,7 @@ class ModuleBayTemplateTest(APIViewTestCases.APIViewTestCase): class DeviceBayTemplateTest(APIViewTestCases.APIViewTestCase): model = DeviceBayTemplate - brief_fields = ['description', 'display', 'id', 'name', 'url'] + brief_fields = ['description', 'display', 'enabled', 'id', 'name', 'url'] bulk_update_data = { 'description': 'New description', } @@ -1244,9 +1518,9 @@ class DeviceBayTemplateTest(APIViewTestCases.APIViewTestCase): ) device_bay_templates = ( - DeviceBayTemplate(device_type=devicetype, name='Device Bay Template 1'), - DeviceBayTemplate(device_type=devicetype, name='Device Bay Template 2'), - DeviceBayTemplate(device_type=devicetype, name='Device Bay Template 3'), + DeviceBayTemplate(device_type=devicetype, name='Device Bay Template 1', enabled=True), + DeviceBayTemplate(device_type=devicetype, name='Device Bay Template 2', enabled=False), + DeviceBayTemplate(device_type=devicetype, name='Device Bay Template 3', enabled=True), ) DeviceBayTemplate.objects.bulk_create(device_bay_templates) @@ -1254,6 +1528,7 @@ class DeviceBayTemplateTest(APIViewTestCases.APIViewTestCase): { 'device_type': devicetype.pk, 'name': 'Device Bay Template 4', + 'enabled': False, }, { 'device_type': devicetype.pk, @@ -1634,6 +1909,81 @@ class DeviceTest(APIViewTestCases.APIViewTestCase): response = self.client.post(url, {}, format='json', HTTP_AUTHORIZATION=token_header) self.assertHttpStatus(response, status.HTTP_200_OK) + def test_list_object_includes_nat_inside_on_primary_ip(self): + device = create_test_device('natted-device') + interface = Interface.objects.create(device=device, name='eth0', type='other') + + real_ip, nat_ip = create_test_nat_ip_pair( + real_address='10.0.0.10/32', + nat_address='198.51.100.10/32', + inside_interface=interface, + ) + + device.primary_ip4 = nat_ip + device.save() + + self.add_permissions('dcim.view_device', 'ipam.view_ipaddress') + response = self.client.get(f'{self._get_list_url()}?id={device.pk}', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + + result = response.data['results'][0] + for field in ('primary_ip', 'primary_ip4'): + self.assertEqual(result[field]['address'], str(nat_ip.address)) + self.assertEqual(result[field]['nat_inside']['address'], str(real_ip.address)) + self.assertEqual(result[field]['nat_outside'], []) + + def test_get_object_includes_nat_outside_on_primary_ip(self): + device = create_test_device('real-ip-device') + interface = Interface.objects.create(device=device, name='eth0', type='other') + + real_ip, nat_ip = create_test_nat_ip_pair( + real_address='10.0.0.11/32', + nat_address='198.51.100.11/32', + inside_interface=interface, + ) + + device.primary_ip4 = real_ip + device.save() + + self.add_permissions('dcim.view_device', 'ipam.view_ipaddress') + response = self.client.get( + f'{self._get_detail_url(device)}?exclude=config_context', + **self.header, + ) + self.assertHttpStatus(response, status.HTTP_200_OK) + + for field in ('primary_ip', 'primary_ip4'): + self.assertEqual(response.data[field]['address'], str(real_ip.address)) + self.assertIsNone(response.data[field]['nat_inside']) + self.assertCountEqual( + [ip['address'] for ip in response.data[field]['nat_outside']], + [str(nat_ip.address)], + ) + + def test_get_object_includes_nat_on_oob_ip(self): + device = create_test_device('oob-nat-device') + interface = Interface.objects.create(device=device, name='oob0', type='other') + + real_ip, nat_ip = create_test_nat_ip_pair( + real_address='10.0.0.12/32', + nat_address='198.51.100.12/32', + inside_interface=interface, + ) + + device.oob_ip = nat_ip + device.save() + + self.add_permissions('dcim.view_device', 'ipam.view_ipaddress') + response = self.client.get( + f'{self._get_detail_url(device)}?exclude=config_context', + **self.header, + ) + self.assertHttpStatus(response, status.HTTP_200_OK) + + self.assertEqual(response.data['oob_ip']['address'], str(nat_ip.address)) + self.assertEqual(response.data['oob_ip']['nat_inside']['address'], str(real_ip.address)) + self.assertEqual(response.data['oob_ip']['nat_outside'], []) + def test_render_config_with_config_template_id(self): default_template = ConfigTemplate.objects.create( name='Default Template', @@ -1742,6 +2092,238 @@ class ModuleTest(APIViewTestCases.APIViewTestCase): }, ] + def test_replicate_components(self): + """ + Installing a module with replicate_components=True (the default) should create + components from the module type's templates on the parent device. + """ + self.add_permissions('dcim.add_module') + manufacturer = Manufacturer.objects.get(name='Generic') + device = create_test_device('Device for Replication Test') + module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Replication Test Module Type') + InterfaceTemplate.objects.create(module_type=module_type, name='eth0', type='1000base-t') + module_bay = ModuleBay.objects.create(device=device, name='Replication Bay') + + url = reverse('dcim-api:module-list') + data = { + 'device': device.pk, + 'module_bay': module_bay.pk, + 'module_type': module_type.pk, + 'replicate_components': True, + } + response = self.client.post(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_201_CREATED) + self.assertTrue(device.interfaces.filter(name='eth0').exists()) + + def test_no_replicate_components(self): + """ + Installing a module with replicate_components=False should NOT create components + from the module type's templates. + """ + self.add_permissions('dcim.add_module') + manufacturer = Manufacturer.objects.get(name='Generic') + device = create_test_device('Device for No Replication Test') + module_type = ModuleType.objects.create(manufacturer=manufacturer, model='No Replication Test Module Type') + InterfaceTemplate.objects.create(module_type=module_type, name='eth0', type='1000base-t') + module_bay = ModuleBay.objects.create(device=device, name='No Replication Bay') + + url = reverse('dcim-api:module-list') + data = { + 'device': device.pk, + 'module_bay': module_bay.pk, + 'module_type': module_type.pk, + 'replicate_components': False, + } + response = self.client.post(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_201_CREATED) + self.assertFalse(device.interfaces.filter(name='eth0').exists()) + + def test_adopt_components(self): + """ + Installing a module with adopt_components=True should assign existing unattached + device components to the new module. + """ + self.add_permissions('dcim.add_module') + manufacturer = Manufacturer.objects.get(name='Generic') + device = create_test_device('Device for Adopt Test') + module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Adopt Test Module Type') + InterfaceTemplate.objects.create(module_type=module_type, name='eth0', type='1000base-t') + module_bay = ModuleBay.objects.create(device=device, name='Adopt Bay') + existing_iface = Interface.objects.create(device=device, name='eth0', type='1000base-t') + + url = reverse('dcim-api:module-list') + data = { + 'device': device.pk, + 'module_bay': module_bay.pk, + 'module_type': module_type.pk, + 'adopt_components': True, + 'replicate_components': False, + } + response = self.client.post(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_201_CREATED) + existing_iface.refresh_from_db() + self.assertIsNotNone(existing_iface.module) + + def test_replicate_components_conflict(self): + """ + Installing a module with replicate_components=True when a component with the same name + already exists should return a validation error. + """ + self.add_permissions('dcim.add_module') + manufacturer = Manufacturer.objects.get(name='Generic') + device = create_test_device('Device for Conflict Test') + module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Conflict Test Module Type') + InterfaceTemplate.objects.create(module_type=module_type, name='eth0', type='1000base-t') + module_bay = ModuleBay.objects.create(device=device, name='Conflict Bay') + Interface.objects.create(device=device, name='eth0', type='1000base-t') + + url = reverse('dcim-api:module-list') + data = { + 'device': device.pk, + 'module_bay': module_bay.pk, + 'module_type': module_type.pk, + 'replicate_components': True, + } + response = self.client.post(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + + def test_adopt_components_already_owned(self): + """ + Installing a module with adopt_components=True when an existing component already + belongs to another module should return a validation error. + """ + self.add_permissions('dcim.add_module') + manufacturer = Manufacturer.objects.get(name='Generic') + device = create_test_device('Device for Adopt Owned Test') + owner_module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Owner Module Type') + module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Adopt Owned Test Module Type') + InterfaceTemplate.objects.create(module_type=module_type, name='eth0', type='1000base-t') + owner_bay = ModuleBay.objects.create(device=device, name='Owner Bay') + target_bay = ModuleBay.objects.create(device=device, name='Adopt Owned Bay') + + # Install a module that owns the interface + owner_module = Module.objects.create(device=device, module_bay=owner_bay, module_type=owner_module_type) + Interface.objects.create(device=device, name='eth0', type='1000base-t', module=owner_module) + + url = reverse('dcim-api:module-list') + data = { + 'device': device.pk, + 'module_bay': target_bay.pk, + 'module_type': module_type.pk, + 'adopt_components': True, + } + response = self.client.post(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + + def test_patch_ignores_replicate_and_adopt(self): + """ + PATCH requests that include replicate_components or adopt_components should not + trigger component replication or adoption (these fields are create-only). + """ + self.add_permissions('dcim.change_module') + manufacturer = Manufacturer.objects.get(name='Generic') + device = create_test_device('Device for PATCH Test') + module_type = ModuleType.objects.create(manufacturer=manufacturer, model='PATCH Test Module Type') + InterfaceTemplate.objects.create(module_type=module_type, name='eth0', type='1000base-t') + module_bay = ModuleBay.objects.create(device=device, name='PATCH Bay') + # Create the module without replication so we can verify PATCH doesn't trigger it + module = Module(device=device, module_bay=module_bay, module_type=module_type) + module._disable_replication = True + module.save() + + url = reverse('dcim-api:module-detail', kwargs={'pk': module.pk}) + data = { + 'replicate_components': True, + 'adopt_components': True, + 'serial': 'PATCHED', + } + response = self.client.patch(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response.data['serial'], 'PATCHED') + # No interfaces should have been created by the PATCH + self.assertFalse(device.interfaces.exists()) + + def test_adopt_and_replicate_components(self): + """ + Installing a module with both adopt_components=True and replicate_components=True + should adopt existing unowned components and create new components for templates + that have no matching existing component. + """ + self.add_permissions('dcim.add_module') + manufacturer = Manufacturer.objects.get(name='Generic') + device = create_test_device('Device for Adopt+Replicate Test') + module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Adopt+Replicate Test Module Type') + InterfaceTemplate.objects.create(module_type=module_type, name='eth0', type='1000base-t') + InterfaceTemplate.objects.create(module_type=module_type, name='eth1', type='1000base-t') + module_bay = ModuleBay.objects.create(device=device, name='Adopt+Replicate Bay') + # eth0 already exists (unowned); eth1 does not + existing_iface = Interface.objects.create(device=device, name='eth0', type='1000base-t') + + url = reverse('dcim-api:module-list') + data = { + 'device': device.pk, + 'module_bay': module_bay.pk, + 'module_type': module_type.pk, + 'adopt_components': True, + 'replicate_components': True, + } + response = self.client.post(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_201_CREATED) + # eth0 should have been adopted (now owned by the new module) + existing_iface.refresh_from_db() + self.assertIsNotNone(existing_iface.module) + # eth1 should have been created + self.assertTrue(device.interfaces.filter(name='eth1').exists()) + + def test_module_token_no_position(self): + """ + Installing a module whose type has a template with a MODULE_TOKEN placeholder into a + module bay with no position defined should return a validation error. + """ + self.add_permissions('dcim.add_module') + manufacturer = Manufacturer.objects.get(name='Generic') + device = create_test_device('Device for Token No-Position Test') + module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Token No-Position Module Type') + # Template name contains the MODULE_TOKEN placeholder + InterfaceTemplate.objects.create( + module_type=module_type, name=f'{MODULE_TOKEN}-eth0', type='1000base-t' + ) + # Module bay has no position + module_bay = ModuleBay.objects.create(device=device, name='No-Position Bay') + + url = reverse('dcim-api:module-list') + data = { + 'device': device.pk, + 'module_bay': module_bay.pk, + 'module_type': module_type.pk, + } + response = self.client.post(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + + def test_module_token_depth_mismatch(self): + """ + Installing a module whose template name has more MODULE_TOKEN placeholders than the + depth of the module bay tree should return a validation error. + """ + self.add_permissions('dcim.add_module') + manufacturer = Manufacturer.objects.get(name='Generic') + device = create_test_device('Device for Token Depth Mismatch Test') + module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Token Depth Mismatch Module Type') + # Template name has two placeholders but the bay is at depth 1 + InterfaceTemplate.objects.create( + module_type=module_type, name=f'{MODULE_TOKEN}-{MODULE_TOKEN}-eth0', type='1000base-t' + ) + module_bay = ModuleBay.objects.create(device=device, name='Depth 1 Bay', position='1') + + url = reverse('dcim-api:module-list') + data = { + 'device': device.pk, + 'module_bay': module_bay.pk, + 'module_type': module_type.pk, + } + response = self.client.post(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + def test_list_objects_by_profile_id(self): profiles = ModuleTypeProfile.objects.filter(name__startswith='Test').order_by('name') self.add_permissions('dcim.view_module') @@ -2394,7 +2976,7 @@ class RearPortTest(APIViewTestCases.APIViewTestCase): class ModuleBayTest(APIViewTestCases.APIViewTestCase): model = ModuleBay - brief_fields = ['description', 'display', 'id', 'installed_module', 'name', 'url'] + brief_fields = ['_occupied', 'description', 'display', 'enabled', 'id', 'installed_module', 'name', 'url'] bulk_update_data = { 'description': 'New description', } @@ -2410,9 +2992,9 @@ class ModuleBayTest(APIViewTestCases.APIViewTestCase): device = Device.objects.create(device_type=device_type, role=role, name='Device 1', site=site) module_bays = ( - ModuleBay(device=device, name='Device Bay 1'), - ModuleBay(device=device, name='Device Bay 2'), - ModuleBay(device=device, name='Device Bay 3'), + ModuleBay(device=device, name='Device Bay 1', enabled=True), + ModuleBay(device=device, name='Device Bay 2', enabled=False), + ModuleBay(device=device, name='Device Bay 3', enabled=True), ) for module_bay in module_bays: module_bay.save() @@ -2421,6 +3003,7 @@ class ModuleBayTest(APIViewTestCases.APIViewTestCase): { 'device': device.pk, 'name': 'Device Bay 4', + 'enabled': False, }, { 'device': device.pk, @@ -2435,7 +3018,7 @@ class ModuleBayTest(APIViewTestCases.APIViewTestCase): class DeviceBayTest(APIViewTestCases.APIViewTestCase): model = DeviceBay - brief_fields = ['description', 'device', 'display', 'id', 'name', 'url'] + brief_fields = ['_occupied', 'description', 'device', 'display', 'enabled', 'id', 'name', 'url'] bulk_update_data = { 'description': 'New description', } @@ -2472,9 +3055,9 @@ class DeviceBayTest(APIViewTestCases.APIViewTestCase): Device.objects.bulk_create(devices) device_bays = ( - DeviceBay(device=devices[0], name='Device Bay 1'), - DeviceBay(device=devices[0], name='Device Bay 2'), - DeviceBay(device=devices[0], name='Device Bay 3'), + DeviceBay(device=devices[0], name='Device Bay 1', enabled=True), + DeviceBay(device=devices[0], name='Device Bay 2', enabled=False), + DeviceBay(device=devices[0], name='Device Bay 3', enabled=True), ) DeviceBay.objects.bulk_create(device_bays) @@ -2599,6 +3182,60 @@ class InventoryItemRoleTest(APIViewTestCases.APIViewTestCase): InventoryItemRole.objects.bulk_create(roles) +class CableBundleTest(APIViewTestCases.APIViewTestCase): + model = CableBundle + brief_fields = ['description', 'display', 'id', 'name', 'url'] + create_data = [ + {'name': 'Cable Bundle 4'}, + {'name': 'Cable Bundle 5'}, + {'name': 'Cable Bundle 6'}, + ] + bulk_update_data = { + 'description': 'New description', + } + + @classmethod + def setUpTestData(cls): + cable_bundles = ( + CableBundle(name='Cable Bundle 1'), + CableBundle(name='Cable Bundle 2'), + CableBundle(name='Cable Bundle 3'), + ) + CableBundle.objects.bulk_create(cable_bundles) + + def test_cable_count(self): + """cable_count annotation is returned correctly in the API response.""" + self.add_permissions('dcim.view_cablebundle') + bundle = CableBundle.objects.first() + + site = Site.objects.create(name='CB Test Site', slug='cb-test-site') + manufacturer = Manufacturer.objects.create(name='CB Manufacturer', slug='cb-manufacturer') + device_type = DeviceType.objects.create( + manufacturer=manufacturer, model='CB Device Type', slug='cb-device-type' + ) + role = DeviceRole.objects.create(name='CB Role', slug='cb-role', color='ff0000') + devices = ( + Device(device_type=device_type, role=role, name='CB Device 1', site=site), + Device(device_type=device_type, role=role, name='CB Device 2', site=site), + ) + Device.objects.bulk_create(devices) + interfaces = ( + Interface(device=devices[0], name='eth0', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface(device=devices[0], name='eth1', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface(device=devices[1], name='eth0', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface(device=devices[1], name='eth1', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + ) + Interface.objects.bulk_create(interfaces) + for a, b in [(interfaces[0], interfaces[2]), (interfaces[1], interfaces[3])]: + cable = Cable(a_terminations=[a], b_terminations=[b], bundle=bundle) + cable.save() + + url = reverse('dcim-api:cablebundle-detail', kwargs={'pk': bundle.pk}) + response = self.client.get(url, **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response.data['cable_count'], 2) + + class CableTest(APIViewTestCases.APIViewTestCase): model = Cable brief_fields = ['description', 'display', 'id', 'label', 'url'] diff --git a/netbox/dcim/tests/test_cable_profiles.py b/netbox/dcim/tests/test_cable_profiles.py index f0e24a76d..1d504f42b 100644 --- a/netbox/dcim/tests/test_cable_profiles.py +++ b/netbox/dcim/tests/test_cable_profiles.py @@ -1,5 +1,12 @@ from django.test import tag +from dcim.cable_profiles import ( + Breakout1C4Px4C1PCableProfile, + Single1C1PCableProfile, + Single1C4PCableProfile, + Trunk2C2PCableProfile, + Trunk2C4PShuffleCableProfile, +) from dcim.choices import CableProfileChoices from dcim.models import Cable, Interface, RearPort from dcim.tests.utils import CablePathTestCase @@ -66,3 +73,201 @@ class CableProfileLinkPeerTests(CablePathTestCase): for interface in interfaces[2:4] + interfaces[6:8]: self.assertEqual(interface.link_peers, [rear_ports[1]]) + + +class CableProfilePeerTerminationTests(CablePathTestCase): + """ + Tests for BaseCableProfile.get_peer_termination() and get_peer_terminations(). + Verifies that the batch method produces identical results to calling + the singular method in a loop. + """ + + @classmethod + def setUpTestData(cls): + super().setUpTestData() + + # Shared pool of interfaces — cables are created per-test since they + # mutate CableTerminations and cached fields on save. + cls.interfaces = [Interface(device=cls.device, name=f'Interface {i}') for i in range(1, 17)] + Interface.objects.bulk_create(cls.interfaces) + + def _assert_batch_matches_singular(self, cable_profile, term_position_pairs): + """ + Helper: assert get_peer_terminations() returns the same results as + calling get_peer_termination() individually for each pair. + """ + expected = [cable_profile.get_peer_termination(term, pos) for term, pos in term_position_pairs] + actual = cable_profile.get_peer_terminations(term_position_pairs) + self.assertEqual(len(actual), len(expected)) + for i, (exp, act) in enumerate(zip(expected, actual)): + exp_peer, exp_pos = exp + act_peer, act_pos = act + self.assertEqual( + (type(act_peer), getattr(act_peer, 'pk', None), act_pos), + (type(exp_peer), getattr(exp_peer, 'pk', None), exp_pos), + msg=f'Mismatch at index {i}: expected {exp}, got {act}', + ) + + def test_empty_pairs(self): + """ + get_peer_terminations() with an empty list returns an empty list. + """ + profile = Single1C1PCableProfile() + self.assertEqual(profile.get_peer_terminations([]), []) + + def test_single_pair_fast_path(self): + """ + A single-pair call should use the fast path and produce the same + result as get_peer_termination(). + """ + cable = Cable( + profile=CableProfileChoices.SINGLE_1C1P, + a_terminations=[self.interfaces[0]], + b_terminations=[self.interfaces[1]], + ) + cable.clean() + cable.save() + + self.interfaces[0].refresh_from_db() + profile = Single1C1PCableProfile() + + self._assert_batch_matches_singular(profile, [(self.interfaces[0], 1)]) + + def test_single_connector_multi_position(self): + """ + Batch resolution on a Single 1C4P profile should return the same + peers as individual lookups for each position. + """ + cable = Cable( + profile=CableProfileChoices.SINGLE_1C4P, + a_terminations=[self.interfaces[0]], + b_terminations=[self.interfaces[4]], + ) + cable.clean() + cable.save() + + self.interfaces[0].refresh_from_db() + profile = Single1C4PCableProfile() + + # Query all 4 positions from the A-side termination + pairs = [(self.interfaces[0], pos) for pos in range(1, 5)] + self._assert_batch_matches_singular(profile, pairs) + + def test_multi_connector_multi_position(self): + """ + Batch resolution on a Trunk 2C2P profile across both connectors + should match individual lookups. + """ + cable = Cable( + profile=CableProfileChoices.TRUNK_2C2P, + a_terminations=[self.interfaces[0], self.interfaces[1]], + b_terminations=[self.interfaces[2], self.interfaces[3]], + ) + cable.clean() + cable.save() + + for iface in self.interfaces[:4]: + iface.refresh_from_db() + profile = Trunk2C2PCableProfile() + + # Build pairs for both A-side terminations across their positions + pairs = [] + for iface in self.interfaces[:2]: + for pos in iface.cable_positions: + pairs.append((iface, pos)) + + self._assert_batch_matches_singular(profile, pairs) + + def test_shuffle_profile_mapping(self): + """ + Batch resolution on a shuffle profile should correctly apply the + non-linear position mapping. + """ + cable = Cable( + profile=CableProfileChoices.TRUNK_2C4P_SHUFFLE, + a_terminations=[self.interfaces[0], self.interfaces[1]], + b_terminations=[self.interfaces[2], self.interfaces[3]], + ) + cable.clean() + cable.save() + + for iface in self.interfaces[:4]: + iface.refresh_from_db() + profile = Trunk2C4PShuffleCableProfile() + + pairs = [] + for iface in self.interfaces[:2]: + for pos in iface.cable_positions: + pairs.append((iface, pos)) + + self._assert_batch_matches_singular(profile, pairs) + + def test_breakout_profile(self): + """ + Batch resolution on a breakout profile should correctly map A-side + positions to different B-side connectors. + """ + cable = Cable( + profile=CableProfileChoices.BREAKOUT_1C4P_4C1P, + a_terminations=[self.interfaces[8]], + b_terminations=self.interfaces[9:13], + ) + cable.clean() + cable.save() + + self.interfaces[8].refresh_from_db() + for iface in self.interfaces[9:13]: + iface.refresh_from_db() + profile = Breakout1C4Px4C1PCableProfile() + + # Test A→B direction (one connector, 4 positions → 4 connectors) + a_pairs = [(self.interfaces[8], pos) for pos in self.interfaces[8].cable_positions] + self._assert_batch_matches_singular(profile, a_pairs) + + # Test B→A direction (4 connectors, 1 position each → one connector) + b_pairs = [(iface, 1) for iface in self.interfaces[9:13]] + self._assert_batch_matches_singular(profile, b_pairs) + + def test_multi_position_single_termination(self): + """ + When a single-connector multi-position profile has only one termination + per side, all positions should resolve to the same peer object. The batch + method must return identical results to the singular method for each. + """ + # Use a multi-position profile but only connect one termination per side. + # The CableTermination will have positions=[1,2,3,4] but only one object + # is attached, so querying any position still resolves to that same peer. + cable = Cable( + profile=CableProfileChoices.SINGLE_1C4P, + a_terminations=[self.interfaces[0]], + b_terminations=[self.interfaces[1]], + ) + cable.clean() + cable.save() + + self.interfaces[0].refresh_from_db() + profile = Single1C4PCableProfile() + + # All 4 positions should resolve to the same B-side termination + pairs = [(self.interfaces[0], pos) for pos in range(1, 5)] + self._assert_batch_matches_singular(profile, pairs) + + def test_duplicate_pairs(self): + """ + Submitting the same (termination, position) pair multiple times should + return the correct result for each occurrence without errors. + """ + cable = Cable( + profile=CableProfileChoices.SINGLE_1C1P, + a_terminations=[self.interfaces[0]], + b_terminations=[self.interfaces[1]], + ) + cable.clean() + cable.save() + + self.interfaces[0].refresh_from_db() + profile = Single1C1PCableProfile() + + # The same pair submitted three times + pairs = [(self.interfaces[0], 1)] * 3 + self._assert_batch_matches_singular(profile, pairs) diff --git a/netbox/dcim/tests/test_cablepaths2.py b/netbox/dcim/tests/test_cablepaths2.py index 5efaee67c..68208ad07 100644 --- a/netbox/dcim/tests/test_cablepaths2.py +++ b/netbox/dcim/tests/test_cablepaths2.py @@ -14,6 +14,7 @@ class CablePathTests(CablePathTestCase): Tests are numbered as follows: 1XX: Test direct connections using each profile 2XX: Topology tests replicated from the legacy test case and adapted to use profiles + 3XX: Dynamic port mapping and termination changes """ def test_101_cable_profile_single_1c1p(self): @@ -1223,6 +1224,98 @@ class CablePathTests(CablePathTestCase): # Test SVG generation CableTraceSVG(interfaces[0]).render() + def test_110_partial_termination_profiled_trunk(self): + """ + Tests that tracing through a partially terminated profiled cable + produces a complete path for the connected pair and an incomplete + path for the unconnected pair, without errors. Also verifies that + attaching the missing termination completes the previously incomplete path. + + [IF1] --C1-- [IF3] + [IF2] (empty) + + Cable profile: Trunk 2C1P with only one B-side termination. + """ + interfaces = [ + Interface.objects.create(device=self.device, name='Interface 1'), + Interface.objects.create(device=self.device, name='Interface 2'), + Interface.objects.create(device=self.device, name='Interface 3'), + ] + + # Create a 2-connector trunk cable with both A-side connectors + # populated but only one B-side connector terminated. + cable1 = Cable( + profile=CableProfileChoices.TRUNK_2C1P, + a_terminations=[interfaces[0], interfaces[1]], + b_terminations=[interfaces[2]], + ) + cable1.clean() + cable1.save() + + # IF1 (connector 1) → IF3 (connector 1): complete path + path1 = self.assertPathExists( + (interfaces[0], cable1, interfaces[2]), + is_complete=True, + is_active=True, + ) + + # IF3 (connector 1) → IF1 (connector 1): complete path (reverse) + path2 = self.assertPathExists( + (interfaces[2], cable1, interfaces[0]), + is_complete=True, + is_active=True, + ) + + # IF2 (connector 2) has no B-side peer. + # Tracing should stop at this segment, and the resulting path + # should remain incomplete. + # Verify via the origin's _path reference rather than matching + # the exact path shape directly. + interfaces[1].refresh_from_db() + self.assertIsNotNone(interfaces[1]._path_id) + path3 = CablePath.objects.get(pk=interfaces[1]._path_id) + self.assertFalse(path3.is_complete) + self.assertTrue(path3.is_active) + + for iface in interfaces: + iface.refresh_from_db() + self.assertPathIsSet(interfaces[0], path1) + self.assertPathIsSet(interfaces[2], path2) + self.assertPathIsSet(interfaces[1], path3) + + # Verify connector/position assignments + self.assertEqual(interfaces[0].cable_connector, 1) + self.assertEqual(interfaces[0].cable_positions, [1]) + self.assertEqual(interfaces[1].cable_connector, 2) + self.assertEqual(interfaces[1].cable_positions, [1]) + self.assertEqual(interfaces[2].cable_connector, 1) + self.assertEqual(interfaces[2].cable_positions, [1]) + + # Now attach the missing B-side termination and verify the + # previously incomplete path becomes complete. + interface4 = Interface.objects.create(device=self.device, name='Interface 4') + cable1.b_terminations = [interfaces[2], interface4] + cable1.clean() + cable1.save() + + path4 = self.assertPathExists( + (interfaces[1], cable1, interface4), + is_complete=True, + is_active=True, + ) + path5 = self.assertPathExists( + (interface4, cable1, interfaces[1]), + is_complete=True, + is_active=True, + ) + + interfaces[1].refresh_from_db() + interface4.refresh_from_db() + self.assertPathIsSet(interfaces[1], path4) + self.assertPathIsSet(interface4, path5) + self.assertEqual(interface4.cable_connector, 2) + self.assertEqual(interface4.cable_positions, [1]) + def test_202_single_path_via_pass_through_with_breakouts(self): """ [IF1] --C1-- [FP1] [RP1] --C2-- [IF3] diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index 154a25bba..e003487dd 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -535,6 +535,37 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) +class RackGroupTestCase(TestCase, ChangeLoggedFilterSetTests): + queryset = RackGroup.objects.all() + filterset = RackGroupFilterSet + + @classmethod + def setUpTestData(cls): + + rack_groups = ( + RackGroup(name='Rack Group 1', slug='rack-group-1', description='foobar1'), + RackGroup(name='Rack Group 2', slug='rack-group-2', description='foobar2'), + RackGroup(name='Rack Group 3', slug='rack-group-3'), + ) + RackGroup.objects.bulk_create(rack_groups) + + def test_q(self): + params = {'q': 'foobar1'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + def test_name(self): + params = {'name': ['Rack Group 1', 'Rack Group 2']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_slug(self): + params = {'slug': ['rack-group-1', 'rack-group-2']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_description(self): + params = {'description': ['foobar1', 'foobar2']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + class RackRoleTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = RackRole.objects.all() filterset = RackRoleFilterSet @@ -739,18 +770,18 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): for region in regions: region.save() - groups = ( + site_groups = ( SiteGroup(name='Site Group 1', slug='site-group-1'), SiteGroup(name='Site Group 2', slug='site-group-2'), SiteGroup(name='Site Group 3', slug='site-group-3'), ) - for group in groups: - group.save() + for site_group in site_groups: + site_group.save() sites = ( - Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]), - Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]), - Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]), + Site(name='Site 1', slug='site-1', region=regions[0], group=site_groups[0]), + Site(name='Site 2', slug='site-2', region=regions[1], group=site_groups[1]), + Site(name='Site 3', slug='site-3', region=regions[2], group=site_groups[2]), ) Site.objects.bulk_create(sites) @@ -811,6 +842,13 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): ) RackType.objects.bulk_create(rack_types) + rack_groups = ( + RackGroup(name='Rack Group 1', slug='rack-group-1'), + RackGroup(name='Rack Group 2', slug='rack-group-2'), + RackGroup(name='Rack Group 3', slug='rack-group-3'), + ) + RackGroup.objects.bulk_create(rack_groups) + rack_roles = ( RackRole(name='Rack Role 1', slug='rack-role-1'), RackRole(name='Rack Role 2', slug='rack-role-2'), @@ -839,6 +877,7 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): facility_id='rack-1', site=sites[0], location=locations[0], + group=rack_groups[0], tenant=tenants[0], status=RackStatusChoices.STATUS_ACTIVE, role=rack_roles[0], @@ -863,6 +902,7 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): facility_id='rack-2', site=sites[1], location=locations[1], + group=rack_groups[1], tenant=tenants[1], status=RackStatusChoices.STATUS_PLANNED, role=rack_roles[1], @@ -887,6 +927,7 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): facility_id='rack-3', site=sites[2], location=locations[2], + group=rack_groups[2], tenant=tenants[2], status=RackStatusChoices.STATUS_RESERVED, role=rack_roles[2], @@ -1018,6 +1059,13 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'location': [locations[0].slug, locations[1].slug]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_rack_group(self): + rack_groups = RackGroup.objects.all()[:2] + params = {'group_id': [rack_groups[0].pk, rack_groups[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'group': [rack_groups[0].slug, rack_groups[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_status(self): params = {'status': [RackStatusChoices.STATUS_ACTIVE, RackStatusChoices.STATUS_PLANNED]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) @@ -1096,18 +1144,18 @@ class RackReservationTestCase(TestCase, ChangeLoggedFilterSetTests): for region in regions: region.save() - groups = ( + site_groups = ( SiteGroup(name='Site Group 1', slug='site-group-1'), SiteGroup(name='Site Group 2', slug='site-group-2'), SiteGroup(name='Site Group 3', slug='site-group-3'), ) - for group in groups: - group.save() + for site_group in site_groups: + site_group.save() sites = ( - Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]), - Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]), - Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]), + Site(name='Site 1', slug='site-1', region=regions[0], group=site_groups[0]), + Site(name='Site 2', slug='site-2', region=regions[1], group=site_groups[1]), + Site(name='Site 3', slug='site-3', region=regions[2], group=site_groups[2]), ) Site.objects.bulk_create(sites) @@ -1119,10 +1167,17 @@ class RackReservationTestCase(TestCase, ChangeLoggedFilterSetTests): for location in locations: location.save() + rack_groups = ( + RackGroup(name='Rack Group 1', slug='rack-group-1'), + RackGroup(name='Rack Group 2', slug='rack-group-2'), + RackGroup(name='Rack Group 3', slug='rack-group-3'), + ) + RackGroup.objects.bulk_create(rack_groups) + racks = ( - Rack(name='Rack 1', site=sites[0], location=locations[0]), - Rack(name='Rack 2', site=sites[1], location=locations[1]), - Rack(name='Rack 3', site=sites[2], location=locations[2]), + Rack(name='Rack 1', site=sites[0], location=locations[0], group=rack_groups[0]), + Rack(name='Rack 2', site=sites[1], location=locations[1], group=rack_groups[1]), + Rack(name='Rack 3', site=sites[2], location=locations[2], group=rack_groups[2]), ) Rack.objects.bulk_create(racks) @@ -1151,7 +1206,7 @@ class RackReservationTestCase(TestCase, ChangeLoggedFilterSetTests): reservations = ( RackReservation( rack=racks[0], - units=[1, 2, 3], + units=[1, 2], status=RackReservationStatusChoices.STATUS_ACTIVE, user=users[0], tenant=tenants[0], @@ -1159,7 +1214,7 @@ class RackReservationTestCase(TestCase, ChangeLoggedFilterSetTests): ), RackReservation( rack=racks[1], - units=[4, 5, 6], + units=[1, 2, 3], status=RackReservationStatusChoices.STATUS_PENDING, user=users[1], tenant=tenants[1], @@ -1167,7 +1222,7 @@ class RackReservationTestCase(TestCase, ChangeLoggedFilterSetTests): ), RackReservation( rack=racks[2], - units=[7, 8, 9], + units=[1, 2, 3, 4], status=RackReservationStatusChoices.STATUS_STALE, user=users[2], tenant=tenants[2], @@ -1208,6 +1263,13 @@ class RackReservationTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'location': [locations[0].slug, locations[1].slug]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_rack_group(self): + rack_groups = RackGroup.objects.all()[:2] + params = {'group_id': [rack_groups[0].pk, rack_groups[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'group': [rack_groups[0].slug, rack_groups[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_status(self): params = {'status': [RackReservationStatusChoices.STATUS_ACTIVE, RackReservationStatusChoices.STATUS_PENDING]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -1230,6 +1292,14 @@ class RackReservationTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'description': ['foobar1', 'foobar2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_unit_count(self): + params = {'unit_count_min': 3} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'unit_count_max': 3} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'unit_count_min': 3, 'unit_count_max': 3} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_tenant_group(self): tenant_groups = TenantGroup.objects.all()[:2] params = {'tenant_group_id': [tenant_groups[0].pk, tenant_groups[1].pk]} @@ -2186,13 +2256,21 @@ class ModuleBayTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTests, ModuleBayTemplate.objects.bulk_create( ( ModuleBayTemplate( - device_type=device_types[0], name='Module Bay 1', description='foobar1' + device_type=device_types[0], name='Module Bay 1', enabled=True, description='foobar1' ), ModuleBayTemplate( - device_type=device_types[1], name='Module Bay 2', description='foobar2', module_type=module_types[0] + device_type=device_types[1], + name='Module Bay 2', + enabled=False, + description='foobar2', + module_type=module_types[0], ), ModuleBayTemplate( - device_type=device_types[2], name='Module Bay 3', description='foobar3', module_type=module_types[1] + device_type=device_types[2], + name='Module Bay 3', + enabled=True, + description='foobar3', + module_type=module_types[1], ), ) ) @@ -2201,6 +2279,12 @@ class ModuleBayTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTests, params = {'name': ['Module Bay 1', 'Module Bay 2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_enabled(self): + params = {'enabled': True} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'enabled': False} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_module_type(self): module_types = ModuleType.objects.all()[:2] params = {'module_type_id': [module_types[0].pk, module_types[1].pk]} @@ -2223,16 +2307,30 @@ class DeviceBayTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTests, ) DeviceType.objects.bulk_create(device_types) - DeviceBayTemplate.objects.bulk_create(( - DeviceBayTemplate(device_type=device_types[0], name='Device Bay 1', description='foobar1'), - DeviceBayTemplate(device_type=device_types[1], name='Device Bay 2', description='foobar2'), - DeviceBayTemplate(device_type=device_types[2], name='Device Bay 3', description='foobar3'), - )) + DeviceBayTemplate.objects.bulk_create( + ( + DeviceBayTemplate( + device_type=device_types[0], name='Device Bay 1', enabled=True, description='foobar1' + ), + DeviceBayTemplate( + device_type=device_types[1], name='Device Bay 2', enabled=False, description='foobar2' + ), + DeviceBayTemplate( + device_type=device_types[2], name='Device Bay 3', enabled=True, description='foobar3' + ), + ) + ) def test_name(self): params = {'name': ['Device Bay 1', 'Device Bay 2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_enabled(self): + params = {'enabled': True} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'enabled': False} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + class InventoryItemTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTests, ChangeLoggedFilterSetTests): queryset = InventoryItemTemplate.objects.all() @@ -5851,11 +5949,11 @@ class ModuleBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil Device.objects.bulk_create(devices) module_bays = ( - ModuleBay(device=devices[0], name='Module Bay 1', label='A', description='First'), - ModuleBay(device=devices[1], name='Module Bay 2', label='B', description='Second'), - ModuleBay(device=devices[2], name='Module Bay 3', label='C', description='Third'), - ModuleBay(device=devices[2], name='Module Bay 4', label='D', description='Fourth'), - ModuleBay(device=devices[2], name='Module Bay 5', label='E', description='Fifth'), + ModuleBay(device=devices[0], name='Module Bay 1', label='A', enabled=True, description='First'), + ModuleBay(device=devices[1], name='Module Bay 2', label='B', enabled=False, description='Second'), + ModuleBay(device=devices[2], name='Module Bay 3', label='C', enabled=True, description='Third'), + ModuleBay(device=devices[2], name='Module Bay 4', label='D', enabled=False, description='Fourth'), + ModuleBay(device=devices[2], name='Module Bay 5', label='E', enabled=True, description='Fifth'), ) for module_bay in module_bays: module_bay.save() @@ -5879,6 +5977,12 @@ class ModuleBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil params = {'label': ['A', 'B']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_enabled(self): + params = {'enabled': True} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) + params = {'enabled': False} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_description(self): params = {'description': ['First', 'Second']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -6038,6 +6142,7 @@ class DeviceBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil device=devices[0], name='Device Bay 1', label='A', + enabled=True, description='First', _site=devices[0].site, _location=devices[0].location, @@ -6047,6 +6152,7 @@ class DeviceBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil device=devices[1], name='Device Bay 2', label='B', + enabled=False, description='Second', _site=devices[1].site, _location=devices[1].location, @@ -6056,6 +6162,7 @@ class DeviceBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil device=devices[2], name='Device Bay 3', label='C', + enabled=True, description='Third', _site=devices[2].site, _location=devices[2].location, @@ -6072,6 +6179,12 @@ class DeviceBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil params = {'label': ['A', 'B']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_enabled(self): + params = {'enabled': True} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'enabled': False} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_description(self): params = {'description': ['First', 'Second']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -6544,6 +6657,32 @@ class VirtualChassisTestCase(TestCase, ChangeLoggedFilterSetTests): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) +class CableBundleTestCase(TestCase, ChangeLoggedFilterSetTests): + queryset = CableBundle.objects.all() + filterset = CableBundleFilterSet + + @classmethod + def setUpTestData(cls): + cable_bundles = ( + CableBundle(name='Cable Bundle 1', description='foobar1'), + CableBundle(name='Cable Bundle 2', description='foobar2'), + CableBundle(name='Cable Bundle 3'), + ) + CableBundle.objects.bulk_create(cable_bundles) + + def test_q(self): + params = {'q': 'foobar1'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + def test_name(self): + params = {'name': ['Cable Bundle 1', 'Cable Bundle 2']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_description(self): + params = {'description': ['foobar1', 'foobar2']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + class CableTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = Cable.objects.all() filterset = CableFilterSet diff --git a/netbox/dcim/tests/test_forms.py b/netbox/dcim/tests/test_forms.py index 2f01525fc..3fe568384 100644 --- a/netbox/dcim/tests/test_forms.py +++ b/netbox/dcim/tests/test_forms.py @@ -11,6 +11,7 @@ from dcim.choices import ( from dcim.forms import * from dcim.models import * from ipam.models import ASN, RIR, VLAN +from utilities.exceptions import AbortRequest from utilities.forms.rendering import M2MAddRemoveFields from utilities.testing import create_test_device from virtualization.models import Cluster, ClusterGroup, ClusterType @@ -176,6 +177,88 @@ class DeviceTestCase(TestCase): self.assertIn('position', form.errors) +class VCPositionTokenFormTestCase(TestCase): + + @classmethod + def setUpTestData(cls): + Site.objects.create(name='Site VC 1', slug='site-vc-1') + manufacturer = Manufacturer.objects.create(name='Manufacturer VC 1', slug='manufacturer-vc-1') + device_type = DeviceType.objects.create( + manufacturer=manufacturer, model='Device Type VC 1', slug='device-type-vc-1' + ) + DeviceRole.objects.create(name='Device Role VC 1', slug='device-role-vc-1', color='ff0000') + InterfaceTemplate.objects.create( + device_type=device_type, + name='ge-{vc_position:0}/0/0', + type='1000base-t', + ) + VirtualChassis.objects.create(name='VC 1') + + def test_device_creation_in_vc_resolves_vc_position(self): + form = DeviceForm(data={ + 'name': 'Device VC Form 1', + 'role': DeviceRole.objects.first().pk, + 'tenant': None, + 'manufacturer': Manufacturer.objects.first().pk, + 'device_type': DeviceType.objects.first().pk, + 'site': Site.objects.first().pk, + 'rack': None, + 'face': None, + 'position': None, + 'platform': None, + 'status': DeviceStatusChoices.STATUS_ACTIVE, + 'virtual_chassis': VirtualChassis.objects.first().pk, + 'vc_position': 2, + }) + self.assertTrue(form.is_valid()) + device = form.save() + self.assertTrue(device.interfaces.filter(name='ge-2/0/0').exists()) + + def test_device_creation_not_in_vc_uses_fallback(self): + form = DeviceForm(data={ + 'name': 'Device VC Form 2', + 'role': DeviceRole.objects.first().pk, + 'tenant': None, + 'manufacturer': Manufacturer.objects.first().pk, + 'device_type': DeviceType.objects.first().pk, + 'site': Site.objects.first().pk, + 'rack': None, + 'face': None, + 'position': None, + 'platform': None, + 'status': DeviceStatusChoices.STATUS_ACTIVE, + }) + self.assertTrue(form.is_valid()) + device = form.save() + self.assertTrue(device.interfaces.filter(name='ge-0/0/0').exists()) + + def test_device_creation_duplicate_name_conflict(self): + # With conflict + device_type = DeviceType.objects.first() + # to generate conflicts create an interface that will exist + InterfaceTemplate.objects.create( + device_type=device_type, + name='ge-0/0/0', + type='1000base-t', + ) + form = DeviceForm(data={ + 'name': 'Device VC Form 3', + 'role': DeviceRole.objects.first().pk, + 'tenant': None, + 'manufacturer': Manufacturer.objects.first().pk, + 'device_type': device_type.pk, + 'site': Site.objects.first().pk, + 'rack': None, + 'face': None, + 'position': None, + 'platform': None, + 'status': DeviceStatusChoices.STATUS_ACTIVE, + }) + self.assertTrue(form.is_valid()) + with self.assertRaises(AbortRequest): + form.save() + + class FrontPortTestCase(TestCase): @classmethod diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index fe9adc603..3e3d1c526 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -749,6 +749,112 @@ class DeviceTestCase(TestCase): ).full_clean() +class DeviceBayTestCase(TestCase): + + @classmethod + def setUpTestData(cls): + site = Site.objects.create(name='Test Site 1', slug='test-site-1') + manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1') + + # Parent device type must support device bays (is_parent_device=True) + parent_device_type = DeviceType.objects.create( + manufacturer=manufacturer, + model='Parent Device Type', + slug='parent-device-type', + subdevice_role=SubdeviceRoleChoices.ROLE_PARENT + ) + # Child device type for installation + child_device_type = DeviceType.objects.create( + manufacturer=manufacturer, + model='Child Device Type', + slug='child-device-type', + u_height=0, + subdevice_role=SubdeviceRoleChoices.ROLE_CHILD + ) + device_role = DeviceRole.objects.create(name='Test Role 1', slug='test-role-1') + + cls.parent_device = Device.objects.create( + name='Parent Device', + device_type=parent_device_type, + role=device_role, + site=site + ) + cls.child_device = Device.objects.create( + name='Child Device', + device_type=child_device_type, + role=device_role, + site=site + ) + cls.child_device_2 = Device.objects.create( + name='Child Device 2', + device_type=child_device_type, + role=device_role, + site=site + ) + + def test_cannot_install_device_in_disabled_bay(self): + """ + Test that a device cannot be installed into a disabled DeviceBay. + """ + # Create a disabled device bay with a device being installed + device_bay = DeviceBay( + device=self.parent_device, + name='Disabled Bay', + enabled=False, + installed_device=self.child_device + ) + + with self.assertRaises(ValidationError) as cm: + device_bay.clean() + + self.assertIn('installed_device', cm.exception.message_dict) + self.assertIn('disabled device bay', str(cm.exception.message_dict['installed_device'])) + + def test_can_disable_bay_with_existing_device(self): + """ + Test that disabling a bay that already has a device installed does NOT raise an error + (same installed_device_id). + """ + # First, create an enabled device bay with a device installed + device_bay = DeviceBay.objects.create( + device=self.parent_device, + name='Bay To Disable', + enabled=True, + installed_device=self.child_device + ) + + # Now disable the bay while keeping the same installed device + device_bay.enabled = False + # This should NOT raise a ValidationError + device_bay.clean() + device_bay.save() + + device_bay.refresh_from_db() + self.assertFalse(device_bay.enabled) + self.assertEqual(device_bay.installed_device, self.child_device) + + def test_cannot_change_installed_device_in_disabled_bay(self): + """ + Test that changing the installed device in a disabled bay raises a ValidationError. + """ + # Create an enabled device bay with a device installed + device_bay = DeviceBay.objects.create( + device=self.parent_device, + name='Bay With Device', + enabled=True, + installed_device=self.child_device + ) + + # Disable the bay and try to change the installed device + device_bay.enabled = False + device_bay.installed_device = self.child_device_2 + + with self.assertRaises(ValidationError) as cm: + device_bay.clean() + + self.assertIn('installed_device', cm.exception.message_dict) + + class ModuleBayTestCase(TestCase): @classmethod @@ -930,12 +1036,15 @@ class ModuleBayTestCase(TestCase): nested_bay = module.modulebays.get(name='Sub-bay 1-1') self.assertEqual(nested_bay.position, '1-1') - @tag('regression') # #20474 - def test_single_module_token_at_nested_depth(self): + # + # Position inheritance tests (#19796) + # + + def test_position_inheritance_depth_2(self): """ - A module type with a single {module} token should install at depth > 1 - without raising a token count mismatch error, resolving to the immediate - parent bay's position. + A module bay with position '{module}/2' under a parent bay with position '1' + should resolve to position '1/2'. A single {module} in the interface template + should then resolve to '1/2'. """ manufacturer = Manufacturer.objects.first() site = Site.objects.first() @@ -943,33 +1052,33 @@ class ModuleBayTestCase(TestCase): device_type = DeviceType.objects.create( manufacturer=manufacturer, - model='Chassis with Rear Card', - slug='chassis-with-rear-card' + model='Chassis for Inheritance', + slug='chassis-for-inheritance' ) ModuleBayTemplate.objects.create( device_type=device_type, - name='Rear card slot', + name='Line card slot 1', position='1' ) - rear_card_type = ModuleType.objects.create( + line_card_type = ModuleType.objects.create( manufacturer=manufacturer, - model='Rear Card' + model='Line Card with Inherited Bays' ) ModuleBayTemplate.objects.create( - module_type=rear_card_type, - name='SFP slot 1', - position='1' + module_type=line_card_type, + name='SFP bay {module}/1', + position='{module}/1' ) ModuleBayTemplate.objects.create( - module_type=rear_card_type, - name='SFP slot 2', - position='2' + module_type=line_card_type, + name='SFP bay {module}/2', + position='{module}/2' ) sfp_type = ModuleType.objects.create( manufacturer=manufacturer, - model='SFP Module' + model='SFP with Inherited Path' ) InterfaceTemplate.objects.create( module_type=sfp_type, @@ -978,20 +1087,20 @@ class ModuleBayTestCase(TestCase): ) device = Device.objects.create( - name='Test Chassis', + name='Inheritance Chassis', device_type=device_type, role=device_role, site=site ) - rear_card_bay = device.modulebays.get(name='Rear card slot') - rear_card = Module.objects.create( + lc_bay = device.modulebays.get(name='Line card slot 1') + line_card = Module.objects.create( device=device, - module_bay=rear_card_bay, - module_type=rear_card_type + module_bay=lc_bay, + module_type=line_card_type ) - sfp_bay = rear_card.modulebays.get(name='SFP slot 2') + sfp_bay = line_card.modulebays.get(name='SFP bay 1/2') sfp_module = Module.objects.create( device=device, module_bay=sfp_bay, @@ -999,7 +1108,200 @@ class ModuleBayTestCase(TestCase): ) interface = sfp_module.interfaces.first() - self.assertEqual(interface.name, 'SFP 2') + self.assertEqual(interface.name, 'SFP 1/2') + + def test_position_inheritance_depth_3(self): + """ + Position inheritance at depth 3: positions should chain through the tree. + """ + manufacturer = Manufacturer.objects.first() + site = Site.objects.first() + device_role = DeviceRole.objects.first() + + device_type = DeviceType.objects.create( + manufacturer=manufacturer, + model='Deep Chassis', + slug='deep-chassis' + ) + ModuleBayTemplate.objects.create( + device_type=device_type, + name='Slot A', + position='A' + ) + + mid_type = ModuleType.objects.create( + manufacturer=manufacturer, + model='Mid Module' + ) + ModuleBayTemplate.objects.create( + module_type=mid_type, + name='Sub {module}-1', + position='{module}-1' + ) + + leaf_type = ModuleType.objects.create( + manufacturer=manufacturer, + model='Leaf Module' + ) + InterfaceTemplate.objects.create( + module_type=leaf_type, + name='Port {module}', + type=InterfaceTypeChoices.TYPE_1GE_FIXED + ) + + device = Device.objects.create( + name='Deep Device', + device_type=device_type, + role=device_role, + site=site + ) + + slot_a = device.modulebays.get(name='Slot A') + mid_module = Module.objects.create( + device=device, + module_bay=slot_a, + module_type=mid_type + ) + + sub_bay = mid_module.modulebays.get(name='Sub A-1') + self.assertEqual(sub_bay.position, 'A-1') + + leaf_module = Module.objects.create( + device=device, + module_bay=sub_bay, + module_type=leaf_type + ) + + interface = leaf_module.interfaces.first() + self.assertEqual(interface.name, 'Port A-1') + + def test_position_inheritance_custom_separator(self): + """ + Users control the separator through the position field template. + Using '.' instead of '/' should work correctly. + """ + manufacturer = Manufacturer.objects.first() + site = Site.objects.first() + device_role = DeviceRole.objects.first() + + device_type = DeviceType.objects.create( + manufacturer=manufacturer, + model='Dot Separator Chassis', + slug='dot-separator-chassis' + ) + ModuleBayTemplate.objects.create( + device_type=device_type, + name='Bay 1', + position='1' + ) + + card_type = ModuleType.objects.create( + manufacturer=manufacturer, + model='Card with Dot Separator' + ) + ModuleBayTemplate.objects.create( + module_type=card_type, + name='Port {module}.1', + position='{module}.1' + ) + + sfp_type = ModuleType.objects.create( + manufacturer=manufacturer, + model='SFP Dot' + ) + InterfaceTemplate.objects.create( + module_type=sfp_type, + name='eth{module}', + type=InterfaceTypeChoices.TYPE_10GE_SFP_PLUS + ) + + device = Device.objects.create( + name='Dot Device', + device_type=device_type, + role=device_role, + site=site + ) + + bay = device.modulebays.get(name='Bay 1') + card = Module.objects.create( + device=device, + module_bay=bay, + module_type=card_type + ) + + port_bay = card.modulebays.get(name='Port 1.1') + sfp = Module.objects.create( + device=device, + module_bay=port_bay, + module_type=sfp_type + ) + + interface = sfp.interfaces.first() + self.assertEqual(interface.name, 'eth1.1') + + def test_multi_token_backwards_compat(self): + """ + Multi-token {module}/{module} at matching depth should still resolve + level-by-level (backwards compatibility). + """ + manufacturer = Manufacturer.objects.first() + site = Site.objects.first() + device_role = DeviceRole.objects.first() + + device_type = DeviceType.objects.create( + manufacturer=manufacturer, + model='Multi Token Chassis', + slug='multi-token-chassis' + ) + ModuleBayTemplate.objects.create( + device_type=device_type, + name='Slot 1', + position='1' + ) + + card_type = ModuleType.objects.create( + manufacturer=manufacturer, + model='Card for Multi Token' + ) + ModuleBayTemplate.objects.create( + module_type=card_type, + name='Port 1', + position='2' + ) + + iface_type = ModuleType.objects.create( + manufacturer=manufacturer, + model='Interface Module Multi Token' + ) + InterfaceTemplate.objects.create( + module_type=iface_type, + name='Gi{module}/{module}', + type=InterfaceTypeChoices.TYPE_1GE_FIXED + ) + + device = Device.objects.create( + name='Multi Token Device', + device_type=device_type, + role=device_role, + site=site + ) + + slot = device.modulebays.get(name='Slot 1') + card = Module.objects.create( + device=device, + module_bay=slot, + module_type=card_type + ) + + port = card.modulebays.get(name='Port 1') + iface_module = Module.objects.create( + device=device, + module_bay=port, + module_type=iface_type + ) + + interface = iface_module.interfaces.first() + self.assertEqual(interface.name, 'Gi1/2') @tag('regression') # #20912 def test_module_bay_parent_cleared_when_module_removed(self): @@ -1163,6 +1465,25 @@ class ModuleBayTestCase(TestCase): self.assertEqual(RearPort.objects.filter(module=module).count(), 1) self.assertEqual(PortMapping.objects.filter(front_port__module=module).count(), 0) + def test_cannot_install_module_in_disabled_bay(self): + """ + Test that a Module cannot be installed into a disabled ModuleBay. + """ + device = Device.objects.first() + manufacturer = Manufacturer.objects.first() + module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Test Module Type Disabled') + + # Create a disabled module bay + disabled_bay = ModuleBay.objects.create(device=device, name='Disabled Bay', enabled=False) + + # Attempt to install a module into the disabled bay + module = Module(device=device, module_bay=disabled_bay, module_type=module_type) + with self.assertRaises(ValidationError) as cm: + module.clean() + + self.assertIn('module_bay', cm.exception.message_dict) + self.assertIn('disabled module bay', str(cm.exception.message_dict['module_bay'])) + class CableTestCase(TestCase): @@ -1584,6 +1905,167 @@ class VirtualChassisTestCase(TestCase): device2.full_clean() +class VCPositionTokenTestCase(TestCase): + + @classmethod + def setUpTestData(cls): + Site.objects.create(name='Test Site 1', slug='test-site-1') + manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1') + DeviceType.objects.create( + manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1' + ) + ModuleType.objects.create( + manufacturer=manufacturer, model='Test Module Type 1' + ) + DeviceRole.objects.create(name='Test Role 1', slug='test-role-1') + + def test_vc_position_token_in_vc(self): + site = Site.objects.first() + device_type = DeviceType.objects.first() + module_type = ModuleType.objects.first() + device_role = DeviceRole.objects.first() + + InterfaceTemplate.objects.create( + module_type=module_type, + name='ge-{vc_position}/{module}/0', + type='1000base-t', + ) + vc = VirtualChassis.objects.create(name='Test VC 1') + device = Device.objects.create( + name='Device VC 1', device_type=device_type, role=device_role, + site=site, virtual_chassis=vc, vc_position=8, + ) + module_bay = ModuleBay.objects.create(device=device, name='Bay 1', position='1') + Module.objects.create(device=device, module_bay=module_bay, module_type=module_type) + + interface = device.interfaces.get(name='ge-8/1/0') + self.assertEqual(interface.name, 'ge-8/1/0') + + def test_vc_position_token_not_in_vc_default_fallback(self): + site = Site.objects.first() + device_type = DeviceType.objects.first() + module_type = ModuleType.objects.first() + device_role = DeviceRole.objects.first() + + InterfaceTemplate.objects.create( + module_type=module_type, + name='ge-{vc_position}/{module}/0', + type='1000base-t', + ) + device = Device.objects.create( + name='Device NoVC 1', device_type=device_type, role=device_role, + site=site, + ) + module_bay = ModuleBay.objects.create(device=device, name='Bay 1', position='1') + Module.objects.create(device=device, module_bay=module_bay, module_type=module_type) + + interface = device.interfaces.get(name='ge-0/1/0') + self.assertEqual(interface.name, 'ge-0/1/0') + + def test_vc_position_token_explicit_fallback(self): + site = Site.objects.first() + device_type = DeviceType.objects.first() + module_type = ModuleType.objects.first() + device_role = DeviceRole.objects.first() + + InterfaceTemplate.objects.create( + module_type=module_type, + name='ge-{vc_position:18}/{module}/0', + type='1000base-t', + ) + device = Device.objects.create( + name='Device NoVC 2', device_type=device_type, role=device_role, + site=site, + ) + module_bay = ModuleBay.objects.create(device=device, name='Bay 1', position='1') + Module.objects.create(device=device, module_bay=module_bay, module_type=module_type) + + interface = device.interfaces.get(name='ge-18/1/0') + self.assertEqual(interface.name, 'ge-18/1/0') + + def test_vc_position_token_explicit_fallback_ignored_when_in_vc(self): + site = Site.objects.first() + device_type = DeviceType.objects.first() + module_type = ModuleType.objects.first() + device_role = DeviceRole.objects.first() + + InterfaceTemplate.objects.create( + module_type=module_type, + name='ge-{vc_position:99}/{module}/0', + type='1000base-t', + ) + vc = VirtualChassis.objects.create(name='Test VC 2') + device = Device.objects.create( + name='Device VC 2', device_type=device_type, role=device_role, + site=site, virtual_chassis=vc, vc_position=2, + ) + module_bay = ModuleBay.objects.create(device=device, name='Bay 1', position='1') + Module.objects.create(device=device, module_bay=module_bay, module_type=module_type) + + interface = device.interfaces.get(name='ge-2/1/0') + self.assertEqual(interface.name, 'ge-2/1/0') + + def test_vc_position_token_device_type_template(self): + site = Site.objects.first() + device_type = DeviceType.objects.first() + device_role = DeviceRole.objects.first() + + InterfaceTemplate.objects.create( + device_type=device_type, + name='ge-{vc_position:0}/0/0', + type='1000base-t', + ) + vc = VirtualChassis.objects.create(name='Test VC 3') + device = Device.objects.create( + name='Device VC 3', device_type=device_type, role=device_role, + site=site, virtual_chassis=vc, vc_position=3, + ) + + interface = device.interfaces.get(name='ge-3/0/0') + self.assertEqual(interface.name, 'ge-3/0/0') + + def test_vc_position_token_device_type_template_not_in_vc(self): + site = Site.objects.first() + device_type = DeviceType.objects.first() + device_role = DeviceRole.objects.first() + + InterfaceTemplate.objects.create( + device_type=device_type, + name='ge-{vc_position:0}/0/0', + type='1000base-t', + ) + device = Device.objects.create( + name='Device NoVC 3', device_type=device_type, role=device_role, + site=site, + ) + + interface = device.interfaces.get(name='ge-0/0/0') + self.assertEqual(interface.name, 'ge-0/0/0') + + def test_vc_position_token_label_resolution(self): + site = Site.objects.first() + device_type = DeviceType.objects.first() + module_type = ModuleType.objects.first() + device_role = DeviceRole.objects.first() + + InterfaceTemplate.objects.create( + module_type=module_type, + name='ge-{vc_position}/{module}/0', + label='Member {vc_position:0} / Slot {module}', + type='1000base-t', + ) + vc = VirtualChassis.objects.create(name='Test VC 4') + device = Device.objects.create( + name='Device VC 4', device_type=device_type, role=device_role, + site=site, virtual_chassis=vc, vc_position=2, + ) + module_bay = ModuleBay.objects.create(device=device, name='Bay 1', position='1') + Module.objects.create(device=device, module_bay=module_bay, module_type=module_type) + + interface = device.interfaces.get(name='ge-2/1/0') + self.assertEqual(interface.label, 'Member 2 / Slot 1') + + class SiteSignalTestCase(TestCase): @tag('regression') diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index 2d41cc611..a9e715020 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -1,13 +1,18 @@ +import csv import json from decimal import Decimal +from io import StringIO from zoneinfo import ZoneInfo import yaml +from django.contrib.contenttypes.models import ContentType +from django.http import StreamingHttpResponse from django.test import override_settings, tag from django.urls import reverse from netaddr import EUI -from core.models import ObjectType +from core.choices import ObjectChangeActionChoices +from core.models import ObjectChange, ObjectType from dcim.choices import * from dcim.constants import * from dcim.models import * @@ -289,6 +294,47 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase): } +class RackGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase): + model = RackGroup + + @classmethod + def setUpTestData(cls): + + rack_groups = ( + RackGroup(name='Rack Group 1', slug='rack-group-1'), + RackGroup(name='Rack Group 2', slug='rack-group-2'), + RackGroup(name='Rack Group 3', slug='rack-group-3'), + ) + RackGroup.objects.bulk_create(rack_groups) + + tags = create_tags('Alpha', 'Bravo', 'Charlie') + + cls.form_data = { + 'name': 'Rack Group X', + 'slug': 'rack-group-x', + 'description': 'New group', + 'tags': [t.pk for t in tags], + } + + cls.csv_data = ( + "name,slug,description", + "Rack Group 4,rack-group-4,Fourth group", + "Rack Group 5,rack-group-5,Fifth group", + "Rack Group 6,rack-group-6,", + ) + + cls.csv_update_data = ( + "id,name,description", + f"{rack_groups[0].pk},Rack Group 7,New description7", + f"{rack_groups[1].pk},Rack Group 8,New description8", + f"{rack_groups[2].pk},Rack Group 9,New description9", + ) + + cls.bulk_edit_data = { + 'description': 'New description', + } + + class RackRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase): model = RackRole @@ -494,6 +540,12 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase): for location in locations: location.save() + rack_groups = ( + RackGroup(name='Rack Group 1', slug='rack-group-1'), + RackGroup(name='Rack Group 2', slug='rack-group-2'), + ) + RackGroup.objects.bulk_create(rack_groups) + rackroles = ( RackRole(name='Rack Role 1', slug='rack-role-1'), RackRole(name='Rack Role 2', slug='rack-role-2'), @@ -501,8 +553,8 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase): RackRole.objects.bulk_create(rackroles) racks = ( - Rack(name='Rack 1', site=sites[0]), - Rack(name='Rack 2', site=sites[0]), + Rack(name='Rack 1', site=sites[0], group=rack_groups[0], role=rackroles[0]), + Rack(name='Rack 2', site=sites[0], group=rack_groups[1]), Rack(name='Rack 3', site=sites[0]), ) Rack.objects.bulk_create(racks) @@ -514,6 +566,7 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'facility_id': 'Facility X', 'site': sites[1].pk, 'location': locations[1].pk, + 'group': rack_groups[1].pk, 'tenant': None, 'status': RackStatusChoices.STATUS_PLANNED, 'role': rackroles[1].pk, @@ -535,10 +588,10 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase): } cls.csv_data = ( - "site,location,name,status,width,u_height,weight,max_weight,weight_unit", - "Site 1,,Rack 4,active,19,42,100,2000,kg", - "Site 1,Location 1,Rack 5,active,19,42,100,2000,kg", - "Site 2,Location 2,Rack 6,active,19,42,100,2000,kg", + "site,location,group,name,status,width,u_height,weight,max_weight,weight_unit", + "Site 1,,,Rack 4,active,19,42,100,2000,kg", + "Site 1,Location 1,Rack Group 1,Rack 5,active,19,42,100,2000,kg", + "Site 2,Location 2,Rack Group 2,Rack 6,active,19,42,100,2000,kg", ) cls.csv_update_data = ( @@ -551,6 +604,7 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase): cls.bulk_edit_data = { 'site': sites[1].pk, 'location': locations[1].pk, + 'group': rack_groups[1].pk, 'tenant': None, 'status': RackStatusChoices.STATUS_DEPRECATED, 'role': rackroles[1].pk, @@ -1141,6 +1195,7 @@ console-ports: self.assertHttpStatus(response, 200) self.assertContains(response, "Record 1 console-ports[1]: Must be a dictionary.") + @override_settings(STREAMING_EXPORTS=True) def test_export_objects(self): url = reverse('dcim:devicetype_list') self.add_permissions('dcim.view_devicetype') @@ -1153,10 +1208,15 @@ console-ports: self.assertEqual(data[0]['manufacturer'], 'Manufacturer 1') self.assertEqual(data[0]['model'], 'Device Type 1') - # Test table-based export + # Test table-based export (streams row-by-row) response = self.client.get(f'{url}?export=table') self.assertHttpStatus(response, 200) self.assertEqual(response.get('Content-Type'), 'text/csv; charset=utf-8') + self.assertIsInstance(response, StreamingHttpResponse) + content = b''.join(response.streaming_content).decode('utf-8') + rows = list(csv.reader(StringIO(content))) + self.assertGreater(len(rows), 1) + self.assertEqual(len(rows) - 1, DeviceType.objects.count()) class ModuleTypeTestCase(ViewTestCases.PrimaryObjectViewTestCase): @@ -1522,6 +1582,7 @@ module-bays: self.assertEqual(mb1.name, 'Module Bay 1') self.assertEqual(mb1.position, '1') + @override_settings(STREAMING_EXPORTS=True) def test_export_objects(self): url = reverse('dcim:moduletype_list') self.add_permissions('dcim.view_moduletype') @@ -1534,10 +1595,15 @@ module-bays: self.assertEqual(data[0]['manufacturer'], 'Manufacturer 1') self.assertEqual(data[0]['model'], 'Module Type 1') - # Test table-based export + # Test table-based export (streams row-by-row) response = self.client.get(f'{url}?export=table') self.assertHttpStatus(response, 200) self.assertEqual(response.get('Content-Type'), 'text/csv; charset=utf-8') + self.assertIsInstance(response, StreamingHttpResponse) + content = b''.join(response.streaming_content).decode('utf-8') + rows = list(csv.reader(StringIO(content))) + self.assertGreater(len(rows), 1) + self.assertEqual(len(rows) - 1, ModuleType.objects.count()) class ModuleTypeProfileTestCase(ViewTestCases.OrganizationalObjectViewTestCase): @@ -2775,6 +2841,50 @@ class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase): f"{console_ports[2].pk},Console Port 9,New description9", ) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[]) + def test_bulk_add_components_with_changelog_message(self): + device1 = Device.objects.get(name='Device 1') + device2 = create_test_device('Device 2') + changelog_message = 'Bulk-created console ports' + + obj_perm = ObjectPermission( + name='Test permission', + actions=['add'], + ) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) + + request = { + 'path': reverse('dcim:device_bulk_add_consoleport'), + 'data': post_data({ + 'pk': [device1.pk, device2.pk], + 'name': 'Console Port Bulk', + 'type': ConsolePortTypeChoices.TYPE_RJ45, + 'description': 'Bulk-created console port', + 'changelog_message': changelog_message, + '_create': True, + }), + } + + initial_count = self._get_queryset().count() + response = self.client.post(**request) + self.assertHttpStatus(response, 302) + self.assertEqual(initial_count + 2, self._get_queryset().count()) + + created_ports = list(ConsolePort.objects.filter(name='Console Port Bulk').order_by('device_id')) + self.assertEqual(len(created_ports), 2) + self.assertEqual([port.device_id for port in created_ports], [device1.pk, device2.pk]) + + objectchanges = ObjectChange.objects.filter( + action=ObjectChangeActionChoices.ACTION_CREATE, + changed_object_type=ContentType.objects.get_for_model(ConsolePort), + changed_object_id__in=[port.pk for port in created_ports], + ) + self.assertEqual(objectchanges.count(), 2) + for objectchange in objectchanges: + self.assertEqual(objectchange.message, changelog_message) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) def test_trace(self): consoleport = ConsolePort.objects.first() @@ -3581,6 +3691,45 @@ class InventoryItemRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase): } +class CableBundleTestCase(ViewTestCases.PrimaryObjectViewTestCase): + model = CableBundle + + @classmethod + def setUpTestData(cls): + cable_bundles = ( + CableBundle(name='Cable Bundle 1'), + CableBundle(name='Cable Bundle 2'), + CableBundle(name='Cable Bundle 3'), + ) + CableBundle.objects.bulk_create(cable_bundles) + + tags = create_tags('Alpha', 'Bravo', 'Charlie') + + cls.form_data = { + 'name': 'Cable Bundle X', + 'description': 'A test bundle', + 'tags': [t.pk for t in tags], + } + + cls.csv_data = ( + "name,description", + "Cable Bundle 4,Fourth bundle", + "Cable Bundle 5,Fifth bundle", + "Cable Bundle 6,", + ) + + cls.csv_update_data = ( + "id,name,description", + f"{cable_bundles[0].pk},Cable Bundle 7,New description7", + f"{cable_bundles[1].pk},Cable Bundle 8,New description8", + f"{cable_bundles[2].pk},Cable Bundle 9,New description9", + ) + + cls.bulk_edit_data = { + 'description': 'New description', + } + + # TODO: Change base class to PrimaryObjectViewTestCase # Blocked by lack of common creation view for cables (termination A must be initialized) class CableTestCase( diff --git a/netbox/dcim/ui/panels.py b/netbox/dcim/ui/panels.py index 3681f4dac..29d0a1ed3 100644 --- a/netbox/dcim/ui/panels.py +++ b/netbox/dcim/ui/panels.py @@ -1,5 +1,4 @@ from django.contrib.contenttypes.models import ContentType -from django.template.loader import render_to_string from django.utils.translation import gettext_lazy as _ from netbox.ui import actions, attrs, panels @@ -45,6 +44,7 @@ class RackPanel(panels.ObjectAttributesPanel): region = attrs.NestedObjectAttr('site.region', linkify=True) site = attrs.RelatedObjectAttr('site', linkify=True, grouped_by='group') location = attrs.NestedObjectAttr('location', linkify=True) + group = attrs.RelatedObjectAttr('group', linkify=True, label=_('Rack group')) name = attrs.TextAttr('name') facility_id = attrs.TextAttr('facility_id', label=_('Facility ID')) tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') @@ -71,9 +71,10 @@ class RackRolePanel(panels.OrganizationalObjectPanel): class RackReservationPanel(panels.ObjectAttributesPanel): units = attrs.TextAttr('unit_list') + unit_count = attrs.TextAttr('unit_count', label=_("Total U's")) status = attrs.ChoiceAttr('status') tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') - user = attrs.RelatedObjectAttr('user') + user = attrs.RelatedObjectAttr('user', linkify=True) description = attrs.TextAttr('description') @@ -218,8 +219,8 @@ class PowerPortPanel(panels.ObjectAttributesPanel): label = attrs.TextAttr('label') type = attrs.ChoiceAttr('type') description = attrs.TextAttr('description') - maximum_draw = attrs.TextAttr('maximum_draw') - allocated_draw = attrs.TextAttr('allocated_draw') + maximum_draw = attrs.TextAttr('maximum_draw', format_string='{}W') + allocated_draw = attrs.TextAttr('allocated_draw', format_string='{}W') class PowerOutletPanel(panels.ObjectAttributesPanel): @@ -242,7 +243,7 @@ class FrontPortPanel(panels.ObjectAttributesPanel): label = attrs.TextAttr('label') type = attrs.ChoiceAttr('type') color = attrs.ColorAttr('color') - positions = attrs.TextAttr('positions') + positions = attrs.NumericAttr('positions') description = attrs.TextAttr('description') @@ -253,7 +254,7 @@ class RearPortPanel(panels.ObjectAttributesPanel): label = attrs.TextAttr('label') type = attrs.ChoiceAttr('type') color = attrs.ColorAttr('color') - positions = attrs.TextAttr('positions') + positions = attrs.NumericAttr('positions') description = attrs.TextAttr('description') @@ -266,6 +267,15 @@ class ModuleBayPanel(panels.ObjectAttributesPanel): description = attrs.TextAttr('description') +class InstalledModulePanel(panels.ObjectAttributesPanel): + title = _('Installed Module') + module = attrs.RelatedObjectAttr('installed_module', linkify=True) + manufacturer = attrs.RelatedObjectAttr('installed_module.module_type.manufacturer', linkify=True) + module_type = attrs.RelatedObjectAttr('installed_module.module_type', linkify=True) + serial = attrs.TextAttr('installed_module.serial', label=_('Serial number'), style='font-monospace') + asset_tag = attrs.TextAttr('installed_module.asset_tag', style='font-monospace') + + class DeviceBayPanel(panels.ObjectAttributesPanel): device = attrs.RelatedObjectAttr('device', linkify=True) name = attrs.TextAttr('name') @@ -273,6 +283,12 @@ class DeviceBayPanel(panels.ObjectAttributesPanel): description = attrs.TextAttr('description') +class InstalledDevicePanel(panels.ObjectAttributesPanel): + title = _('Installed Device') + device = attrs.RelatedObjectAttr('installed_device', linkify=True) + device_type = attrs.RelatedObjectAttr('installed_device.device_type') + + class InventoryItemPanel(panels.ObjectAttributesPanel): device = attrs.RelatedObjectAttr('device', linkify=True) parent = attrs.RelatedObjectAttr('parent', linkify=True, label=_('Parent item')) @@ -297,6 +313,7 @@ class CablePanel(panels.ObjectAttributesPanel): status = attrs.ChoiceAttr('status') profile = attrs.ChoiceAttr('profile') tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') + bundle = attrs.RelatedObjectAttr('bundle', linkify=True) label = attrs.TextAttr('label') description = attrs.TextAttr('description') color = attrs.ColorAttr('color') @@ -390,10 +407,6 @@ class ConnectionPanel(panels.ObjectPanel): 'show_endpoints': self.show_endpoints, } - def render(self, context): - ctx = self.get_context(context) - return render_to_string(self.template_name, ctx, request=ctx.get('request')) - class InventoryItemsPanel(panels.ObjectPanel): """ @@ -411,10 +424,6 @@ class InventoryItemsPanel(panels.ObjectPanel): ), ] - def render(self, context): - ctx = self.get_context(context) - return render_to_string(self.template_name, ctx, request=ctx.get('request')) - class VirtualChassisMembersPanel(panels.ObjectPanel): """ @@ -448,10 +457,8 @@ class VirtualChassisMembersPanel(panels.ObjectPanel): 'vc_members': context.get('vc_members'), } - def render(self, context): - if not context.get('vc_members'): - return '' - return super().render(context) + def should_render(self, context): + return bool(context.get('vc_members')) class PowerUtilizationPanel(panels.ObjectPanel): @@ -467,11 +474,9 @@ class PowerUtilizationPanel(panels.ObjectPanel): 'vc_members': context.get('vc_members'), } - def render(self, context): + def should_render(self, context): obj = context['object'] - if not obj.powerports.exists() or not obj.poweroutlets.exists(): - return '' - return super().render(context) + return obj.powerports.exists() and obj.poweroutlets.exists() class InterfacePanel(panels.ObjectAttributesPanel): @@ -482,7 +487,7 @@ class InterfacePanel(panels.ObjectAttributesPanel): type = attrs.ChoiceAttr('type') speed = attrs.TemplatedAttr('speed', template_name='dcim/interface/attrs/speed.html', label=_('Speed')) duplex = attrs.ChoiceAttr('duplex') - mtu = attrs.TextAttr('mtu', label=_('MTU')) + mtu = attrs.NumericAttr('mtu', label=_('MTU')) enabled = attrs.BooleanAttr('enabled') mgmt_only = attrs.BooleanAttr('mgmt_only', label=_('Management only')) description = attrs.TextAttr('description') @@ -491,7 +496,7 @@ class InterfacePanel(panels.ObjectAttributesPanel): mode = attrs.ChoiceAttr('mode', label=_('802.1Q mode')) qinq_svlan = attrs.RelatedObjectAttr('qinq_svlan', linkify=True, label=_('Q-in-Q SVLAN')) untagged_vlan = attrs.RelatedObjectAttr('untagged_vlan', linkify=True, label=_('Untagged VLAN')) - tx_power = attrs.TextAttr('tx_power', label=_('Transmit power (dBm)')) + tx_power = attrs.TextAttr('tx_power', label=_('Transmit power'), format_string='{} dBm') tunnel = attrs.RelatedObjectAttr('tunnel_termination.tunnel', linkify=True, label=_('Tunnel')) l2vpn = attrs.RelatedObjectAttr('l2vpn_termination.l2vpn', linkify=True, label=_('L2VPN')) @@ -524,12 +529,9 @@ class InterfaceConnectionPanel(panels.ObjectPanel): template_name = 'dcim/panels/interface_connection.html' title = _('Connection') - def render(self, context): + def should_render(self, context): obj = context.get('object') - if obj and obj.is_virtual: - return '' - ctx = self.get_context(context) - return render_to_string(self.template_name, ctx, request=ctx.get('request')) + return False if (obj is None or obj.is_virtual) else True class VirtualCircuitPanel(panels.ObjectPanel): @@ -539,12 +541,11 @@ class VirtualCircuitPanel(panels.ObjectPanel): template_name = 'dcim/panels/interface_virtual_circuit.html' title = _('Virtual Circuit') - def render(self, context): + def should_render(self, context): obj = context.get('object') if not obj or not obj.is_virtual or not hasattr(obj, 'virtual_circuit_termination'): - return '' - ctx = self.get_context(context) - return render_to_string(self.template_name, ctx, request=ctx.get('request')) + return False + return True class InterfaceWirelessPanel(panels.ObjectPanel): @@ -554,12 +555,9 @@ class InterfaceWirelessPanel(panels.ObjectPanel): template_name = 'dcim/panels/interface_wireless.html' title = _('Wireless') - def render(self, context): + def should_render(self, context): obj = context.get('object') - if not obj or not obj.is_wireless: - return '' - ctx = self.get_context(context) - return render_to_string(self.template_name, ctx, request=ctx.get('request')) + return False if (obj is None or not obj.is_wireless) else True class WirelessLANsPanel(panels.ObjectPanel): @@ -569,9 +567,6 @@ class WirelessLANsPanel(panels.ObjectPanel): template_name = 'dcim/panels/interface_wireless_lans.html' title = _('Wireless LANs') - def render(self, context): + def should_render(self, context): obj = context.get('object') - if not obj or not obj.is_wireless: - return '' - ctx = self.get_context(context) - return render_to_string(self.template_name, ctx, request=ctx.get('request')) + return False if (obj is None or not obj.is_wireless) else True diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index b24c81069..86cf79924 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -19,6 +19,9 @@ urlpatterns = [ path('locations/', include(get_model_urls('dcim', 'location', detail=False))), path('locations//', include(get_model_urls('dcim', 'location'))), + path('rack-groups/', include(get_model_urls('dcim', 'rackgroup', detail=False))), + path('rack-groups//', include(get_model_urls('dcim', 'rackgroup'))), + path('rack-roles/', include(get_model_urls('dcim', 'rackrole', detail=False))), path('rack-roles//', include(get_model_urls('dcim', 'rackrole'))), @@ -150,6 +153,9 @@ urlpatterns = [ path('cables/', include(get_model_urls('dcim', 'cable', detail=False))), path('cables//', include(get_model_urls('dcim', 'cable'))), + path('cable-bundles/', include(get_model_urls('dcim', 'cablebundle', detail=False))), + path('cable-bundles//', include(get_model_urls('dcim', 'cablebundle'))), + # Console/power/interface connections (read-only) path('console-connections/', views.ConsoleConnectionsListView.as_view(), name='console_connections_list'), path('power-connections/', views.PowerConnectionsListView.as_view(), name='power_connections_list'), diff --git a/netbox/dcim/utils.py b/netbox/dcim/utils.py index fc4bae02c..ce4dd15ef 100644 --- a/netbox/dcim/utils.py +++ b/netbox/dcim/utils.py @@ -8,42 +8,19 @@ from django.utils.translation import gettext as _ from dcim.constants import MODULE_TOKEN -def compile_path_node(ct_id, object_id): - return f'{ct_id}:{object_id}' - - -def decompile_path_node(repr): - ct_id, object_id = repr.split(':') - return int(ct_id), int(object_id) - - -def object_to_path_node(obj): - """ - Return a representation of an object suitable for inclusion in a CablePath path. Node representation is in the - form :. - """ - ct = ContentType.objects.get_for_model(obj) - return compile_path_node(ct.pk, obj.pk) - - -def path_node_to_object(repr): - """ - Given the string representation of a path node, return the corresponding instance. If the object no longer - exists, return None. - """ - ct_id, object_id = decompile_path_node(repr) - ct = ContentType.objects.get_for_id(ct_id) - return ct.model_class().objects.filter(pk=object_id).first() - - def get_module_bay_positions(module_bay): """ Given a module bay, traverse up the module hierarchy and return - a list of bay position strings from root to leaf. + a list of bay position strings from root to leaf, resolving any + {module} tokens in each position using the parent position + (position inheritance). """ positions = [] while module_bay: - positions.append(module_bay.position) + pos = module_bay.position or '' + if positions and MODULE_TOKEN in pos: + pos = pos.replace(MODULE_TOKEN, positions[-1]) + positions.append(pos) if module_bay.module: module_bay = module_bay.module.module_bay else: @@ -81,6 +58,34 @@ def resolve_module_placeholder(value, positions): ) +def compile_path_node(ct_id, object_id): + return f'{ct_id}:{object_id}' + + +def decompile_path_node(repr): + ct_id, object_id = repr.split(':') + return int(ct_id), int(object_id) + + +def object_to_path_node(obj): + """ + Return a representation of an object suitable for inclusion in a CablePath path. Node representation is in the + form :. + """ + ct = ContentType.objects.get_for_model(obj) + return compile_path_node(ct.pk, obj.pk) + + +def path_node_to_object(repr): + """ + Given the string representation of a path node, return the corresponding instance. If the object no longer + exists, return None. + """ + ct_id, object_id = decompile_path_node(repr) + ct = ContentType.objects.get_for_id(ct_id) + return ct.model_class().objects.filter(pk=object_id).first() + + def create_cablepaths(objects): """ Create CablePaths for all paths originating from the specified set of nodes. diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index dd3531b84..63666cce0 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -3,7 +3,7 @@ from django.contrib import messages from django.contrib.contenttypes.models import ContentType from django.core.paginator import EmptyPage, PageNotAnInteger from django.db import router, transaction -from django.db.models import Prefetch +from django.db.models import Func, IntegerField, Prefetch from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse @@ -27,7 +27,6 @@ from netbox.ui.panels import ( NestedGroupObjectPanel, ObjectsTablePanel, OrganizationalObjectPanel, - Panel, RelatedObjectsPanel, TemplatePanel, ) @@ -258,6 +257,7 @@ class RegionView(GetRelatedModelsMixin, generic.ObjectView): model='dcim.Region', title=_('Child Regions'), filters={'parent_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject('dcim.Region', url_params={'parent': lambda ctx: ctx['object'].pk}), ], @@ -390,6 +390,7 @@ class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView): model='dcim.SiteGroup', title=_('Child Groups'), filters={'parent_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject('dcim.SiteGroup', url_params={'parent': lambda ctx: ctx['object'].pk}), ], @@ -540,6 +541,7 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='dcim.Location', filters={'site_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['site'], actions=[ actions.AddObject('dcim.Location', url_params={'site': lambda ctx: ctx['object'].pk}), ], @@ -552,6 +554,7 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView): 'rack_id': settings.FILTERS_NULL_CHOICE_VALUE, 'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE, }, + exclude_columns=['site'], actions=[ actions.AddObject('dcim.Device', url_params={'site': lambda ctx: ctx['object'].pk}), ], @@ -674,6 +677,7 @@ class LocationView(GetRelatedModelsMixin, generic.ObjectView): model='dcim.Location', title=_('Child Locations'), filters={'parent_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject( 'dcim.Location', @@ -692,6 +696,7 @@ class LocationView(GetRelatedModelsMixin, generic.ObjectView): 'rack_id': settings.FILTERS_NULL_CHOICE_VALUE, 'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE, }, + exclude_columns=['location'], actions=[ actions.AddObject( 'dcim.Device', @@ -787,6 +792,85 @@ class LocationBulkDeleteView(generic.BulkDeleteView): table = tables.LocationTable +# +# Rack groups +# + + +@register_model_view(RackGroup, 'list', path='', detail=False) +class RackGroupListView(generic.ObjectListView): + queryset = RackGroup.objects.annotate( + rack_count=count_related(Rack, 'group') + ) + filterset = filtersets.RackGroupFilterSet + filterset_form = forms.RackGroupFilterForm + table = tables.RackGroupTable + + +@register_model_view(RackGroup) +class RackGroupView(GetRelatedModelsMixin, generic.ObjectView): + queryset = RackGroup.objects.all() + layout = layout.SimpleLayout( + left_panels=[ + OrganizationalObjectPanel(), + TagsPanel(), + ], + right_panels=[ + RelatedObjectsPanel(), + CustomFieldsPanel(), + CommentsPanel(), + ], + ) + + def get_extra_context(self, request, instance): + return { + 'related_models': self.get_related_models(request, instance), + } + + +@register_model_view(RackGroup, 'add', detail=False) +@register_model_view(RackGroup, 'edit') +class RackGroupEditView(generic.ObjectEditView): + queryset = RackGroup.objects.all() + form = forms.RackGroupForm + + +@register_model_view(RackGroup, 'delete') +class RackGroupDeleteView(generic.ObjectDeleteView): + queryset = RackGroup.objects.all() + + +@register_model_view(RackGroup, 'bulk_import', path='import', detail=False) +class RackGroupBulkImportView(generic.BulkImportView): + queryset = RackGroup.objects.all() + model_form = forms.RackGroupImportForm + + +@register_model_view(RackGroup, 'bulk_edit', path='edit', detail=False) +class RackGroupBulkEditView(generic.BulkEditView): + queryset = RackGroup.objects.annotate( + rack_count=count_related(Rack, 'group') + ) + filterset = filtersets.RackGroupFilterSet + table = tables.RackGroupTable + form = forms.RackGroupBulkEditForm + + +@register_model_view(RackGroup, 'bulk_rename', path='rename', detail=False) +class RackGroupBulkRenameView(generic.BulkRenameView): + queryset = RackGroup.objects.all() + filterset = filtersets.RackGroupFilterSet + + +@register_model_view(RackGroup, 'bulk_delete', path='delete', detail=False) +class RackGroupBulkDeleteView(generic.BulkDeleteView): + queryset = RackGroup.objects.annotate( + rack_count=count_related(Rack, 'group') + ) + filterset = filtersets.RackGroupFilterSet + table = tables.RackGroupTable + + # # Rack roles # @@ -1150,7 +1234,9 @@ class RackBulkDeleteView(generic.BulkDeleteView): @register_model_view(RackReservation, 'list', path='', detail=False) class RackReservationListView(generic.ObjectListView): - queryset = RackReservation.objects.all() + queryset = RackReservation.objects.annotate( + unit_count=Func('units', function='CARDINALITY', output_field=IntegerField()) + ) filterset = filtersets.RackReservationFilterSet filterset_form = forms.RackReservationFilterForm table = tables.RackReservationTable @@ -1159,10 +1245,12 @@ class RackReservationListView(generic.ObjectListView): @register_model_view(RackReservation) class RackReservationView(generic.ObjectView): - queryset = RackReservation.objects.all() + queryset = RackReservation.objects.annotate( + unit_count=Func('units', function='CARDINALITY', output_field=IntegerField()) + ) layout = layout.SimpleLayout( left_panels=[ - panels.RackPanel(accessor='object.rack', only=['region', 'site', 'location', 'name']), + panels.RackPanel(accessor='object.rack', only=['region', 'site', 'location', 'group', 'name']), panels.RackReservationPanel(title=_('Reservation')), CustomFieldsPanel(), TagsPanel(), @@ -1212,7 +1300,9 @@ class RackReservationImportView(generic.BulkImportView): @register_model_view(RackReservation, 'bulk_edit', path='edit', detail=False) class RackReservationBulkEditView(generic.BulkEditView): - queryset = RackReservation.objects.all() + queryset = RackReservation.objects.annotate( + unit_count=Func('units', function='CARDINALITY', output_field=IntegerField()) + ) filterset = filtersets.RackReservationFilterSet table = tables.RackReservationTable form = forms.RackReservationBulkEditForm @@ -1220,7 +1310,9 @@ class RackReservationBulkEditView(generic.BulkEditView): @register_model_view(RackReservation, 'bulk_delete', path='delete', detail=False) class RackReservationBulkDeleteView(generic.BulkDeleteView): - queryset = RackReservation.objects.all() + queryset = RackReservation.objects.annotate( + unit_count=Func('units', function='CARDINALITY', output_field=IntegerField()) + ) filterset = filtersets.RackReservationFilterSet table = tables.RackReservationTable @@ -1599,6 +1691,7 @@ class ModuleTypeProfileView(generic.ObjectView): filters={ 'profile_id': lambda ctx: ctx['object'].pk, }, + exclude_columns=['profile'], actions=[ actions.AddObject( 'dcim.ModuleType', @@ -1677,7 +1770,7 @@ class ModuleTypeView(GetRelatedModelsMixin, generic.ObjectView): CommentsPanel(), ], right_panels=[ - Panel( + TemplatePanel( title=_('Attributes'), template_name='dcim/panels/module_type_attributes.html', ), @@ -2340,6 +2433,7 @@ class DeviceRoleView(GetRelatedModelsMixin, generic.ObjectView): model='dcim.DeviceRole', title=_('Child Device Roles'), filters={'parent_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject('dcim.DeviceRole', url_params={'parent': lambda ctx: ctx['object'].pk}), ], @@ -2440,6 +2534,7 @@ class PlatformView(GetRelatedModelsMixin, generic.ObjectView): model='dcim.Platform', title=_('Child Platforms'), filters={'parent_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject('dcim.Platform', url_params={'parent': lambda ctx: ctx['object'].pk}), ], @@ -2518,6 +2613,7 @@ class DeviceView(generic.ObjectView): ObjectsTablePanel( model='dcim.VirtualDeviceContext', filters={'device_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['device'], actions=[ actions.AddObject('dcim.VirtualDeviceContext', url_params={'device': lambda ctx: ctx['object'].pk}), ], @@ -2530,6 +2626,7 @@ class DeviceView(generic.ObjectView): model='ipam.Service', title=_('Application Services'), filters={'device_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject( 'ipam.Service', @@ -2847,7 +2944,7 @@ class ModuleView(GetRelatedModelsMixin, generic.ObjectView): CommentsPanel(), ], right_panels=[ - Panel( + TemplatePanel( title=_('Module Type'), template_name='dcim/panels/module_type.html', ), @@ -3289,11 +3386,13 @@ class InterfaceView(generic.ObjectView): model='ipam.IPAddress', filters={'interface_id': lambda ctx: ctx['object'].pk}, title=_('IP Addresses'), + exclude_columns=['assigned', 'assigned_object', 'assigned_object_parent'], ), ObjectsTablePanel( model='dcim.MACAddress', filters={'interface_id': lambda ctx: ctx['object'].pk}, title=_('MAC Addresses'), + exclude_columns=['assigned_object', 'assigned_object_parent'], ), ObjectsTablePanel( model='ipam.VLAN', @@ -3653,10 +3752,7 @@ class ModuleBayView(generic.ObjectView): ], right_panels=[ CustomFieldsPanel(), - Panel( - title=_('Installed Module'), - template_name='dcim/panels/installed_module.html', - ), + panels.InstalledModulePanel(), ], ) @@ -3728,10 +3824,7 @@ class DeviceBayView(generic.ObjectView): TagsPanel(), ], right_panels=[ - Panel( - title=_('Installed Device'), - template_name='dcim/panels/installed_device.html', - ), + panels.InstalledDevicePanel(), ], ) @@ -3967,6 +4060,7 @@ class InventoryItemRoleListView(generic.ObjectListView): @register_model_view(InventoryItemRole) class InventoryItemRoleView(GetRelatedModelsMixin, generic.ObjectView): queryset = InventoryItemRole.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.InventoryItemRolePanel(), @@ -4131,6 +4225,72 @@ class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView): default_return_url = 'dcim:device_list' +# +# Cable bundles +# + +@register_model_view(CableBundle, 'list', path='', detail=False) +class CableBundleListView(generic.ObjectListView): + queryset = CableBundle.objects.annotate( + cable_count=count_related(Cable, 'bundle') + ) + filterset = filtersets.CableBundleFilterSet + filterset_form = forms.CableBundleFilterForm + table = tables.CableBundleTable + + +@register_model_view(CableBundle) +class CableBundleView(generic.ObjectView): + queryset = CableBundle.objects.all() + + def get_extra_context(self, request, instance): + cables_table = tables.CableTable( + instance.cables.all().prefetch_related( + 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location', + 'terminations___site', + ), + orderable=False, + ) + cables_table.configure(request) + + return { + 'cables_table': cables_table, + } + + +@register_model_view(CableBundle, 'add', detail=False) +@register_model_view(CableBundle, 'edit') +class CableBundleEditView(generic.ObjectEditView): + queryset = CableBundle.objects.all() + form = forms.CableBundleForm + + +@register_model_view(CableBundle, 'delete') +class CableBundleDeleteView(generic.ObjectDeleteView): + queryset = CableBundle.objects.all() + + +@register_model_view(CableBundle, 'bulk_import', path='import', detail=False) +class CableBundleBulkImportView(generic.BulkImportView): + queryset = CableBundle.objects.all() + model_form = forms.CableBundleImportForm + + +@register_model_view(CableBundle, 'bulk_edit', path='edit', detail=False) +class CableBundleBulkEditView(generic.BulkEditView): + queryset = CableBundle.objects.all() + filterset = filtersets.CableBundleFilterSet + table = tables.CableBundleTable + form = forms.CableBundleBulkEditForm + + +@register_model_view(CableBundle, 'bulk_delete', path='delete', detail=False) +class CableBundleBulkDeleteView(generic.BulkDeleteView): + queryset = CableBundle.objects.all() + filterset = filtersets.CableBundleFilterSet + table = tables.CableBundleTable + + # # Cables # @@ -4149,6 +4309,7 @@ class CableListView(generic.ObjectListView): @register_model_view(Cable) class CableView(generic.ObjectView): queryset = Cable.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.CablePanel(), @@ -4157,11 +4318,11 @@ class CableView(generic.ObjectView): CommentsPanel(), ], right_panels=[ - Panel( + TemplatePanel( title=_('Termination A'), template_name='dcim/panels/cable_termination_a.html', ), - Panel( + TemplatePanel( title=_('Termination B'), template_name='dcim/panels/cable_termination_b.html', ), @@ -4299,6 +4460,7 @@ class VirtualChassisListView(generic.ObjectListView): @register_model_view(VirtualChassis) class VirtualChassisView(generic.ObjectView): queryset = VirtualChassis.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.VirtualChassisPanel(), @@ -4724,6 +4886,7 @@ class VirtualDeviceContextListView(generic.ObjectListView): @register_model_view(VirtualDeviceContext) class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView): queryset = VirtualDeviceContext.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.VirtualDeviceContextPanel(), @@ -4809,6 +4972,7 @@ class MACAddressListView(generic.ObjectListView): @register_model_view(MACAddress) class MACAddressView(generic.ObjectView): queryset = MACAddress.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.MACAddressPanel(), diff --git a/netbox/extras/api/mixins.py b/netbox/extras/api/mixins.py index adaf4d55e..6a3713be4 100644 --- a/netbox/extras/api/mixins.py +++ b/netbox/extras/api/mixins.py @@ -1,9 +1,8 @@ from django.utils.translation import gettext as _ -from jinja2.exceptions import TemplateError from rest_framework.decorators import action from rest_framework.renderers import JSONRenderer from rest_framework.response import Response -from rest_framework.status import HTTP_400_BAD_REQUEST +from rest_framework.status import HTTP_400_BAD_REQUEST, HTTP_500_INTERNAL_SERVER_ERROR from extras.models import ConfigTemplate from netbox.api.authentication import TokenWritePermission @@ -47,10 +46,11 @@ class ConfigTemplateRenderMixin: def render_configtemplate(self, request, configtemplate, context): try: output = configtemplate.render(context=context) - except TemplateError as e: - return Response({ - 'detail': f"An error occurred while rendering the template (line {e.lineno}): {e}" - }, status=500) + except Exception as e: + detail = configtemplate.format_render_error(e) + if request.accepted_renderer.format == 'txt': + return Response(detail, status=HTTP_500_INTERNAL_SERVER_ERROR) + return Response({'detail': detail}, status=HTTP_500_INTERNAL_SERVER_ERROR) # If the client has requested "text/plain", return the raw content. if request.accepted_renderer.format == 'txt': diff --git a/netbox/extras/api/serializers_/configtemplates.py b/netbox/extras/api/serializers_/configtemplates.py index ac9f40738..4bf4608ac 100644 --- a/netbox/extras/api/serializers_/configtemplates.py +++ b/netbox/extras/api/serializers_/configtemplates.py @@ -28,7 +28,7 @@ class ConfigTemplateSerializer( model = ConfigTemplate fields = [ 'id', 'url', 'display_url', 'display', 'name', 'description', 'environment_params', 'template_code', - 'mime_type', 'file_name', 'file_extension', 'as_attachment', 'data_source', 'data_path', 'data_file', - 'auto_sync_enabled', 'data_synced', 'owner', 'tags', 'created', 'last_updated', + 'mime_type', 'file_name', 'file_extension', 'as_attachment', 'debug', 'data_source', 'data_path', + 'data_file', 'auto_sync_enabled', 'data_synced', 'owner', 'tags', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/extras/api/serializers_/customfields.py b/netbox/extras/api/serializers_/customfields.py index b12979439..175b88646 100644 --- a/netbox/extras/api/serializers_/customfields.py +++ b/netbox/extras/api/serializers_/customfields.py @@ -27,13 +27,17 @@ class CustomFieldChoiceSetSerializer(OwnerMixin, ChangeLogMessageSerializer, Val max_length=2 ) ) + choice_colors = serializers.DictField( + child=serializers.ChoiceField(choices=CustomFieldChoiceColorChoices), + required=False, + ) choices_count = serializers.IntegerField(read_only=True) class Meta: model = CustomFieldChoiceSet fields = [ 'id', 'url', 'display_url', 'display', 'name', 'description', 'base_choices', 'extra_choices', - 'order_alphabetically', 'choices_count', 'owner', 'created', 'last_updated', + 'choice_colors', 'order_alphabetically', 'choices_count', 'owner', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'name', 'description', 'choices_count') @@ -65,8 +69,8 @@ class CustomFieldSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedMod 'id', 'url', 'display_url', 'display', 'object_types', 'type', 'related_object_type', 'data_type', 'name', 'label', 'group_name', 'description', 'required', 'unique', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'is_cloneable', 'default', 'related_object_filter', 'weight', - 'validation_minimum', 'validation_maximum', 'validation_regex', 'choice_set', 'owner', 'comments', - 'created', 'last_updated', + 'validation_minimum', 'validation_maximum', 'validation_regex', 'validation_schema', 'choice_set', + 'owner', 'comments', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/extras/api/serializers_/scripts.py b/netbox/extras/api/serializers_/scripts.py index 08fb7baaa..81a613fc5 100644 --- a/netbox/extras/api/serializers_/scripts.py +++ b/netbox/extras/api/serializers_/scripts.py @@ -7,7 +7,7 @@ from drf_spectacular.utils import extend_schema_field from rest_framework import serializers from core.api.serializers_.jobs import JobSerializer -from core.choices import ManagedFileRootPathChoices +from core.choices import JobNotificationChoices, ManagedFileRootPathChoices from extras.models import Script, ScriptModule from extras.utils import validate_script_content from netbox.api.serializers import ValidatedModelSerializer @@ -124,6 +124,19 @@ class ScriptInputSerializer(serializers.Serializer): commit = serializers.BooleanField() schedule_at = serializers.DateTimeField(required=False, allow_null=True) interval = serializers.IntegerField(required=False, allow_null=True) + notifications = serializers.ChoiceField( + choices=JobNotificationChoices, + required=False, + default=JobNotificationChoices.NOTIFICATION_ALWAYS, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Default to script's Meta.notifications_default if set + script = self.context.get('script') + if script and script.python_class: + self.fields['notifications'].default = script.python_class.notifications_default def validate_schedule_at(self, value): """ diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index 5a2c03212..6f57f1957 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -338,7 +338,8 @@ class ScriptViewSet(ModelViewSet): commit=input_serializer.data['commit'], job_timeout=script.python_class.job_timeout, schedule_at=input_serializer.validated_data.get('schedule_at'), - interval=input_serializer.validated_data.get('interval') + interval=input_serializer.validated_data.get('interval'), + notifications=input_serializer.validated_data.get('notifications'), ) serializer = serializers.ScriptDetailSerializer(script, context={'request': request}) diff --git a/netbox/extras/choices.py b/netbox/extras/choices.py index de319bd15..b011819d4 100644 --- a/netbox/extras/choices.py +++ b/netbox/extras/choices.py @@ -95,6 +95,39 @@ class CustomFieldChoiceSetBaseChoices(ChoiceSet): ) +class CustomFieldChoiceColorChoices(ChoiceSet): + + BLUE = 'blue' + INDIGO = 'indigo' + PURPLE = 'purple' + PINK = 'pink' + RED = 'red' + ORANGE = 'orange' + YELLOW = 'yellow' + GREEN = 'green' + TEAL = 'teal' + CYAN = 'cyan' + GRAY = 'gray' + BLACK = 'black' + WHITE = 'white' + + CHOICES = ( + (BLUE, _('Blue'), BLUE), + (INDIGO, _('Indigo'), INDIGO), + (PURPLE, _('Purple'), PURPLE), + (PINK, _('Pink'), PINK), + (RED, _('Red'), RED), + (ORANGE, _('Orange'), ORANGE), + (YELLOW, _('Yellow'), YELLOW), + (GREEN, _('Green'), GREEN), + (TEAL, _('Teal'), TEAL), + (CYAN, _('Cyan'), CYAN), + (GRAY, _('Gray'), GRAY), + (BLACK, _('Black'), BLACK), + (WHITE, _('White'), WHITE), + ) + + # # CustomLinks # diff --git a/netbox/extras/filtersets.py b/netbox/extras/filtersets.py index 2b995e52a..8e9683636 100644 --- a/netbox/extras/filtersets.py +++ b/netbox/extras/filtersets.py @@ -200,6 +200,12 @@ class CustomFieldChoiceSetFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet choice = MultiValueCharFilter( method='filter_by_choice' ) + choice_colors = django_filters.MultipleChoiceFilter( + choices=CustomFieldChoiceColorChoices, + method='filter_by_choice_colors', + label=_('Choice colors'), + distinct=False, + ) class Meta: model = CustomFieldChoiceSet @@ -219,6 +225,25 @@ class CustomFieldChoiceSetFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet # TODO: Support case-insensitive matching return queryset.filter(extra_choices__overlap=value) + def filter_by_choice_colors(self, queryset, name, value): + if not value: + return queryset + + choice_color_keys = set() + for choice_colors in queryset.values_list('choice_colors', flat=True): + if isinstance(choice_colors, dict): + choice_color_keys.update(choice_colors.keys()) + + if not choice_color_keys: + return queryset.none() + + params = Q() + for key in choice_color_keys: + for color in value: + params |= Q(choice_colors__contains={key: color}) + + return queryset.filter(params) + @register_filterset class CustomLinkFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet): @@ -857,7 +882,7 @@ class ConfigTemplateFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet): class Meta: model = ConfigTemplate fields = ( - 'id', 'name', 'description', 'mime_type', 'file_name', 'file_extension', 'as_attachment', + 'id', 'name', 'description', 'mime_type', 'file_name', 'file_extension', 'as_attachment', 'debug', 'auto_sync_enabled', 'data_synced' ) diff --git a/netbox/extras/forms/bulk_edit.py b/netbox/extras/forms/bulk_edit.py index 6805898fc..9fecfdb7a 100644 --- a/netbox/extras/forms/bulk_edit.py +++ b/netbox/extras/forms/bulk_edit.py @@ -7,7 +7,7 @@ from netbox.events import get_event_type_choices from netbox.forms import NetBoxModelBulkEditForm, PrimaryModelBulkEditForm from netbox.forms.mixins import ChangelogMessageMixin, OwnerMixin from utilities.forms import BulkEditForm, add_blank_choice -from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField +from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField, JSONField from utilities.forms.rendering import FieldSet from utilities.forms.widgets import BulkEditNullBooleanSelect @@ -88,14 +88,21 @@ class CustomFieldBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm): label=_('Validation regex'), required=False ) + validation_schema = JSONField( + label=_('Validation schema'), + required=False + ) comments = CommentField() fieldsets = ( FieldSet('group_name', 'description', 'weight', 'required', 'unique', 'choice_set', name=_('Attributes')), FieldSet('ui_visible', 'ui_editable', 'is_cloneable', name=_('Behavior')), - FieldSet('validation_minimum', 'validation_maximum', 'validation_regex', name=_('Validation')), + FieldSet( + 'validation_minimum', 'validation_maximum', 'validation_regex', 'validation_schema', + name=_('Validation') + ), ) - nullable_fields = ('group_name', 'description', 'choice_set') + nullable_fields = ('group_name', 'description', 'choice_set', 'validation_schema') class CustomFieldChoiceSetBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm): @@ -392,6 +399,11 @@ class ConfigTemplateBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm required=False, widget=BulkEditNullBooleanSelect() ) + debug = forms.NullBooleanField( + label=_('Debug'), + required=False, + widget=BulkEditNullBooleanSelect() + ) auto_sync_enabled = forms.NullBooleanField( label=_('Auto sync enabled'), required=False, diff --git a/netbox/extras/forms/bulk_import.py b/netbox/extras/forms/bulk_import.py index 91d5b2751..78f6d8c0b 100644 --- a/netbox/extras/forms/bulk_import.py +++ b/netbox/extras/forms/bulk_import.py @@ -80,7 +80,8 @@ class CustomFieldImportForm(OwnerCSVMixin, CSVModelForm): fields = ( 'name', 'label', 'group_name', 'type', 'object_types', 'related_object_type', 'required', 'unique', 'description', 'search_weight', 'filter_logic', 'default', 'choice_set', 'weight', 'validation_minimum', - 'validation_maximum', 'validation_regex', 'ui_visible', 'ui_editable', 'is_cloneable', 'owner', 'comments', + 'validation_maximum', 'validation_regex', 'validation_schema', 'ui_visible', 'ui_editable', + 'is_cloneable', 'owner', 'comments', ) @@ -98,11 +99,19 @@ class CustomFieldChoiceSetImportForm(OwnerCSVMixin, CSVModelForm): '"choice1:First Choice,choice2:Second Choice"' ) ) + choice_colors = SimpleArrayField( + base_field=forms.CharField(), + required=False, + help_text=_( + 'Quoted string of comma-separated color mappings in the format ' + '"choice1:red,choice2:green". Supported colors: {colors}' + ).format(colors=', '.join(CustomFieldChoiceColorChoices.values())), + ) class Meta: model = CustomFieldChoiceSet fields = ( - 'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically', 'owner', + 'name', 'description', 'base_choices', 'extra_choices', 'choice_colors', 'order_alphabetically', 'owner', ) def clean_extra_choices(self): @@ -119,6 +128,28 @@ class CustomFieldChoiceSetImportForm(OwnerCSVMixin, CSVModelForm): return data return None + def clean_choice_colors(self): + if isinstance(self.cleaned_data['choice_colors'], list): + data = {} + for line in self.cleaned_data['choice_colors']: + try: + value, color = re.split(r'(?choice1:First Choice') ) + choice_colors = forms.CharField( + widget=ChoicesWidget(), + required=False, + help_text=mark_safe( + _( + 'Bind an optional color to a choice by its value. Enter one mapping per line as ' + 'value:color. Example:' + ) + + ' choice1:red
' + + _('Supported colors: {colors}').format( + colors=', '.join(f'{color}' for color in CustomFieldChoiceColorChoices.values()) + ) + ), + ) fieldsets = ( FieldSet( - 'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically', + 'name', 'description', 'base_choices', 'extra_choices', 'choice_colors', 'order_alphabetically', name=_('Custom Field Choice Set') ), ) class Meta: model = CustomFieldChoiceSet - fields = ('name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically', 'owner') + fields = ( + 'name', 'description', 'base_choices', 'extra_choices', 'choice_colors', 'order_alphabetically', 'owner' + ) def __init__(self, *args, initial=None, **kwargs): super().__init__(*args, initial=initial, **kwargs) @@ -219,9 +250,25 @@ class CustomFieldChoiceSetForm(ChangelogMessageMixin, OwnerMixin, forms.ModelFor self.initial['extra_choices'] = '\n'.join(choices) + # Convert choice_colors JSONField from model to CharField for form + if 'choice_colors' in self.initial: + choice_colors = self.initial.get('choice_colors') or {} + + if isinstance(choice_colors, str): + choice_colors = json.loads(choice_colors) + + mappings = [] + for value, color in sorted(choice_colors.items()): + value = value.replace(':', '\\:') + mappings.append(f'{value}:{color}') + + self.initial['choice_colors'] = '\n'.join(mappings) + def clean_extra_choices(self): data = [] for line in self.cleaned_data['extra_choices'].splitlines(): + if not line.strip(): + continue try: value, label = re.split(r'(? tuple[QuerySet, Q]: + if not value: + return queryset, Q() + + field_name = f'{prefix}choice_colors' + choice_values = set() + + for mapping in queryset.exclude(**{f'{field_name}__isnull': True}).values_list(field_name, flat=True): + if isinstance(mapping, dict): + choice_values.update(mapping.keys()) + + if not choice_values: + return queryset, Q(pk__in=[]) + + params = Q() + for color in value: + for choice_value in choice_values: + params |= Q(**{f'{field_name}__contains': {choice_value: color}}) + + return queryset, params + @strawberry_django.filter_type(models.CustomLink, lookups=True) class CustomLinkFilter(ChangeLoggedModelFilter): diff --git a/netbox/extras/graphql/types.py b/netbox/extras/graphql/types.py index 1aac5e971..bed77aad0 100644 --- a/netbox/extras/graphql/types.py +++ b/netbox/extras/graphql/types.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Annotated import strawberry import strawberry_django +from strawberry.scalars import JSON from core.graphql.mixins import SyncedDataMixin from extras import models @@ -106,7 +107,7 @@ class CustomFieldType(OwnerMixin, ObjectType): @strawberry_django.type( models.CustomFieldChoiceSet, - exclude=['extra_choices'], + exclude=['extra_choices', 'choice_colors'], filters=CustomFieldChoiceSetFilter, pagination=True ) @@ -114,6 +115,7 @@ class CustomFieldChoiceSetType(OwnerMixin, ObjectType): choices_for: list[Annotated["CustomFieldType", strawberry.lazy('extras.graphql.types')]] extra_choices: list[list[str]] | None + choice_colors: JSON @strawberry_django.type( diff --git a/netbox/extras/management/commands/housekeeping.py b/netbox/extras/management/commands/housekeeping.py index 0d8a7e0b9..e68142ac3 100644 --- a/netbox/extras/management/commands/housekeeping.py +++ b/netbox/extras/management/commands/housekeeping.py @@ -1,3 +1,4 @@ +import warnings from datetime import timedelta from importlib import import_module @@ -5,9 +6,11 @@ import requests from django.conf import settings from django.core.cache import cache from django.core.management.base import BaseCommand +from django.db.models import Exists, OuterRef, Subquery from django.utils import timezone from packaging import version +from core.choices import ObjectChangeActionChoices from core.models import Job, ObjectChange from netbox.config import Config from utilities.proxy import resolve_proxies @@ -17,11 +20,12 @@ class Command(BaseCommand): help = "Perform nightly housekeeping tasks [DEPRECATED]" def handle(self, *args, **options): - self.stdout.write( + warnings.warn( + "\n\nDEPRECATION WARNING\n" "Running this command is no longer necessary: All housekeeping tasks\n" "are addressed automatically via NetBox's built-in job scheduler. It\n" - "will be removed in a future release.", - self.style.WARNING + "will be removed in a future release.\n", + category=FutureWarning, ) config = Config() @@ -45,29 +49,63 @@ class Command(BaseCommand): # Delete expired ObjectChanges if options['verbosity']: - self.stdout.write("[*] Checking for expired changelog records") + self.stdout.write('[*] Checking for expired changelog records') if config.CHANGELOG_RETENTION: cutoff = timezone.now() - timedelta(days=config.CHANGELOG_RETENTION) if options['verbosity'] >= 2: - self.stdout.write(f"\tRetention period: {config.CHANGELOG_RETENTION} days") - self.stdout.write(f"\tCut-off time: {cutoff}") - expired_records = ObjectChange.objects.filter(time__lt=cutoff).count() + self.stdout.write(f'\tRetention period: {config.CHANGELOG_RETENTION} days') + self.stdout.write(f'\tCut-off time: {cutoff}') + + expired_qs = ObjectChange.objects.filter(time__lt=cutoff) + + # When enabled, retain each object's original create and most recent update record while pruning expired + # changelog entries. This applies only to objects without a delete record. + if config.CHANGELOG_RETAIN_CREATE_LAST_UPDATE: + if options['verbosity'] >= 2: + self.stdout.write('\tRetaining create & last update records for non-deleted objects') + + deleted_exists = ObjectChange.objects.filter( + action=ObjectChangeActionChoices.ACTION_DELETE, + changed_object_type_id=OuterRef('changed_object_type_id'), + changed_object_id=OuterRef('changed_object_id'), + ) + + # Keep create records only where no delete exists for that object + create_pks_to_keep = ( + ObjectChange.objects.filter(action=ObjectChangeActionChoices.ACTION_CREATE) + .annotate(has_delete=Exists(deleted_exists)) + .filter(has_delete=False) + .values('pk') + ) + + # Keep the most recent update per object only where no delete exists for the object + latest_update_pks_to_keep = ( + ObjectChange.objects.filter(action=ObjectChangeActionChoices.ACTION_UPDATE) + .annotate(has_delete=Exists(deleted_exists)) + .filter(has_delete=False) + .order_by('changed_object_type_id', 'changed_object_id', '-time', '-pk') + .distinct('changed_object_type_id', 'changed_object_id') + .values('pk') + ) + + expired_qs = expired_qs.exclude(pk__in=Subquery(create_pks_to_keep)) + expired_qs = expired_qs.exclude(pk__in=Subquery(latest_update_pks_to_keep)) + + expired_records = expired_qs.count() if expired_records: if options['verbosity']: self.stdout.write( - f"\tDeleting {expired_records} expired records... ", - self.style.WARNING, - ending="" + f'\tDeleting {expired_records} expired records... ', self.style.WARNING, ending='' ) self.stdout.flush() - ObjectChange.objects.filter(time__lt=cutoff).delete() + expired_qs.delete() if options['verbosity']: - self.stdout.write("Done.", self.style.SUCCESS) + self.stdout.write('Done.', self.style.SUCCESS) elif options['verbosity']: - self.stdout.write("\tNo expired records found.", self.style.SUCCESS) + self.stdout.write('\tNo expired records found.', self.style.SUCCESS) elif options['verbosity']: self.stdout.write( - f"\tSkipping: No retention period specified (CHANGELOG_RETENTION = {config.CHANGELOG_RETENTION})" + f'\tSkipping: No retention period specified (CHANGELOG_RETENTION = {config.CHANGELOG_RETENTION})' ) # Delete expired Jobs diff --git a/netbox/extras/migrations/0135_configtemplate_debug.py b/netbox/extras/migrations/0135_configtemplate_debug.py new file mode 100644 index 000000000..cddfaf240 --- /dev/null +++ b/netbox/extras/migrations/0135_configtemplate_debug.py @@ -0,0 +1,22 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('extras', '0134_owner'), + ] + + operations = [ + migrations.AddField( + model_name='configtemplate', + name='debug', + field=models.BooleanField( + default=False, + help_text=( + 'Enable verbose error output when rendering this template. ' + 'Not recommended for production use.' + ), + verbose_name='debug', + ), + ), + ] diff --git a/netbox/extras/migrations/0136_customfield_validation_schema.py b/netbox/extras/migrations/0136_customfield_validation_schema.py new file mode 100644 index 000000000..9b4913fdd --- /dev/null +++ b/netbox/extras/migrations/0136_customfield_validation_schema.py @@ -0,0 +1,24 @@ +from django.db import migrations, models + +import utilities.jsonschema + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0135_configtemplate_debug'), + ] + + operations = [ + migrations.AddField( + model_name='customfield', + name='validation_schema', + field=models.JSONField( + blank=True, + help_text='A JSON schema definition for validating the custom field value', + null=True, + validators=[utilities.jsonschema.validate_schema], + verbose_name='validation schema', + ), + ), + ] diff --git a/netbox/extras/migrations/0137_default_ordering_indexes.py b/netbox/extras/migrations/0137_default_ordering_indexes.py new file mode 100644 index 000000000..f15c3659e --- /dev/null +++ b/netbox/extras/migrations/0137_default_ordering_indexes.py @@ -0,0 +1,74 @@ +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('core', '0022_default_ordering_indexes'), + ('dcim', '0232_default_ordering_indexes'), + ('extras', '0136_customfield_validation_schema'), + ('tenancy', '0023_add_mptt_tree_indexes'), + ('users', '0015_owner'), + ('virtualization', '0054_virtualmachinetype'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddIndex( + model_name='bookmark', + index=models.Index(fields=['created', 'id'], name='extras_book_created_1cb4a5_idx'), + ), + migrations.AddIndex( + model_name='configcontext', + index=models.Index(fields=['weight', 'name'], name='extras_conf_weight_ef9a81_idx'), + ), + migrations.AddIndex( + model_name='configtemplate', + index=models.Index(fields=['name'], name='extras_conf_name_e276bf_idx'), + ), + migrations.AddIndex( + model_name='customfield', + index=models.Index(fields=['group_name', 'weight', 'name'], name='extras_cust_group_n_40cb93_idx'), + ), + migrations.AddIndex( + model_name='customlink', + index=models.Index(fields=['group_name', 'weight', 'name'], name='extras_cust_group_n_5a8be0_idx'), + ), + migrations.AddIndex( + model_name='exporttemplate', + index=models.Index(fields=['name'], name='extras_expo_name_55a9af_idx'), + ), + migrations.AddIndex( + model_name='imageattachment', + index=models.Index(fields=['name', 'id'], name='extras_imag_name_23cd9f_idx'), + ), + migrations.AddIndex( + model_name='journalentry', + index=models.Index(fields=['-created'], name='extras_jour_created_ec0fac_idx'), + ), + migrations.AddIndex( + model_name='notification', + index=models.Index(fields=['-created', 'id'], name='extras_noti_created_1d5146_idx'), + ), + migrations.AddIndex( + model_name='savedfilter', + index=models.Index(fields=['weight', 'name'], name='extras_save_weight_c070c4_idx'), + ), + migrations.AddIndex( + model_name='script', + index=models.Index(fields=['module', 'name'], name='extras_scri_module__8bd99c_idx'), + ), + migrations.AddIndex( + model_name='subscription', + index=models.Index(fields=['-created', 'user'], name='extras_subs_created_034618_idx'), + ), + migrations.AddIndex( + model_name='tableconfig', + index=models.Index(fields=['weight', 'name'], name='extras_tabl_weight_7c4bb6_idx'), + ), + migrations.AddIndex( + model_name='tag', + index=models.Index(fields=['weight', 'name'], name='extras_tag_weight_d99f50_idx'), + ), + ] diff --git a/netbox/extras/migrations/0138_customfieldchoiceset_choice_colors.py b/netbox/extras/migrations/0138_customfieldchoiceset_choice_colors.py new file mode 100644 index 000000000..e3d113bc5 --- /dev/null +++ b/netbox/extras/migrations/0138_customfieldchoiceset_choice_colors.py @@ -0,0 +1,15 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('extras', '0137_default_ordering_indexes'), + ] + + operations = [ + migrations.AddField( + model_name='customfieldchoiceset', + name='choice_colors', + field=models.JSONField(blank=True, default=dict), + ), + ] diff --git a/netbox/extras/models/configs.py b/netbox/extras/models/configs.py index 88ec1276e..235cf7160 100644 --- a/netbox/extras/models/configs.py +++ b/netbox/extras/models/configs.py @@ -1,4 +1,4 @@ -from collections import defaultdict +import traceback import jsonschema from django.conf import settings @@ -6,9 +6,9 @@ from django.core.validators import ValidationError from django.db import models from django.urls import reverse from django.utils.translation import gettext_lazy as _ +from jinja2.exceptions import TemplateError from jsonschema.exceptions import ValidationError as JSONValidationError -from core.models import ObjectType from extras.models.mixins import RenderTemplateMixin from extras.querysets import ConfigContextQuerySet from netbox.models import ChangeLoggedModel, PrimaryModel @@ -176,6 +176,9 @@ class ConfigContext(SyncedDataMixin, CloningMixin, CustomLinksMixin, OwnerMixin, class Meta: ordering = ['weight', 'name'] + indexes = ( + models.Index(fields=('weight', 'name')), # Default ordering + ) verbose_name = _('config context') verbose_name_plural = _('config contexts') @@ -284,9 +287,19 @@ class ConfigTemplate( max_length=200, blank=True ) + debug = models.BooleanField( + verbose_name=_('debug'), + default=False, + help_text=_( + 'Enable verbose error output when rendering this template. Not recommended for production use.' + ) + ) class Meta: ordering = ('name',) + indexes = ( + models.Index(fields=('name',)), # Default ordering + ) verbose_name = _('config template') verbose_name_plural = _('config templates') @@ -303,16 +316,19 @@ class ConfigTemplate( self.template_code = self.data_file.data_as_string sync_data.alters_data = True - def get_context(self, context=None, queryset=None): - _context = defaultdict(dict) - - # Populate all public models for reference within the template - for object_type in ObjectType.objects.public(): - if model := object_type.model_class(): - _context[object_type.app_label][model.__name__] = model - - # Apply the provided context data, if any - if context is not None: - _context.update(context) - - return _context + def format_render_error(self, exc): + """ + Return a formatted error string for a rendering exception. When debug is enabled, the full + traceback for the provided exception is returned. Otherwise, a concise, user-facing message + is returned. + """ + if self.debug: + return ''.join(traceback.format_exception(exc)) + if isinstance(exc, TemplateError): + parts = [f"{type(exc).__name__}: {exc}"] + if getattr(exc, 'name', None): + parts.append(_("Template: {name}").format(name=exc.name)) + if getattr(exc, 'lineno', None): + parts.append(_("Line: {lineno}").format(lineno=exc.lineno)) + return "\n".join(parts) + return f"{type(exc).__name__}: {exc}" diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index fbb4aefc2..e7e8d2b84 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -4,6 +4,7 @@ import re from datetime import date, datetime import django_filters +import jsonschema from django import forms from django.conf import settings from django.contrib.postgres.fields import ArrayField @@ -15,6 +16,7 @@ from django.urls import reverse from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ +from jsonschema.exceptions import ValidationError as JSONValidationError from core.models import ObjectType from extras.choices import * @@ -40,6 +42,7 @@ from utilities.forms.fields import ( ) from utilities.forms.utils import add_blank_choice from utilities.forms.widgets import APISelect, APISelectMultiple, DatePicker, DateTimePicker +from utilities.jsonschema import validate_schema from utilities.querysets import RestrictedQuerySet from utilities.templatetags.builtins.filters import render_markdown from utilities.validators import validate_regex @@ -222,6 +225,13 @@ class CustomField(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedMo 'example, ^[A-Z]{3}$ will limit values to exactly three uppercase letters.' ) ) + validation_schema = models.JSONField( + blank=True, + null=True, + validators=[validate_schema], + verbose_name=_('validation schema'), + help_text=_('A JSON schema definition for validating the custom field value') + ) choice_set = models.ForeignKey( to='CustomFieldChoiceSet', on_delete=models.PROTECT, @@ -259,11 +269,14 @@ class CustomField(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedMo clone_fields = ( 'object_types', 'type', 'related_object_type', 'group_name', 'description', 'required', 'unique', 'search_weight', 'filter_logic', 'default', 'weight', 'validation_minimum', 'validation_maximum', - 'validation_regex', 'choice_set', 'ui_visible', 'ui_editable', 'is_cloneable', + 'validation_regex', 'validation_schema', 'choice_set', 'ui_visible', 'ui_editable', 'is_cloneable', ) class Meta: ordering = ['group_name', 'weight', 'name'] + indexes = ( + models.Index(fields=('group_name', 'weight', 'name')), # Default ordering + ) verbose_name = _('custom field') verbose_name_plural = _('custom fields') @@ -304,6 +317,11 @@ class CustomField(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedMo self._choice_map = dict(self.choices) return self._choice_map.get(value, value) + def get_choice_color(self, value): + if self.choice_set: + return self.choice_set.get_choice_color(value) + return None + def populate_initial_data(self, content_types): """ Populate initial custom field data upon either a) the creation of a new CustomField, or @@ -389,6 +407,12 @@ class CustomField(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedMo 'validation_regex': _("Regular expression validation is supported only for text and URL fields") }) + # Schema validation can be set only for JSON fields + if self.validation_schema and self.type != CustomFieldTypeChoices.TYPE_JSON: + raise ValidationError({ + 'validation_schema': _("JSON schema validation is supported only for JSON fields") + }) + # Uniqueness can not be enforced for boolean fields if self.unique and self.type == CustomFieldTypeChoices.TYPE_BOOLEAN: raise ValidationError({ @@ -820,6 +844,16 @@ class CustomField(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedMo if type(id) is not int: raise ValidationError(_("Found invalid object ID: {id}").format(id=id)) + # Validate JSON against schema (if defined) + elif self.type == CustomFieldTypeChoices.TYPE_JSON: + if self.validation_schema: + try: + jsonschema.validate(value, schema=self.validation_schema) + except JSONValidationError as e: + raise ValidationError( + _("Value does not conform to the assigned schema: {error}").format(error=e.message) + ) + elif self.required: raise ValidationError(_("Required field cannot be empty.")) @@ -851,12 +885,16 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, OwnerMixin, Chang blank=True, null=True ) + choice_colors = models.JSONField( + default=dict, + blank=True, + ) order_alphabetically = models.BooleanField( default=False, help_text=_('Choices are automatically ordered alphabetically') ) - clone_fields = ('extra_choices', 'order_alphabetically') + clone_fields = ('extra_choices', 'choice_colors', 'order_alphabetically') class Meta: ordering = ('name',) @@ -890,6 +928,24 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, OwnerMixin, Chang self._choices = sorted(self._choices, key=lambda x: x[0]) return self._choices + @property + def colors(self): + """ + Return merged color mappings from the selected base choice set (if it defines colors) + and any custom color overrides defined on this choice set. + """ + if not hasattr(self, '_colors'): + self._colors = {} + if self.base_choices: + base_choice_set = CHOICE_SETS.get(self.base_choices) + self._colors.update(getattr(base_choice_set, 'colors', {})) + if self.choice_colors: + self._colors.update(self.choice_colors) + return self._colors + + def get_choice_color(self, value): + return self.colors.get(value) + @property def choices_count(self): return len(self.choices) @@ -905,25 +961,56 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, OwnerMixin, Chang if not self.base_choices and not self.extra_choices: raise ValidationError(_("Must define base or extra choices.")) - # Check for duplicate values in extra_choices - choice_values = [c[0] for c in self.extra_choices] if self.extra_choices else [] - if len(set(choice_values)) != len(choice_values): - # At least one duplicate value is present. Find the first one and raise an error. - _seen = [] - for value in choice_values: - if value in _seen: + if self.choice_colors is None: + self.choice_colors = {} + elif not isinstance(self.choice_colors, dict): + raise ValidationError({ + 'choice_colors': _('Color mappings must be defined as a JSON object.') + }) + + valid_choice_values = set() + extra_choice_values = set() + + if self.base_choices: + valid_choice_values.update(CHOICE_SETS.get(self.base_choices).values()) + + if self.extra_choices: + for value, _label in self.extra_choices: + if value in extra_choice_values: raise ValidationError(_("Duplicate value '{value}' found in extra choices.").format(value=value)) - _seen.append(value) + extra_choice_values.add(value) + valid_choice_values.update(extra_choice_values) + + invalid_choice_values = set() + invalid_colors = set() + valid_colors = set(CustomFieldChoiceColorChoices.values()) + + for value, color in self.choice_colors.items(): + if value not in valid_choice_values: + invalid_choice_values.add(value) + if color not in valid_colors: + invalid_colors.add(color) + + if invalid_choice_values: + raise ValidationError({ + 'choice_colors': _( + 'Color mappings must reference an existing choice value. Invalid value(s): {values}.' + ).format(values=', '.join(sorted(invalid_choice_values))) + }) + + if invalid_colors: + raise ValidationError({ + 'choice_colors': _( + 'Invalid color value(s): {colors}. Use a supported named color.' + ).format(colors=', '.join(sorted(invalid_colors))) + }) # Check whether any choices have been removed. If so, check whether any of the removed # choices are still set in custom field data for any object. original_choices = set([ c[0] for c in self._original_extra_choices ]) if self._original_extra_choices else set() - current_choices = set([ - c[0] for c in self.extra_choices - ]) if self.extra_choices else set() - if removed_choices := original_choices - current_choices: + if removed_choices := original_choices - valid_choice_values: for custom_field in self.choices_for.all(): for object_type in custom_field.object_types.all(): model = object_type.model_class() diff --git a/netbox/extras/models/mixins.py b/netbox/extras/models/mixins.py index 47a0b97e8..805916d9f 100644 --- a/netbox/extras/models/mixins.py +++ b/netbox/extras/models/mixins.py @@ -2,6 +2,7 @@ import importlib.abc import importlib.util import os import sys +from collections import defaultdict from django.core.files.storage import storages from django.db import models @@ -9,6 +10,7 @@ from django.http import HttpResponse from django.utils.module_loading import import_string from django.utils.translation import gettext_lazy as _ +from core.models import ObjectType from extras.constants import DEFAULT_MIME_TYPE, JINJA_ENV_PARAMS_WITH_PATH_IMPORT from extras.utils import filename_from_model, filename_from_object from utilities.jinja2 import render_jinja2 @@ -120,9 +122,17 @@ class RenderTemplateMixin(models.Model): abstract = True def get_context(self, context=None, queryset=None): - raise NotImplementedError(_("{class_name} must implement a get_context() method.").format( - class_name=self.__class__ - )) + _context = defaultdict(dict) + + # Populate all public models for reference within the template + for object_type in ObjectType.objects.public(): + if model := object_type.model_class(): + _context[object_type.app_label][model.__name__] = model + + if context is not None: + _context.update(context) + + return _context def get_environment_params(self): """ @@ -140,7 +150,8 @@ class RenderTemplateMixin(models.Model): """ context = self.get_context(context=context, queryset=queryset) env_params = self.get_environment_params() - output = render_jinja2(self.template_code, context, env_params, getattr(self, 'data_file', None)) + debug = getattr(self, 'debug', False) + output = render_jinja2(self.template_code, context, env_params, getattr(self, 'data_file', None), debug=debug) # Replace CRLF-style line terminators output = output.replace('\r\n', '\n') diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index b117ab6f0..f3b8fae61 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -360,6 +360,9 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedMod class Meta: ordering = ['group_name', 'weight', 'name'] + indexes = ( + models.Index(fields=('group_name', 'weight', 'name')), # Default ordering + ) verbose_name = _('custom link') verbose_name_plural = _('custom links') @@ -433,6 +436,9 @@ class ExportTemplate( class Meta: ordering = ('name',) + indexes = ( + models.Index(fields=('name',)), # Default ordering + ) verbose_name = _('export template') verbose_name_plural = _('export templates') @@ -462,14 +468,8 @@ class ExportTemplate( sync_data.alters_data = True def get_context(self, context=None, queryset=None): - _context = { - 'queryset': queryset, - } - - # Apply the provided context data, if any - if context is not None: - _context.update(context) - + _context = super().get_context(context=context, queryset=queryset) + _context['queryset'] = queryset return _context @@ -525,6 +525,9 @@ class SavedFilter(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedMo class Meta: ordering = ('weight', 'name') + indexes = ( + models.Index(fields=('weight', 'name')), # Default ordering + ) verbose_name = _('saved filter') verbose_name_plural = _('saved filters') @@ -607,6 +610,9 @@ class TableConfig(CloningMixin, ChangeLoggedModel): class Meta: ordering = ('weight', 'name') + indexes = ( + models.Index(fields=('weight', 'name')), # Default ordering + ) verbose_name = _('table config') verbose_name_plural = _('table configs') @@ -710,6 +716,7 @@ class ImageAttachment(ChangeLoggedModel): class Meta: ordering = ('name', 'pk') # name may be non-unique indexes = ( + models.Index(fields=('name', 'id')), # Default ordering models.Index(fields=('object_type', 'object_id')), ) verbose_name = _('image attachment') @@ -820,6 +827,7 @@ class JournalEntry(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ExportTemplat class Meta: ordering = ('-created',) indexes = ( + models.Index(fields=('-created',)), # Default ordering models.Index(fields=('assigned_object_type', 'assigned_object_id')), ) verbose_name = _('journal entry') @@ -875,6 +883,7 @@ class Bookmark(models.Model): class Meta: ordering = ('created', 'pk') indexes = ( + models.Index(fields=('created', 'id')), # Default ordering models.Index(fields=('object_type', 'object_id')), ) constraints = ( diff --git a/netbox/extras/models/notifications.py b/netbox/extras/models/notifications.py index f813a0d29..c2e30c697 100644 --- a/netbox/extras/models/notifications.py +++ b/netbox/extras/models/notifications.py @@ -73,6 +73,7 @@ class Notification(models.Model): class Meta: ordering = ('-created', 'pk') indexes = ( + models.Index(fields=('-created', 'id')), # Default ordering models.Index(fields=('object_type', 'object_id')), ) constraints = ( @@ -215,6 +216,7 @@ class Subscription(models.Model): class Meta: ordering = ('-created', 'user') indexes = ( + models.Index(fields=('-created', 'user')), # Default ordering models.Index(fields=('object_type', 'object_id')), ) constraints = ( diff --git a/netbox/extras/models/scripts.py b/netbox/extras/models/scripts.py index c20b223e1..02f3902d1 100644 --- a/netbox/extras/models/scripts.py +++ b/netbox/extras/models/scripts.py @@ -60,6 +60,9 @@ class Script(EventRulesMixin, JobsMixin): name='extras_script_unique_name_module' ), ) + indexes = ( + models.Index(fields=('module', 'name')), # Default ordering + ) verbose_name = _('script') verbose_name_plural = _('scripts') diff --git a/netbox/extras/models/tags.py b/netbox/extras/models/tags.py index 1fdb48462..f04b2e3cc 100644 --- a/netbox/extras/models/tags.py +++ b/netbox/extras/models/tags.py @@ -52,6 +52,9 @@ class Tag(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel, Tag class Meta: ordering = ('weight', 'name') + indexes = ( + models.Index(fields=('weight', 'name')), # Default ordering + ) verbose_name = _('tag') verbose_name_plural = _('tags') diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 9d3e5b75f..f286670da 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -10,6 +10,7 @@ from django.utils import timezone from django.utils.functional import classproperty from django.utils.translation import gettext as _ +from core.choices import JobNotificationChoices from extras.choices import LogLevelChoices from extras.models import ScriptModule from ipam.formfields import IPAddressFormField, IPNetworkFormField @@ -389,6 +390,10 @@ class BaseScript: def scheduling_enabled(self): return getattr(self.Meta, 'scheduling_enabled', True) + @classproperty + def notifications_default(self): + return getattr(self.Meta, 'notifications_default', JobNotificationChoices.NOTIFICATION_ALWAYS) + @property def filename(self): return inspect.getfile(self.__class__) @@ -491,7 +496,10 @@ class BaseScript: fieldsets.append((_('Script Data'), fields)) # Append the default fieldset if defined in the Meta class - exec_parameters = ('_schedule_at', '_interval', '_commit') if self.scheduling_enabled else ('_commit',) + if self.scheduling_enabled: + exec_parameters = ('_schedule_at', '_interval', '_commit', '_notifications') + else: + exec_parameters = ('_commit', '_notifications') fieldsets.append((_('Script Execution Parameters'), exec_parameters)) return fieldsets @@ -511,6 +519,9 @@ class BaseScript: # Set initial "commit" checkbox state based on the script's Meta parameter form.fields['_commit'].initial = self.commit_default + # Set initial "notifications" selection based on the script's Meta parameter + form.fields['_notifications'].initial = self.notifications_default + # Hide fields if scheduling has been disabled if not self.scheduling_enabled: form.fields['_schedule_at'].widget = forms.HiddenInput() diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index 3ca68a922..3df1d7c70 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -121,6 +121,10 @@ class CustomFieldTable(NetBoxTable): validation_regex = tables.Column( verbose_name=_('Validation Regex'), ) + validation_schema = columns.BooleanColumn( + verbose_name=_('Validation Schema'), + false_mark=None, + ) owner = tables.Column( linkify=True, verbose_name=_('Owner') @@ -132,7 +136,7 @@ class CustomFieldTable(NetBoxTable): 'pk', 'id', 'name', 'object_types', 'label', 'type', 'related_object_type', 'group_name', 'required', 'unique', 'default', 'description', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'is_cloneable', 'weight', 'choice_set', 'choices', 'validation_minimum', 'validation_maximum', - 'validation_regex', 'comments', 'created', 'last_updated', + 'validation_regex', 'validation_schema', 'comments', 'created', 'last_updated', ) default_columns = ( 'pk', 'name', 'object_types', 'label', 'group_name', 'type', 'required', 'unique', 'description', @@ -699,6 +703,10 @@ class ConfigTemplateTable(NetBoxTable): verbose_name=_('As Attachment'), false_mark=None ) + debug = columns.BooleanColumn( + verbose_name=_('Debug'), + false_mark=None + ) owner = tables.Column( linkify=True, verbose_name=_('Owner') @@ -730,7 +738,7 @@ class ConfigTemplateTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = ConfigTemplate fields = ( - 'pk', 'id', 'name', 'description', 'data_source', 'data_file', 'data_synced', 'as_attachment', + 'pk', 'id', 'name', 'description', 'data_source', 'data_file', 'data_synced', 'as_attachment', 'debug', 'mime_type', 'file_name', 'file_extension', 'role_count', 'platform_count', 'device_count', 'vm_count', 'created', 'last_updated', 'tags', ) diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index a50ce72a0..22fea092c 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -215,6 +215,10 @@ class CustomFieldChoiceSetTest(APIViewTestCases.APIViewTestCase): ['4B', 'Choice 2'], ['4C', 'Choice 3'], ], + 'choice_colors': { + '4A': 'red', + '4B': 'green', + }, }, { 'name': 'Choice Set 5', @@ -223,6 +227,9 @@ class CustomFieldChoiceSetTest(APIViewTestCases.APIViewTestCase): ['5B', 'Choice 2'], ['5C', 'Choice 3'], ], + 'choice_colors': { + '5C': 'blue', + }, }, { 'name': 'Choice Set 6', @@ -243,6 +250,10 @@ class CustomFieldChoiceSetTest(APIViewTestCases.APIViewTestCase): ['X2', 'Choice 2'], ['X3', 'Choice 3'], ], + 'choice_colors': { + 'X1': 'red', + 'X3': 'green', + }, 'description': 'New description', } @@ -281,6 +292,38 @@ class CustomFieldChoiceSetTest(APIViewTestCases.APIViewTestCase): response = self.client.post(self._get_list_url(), data, format='json', **self.header) self.assertEqual(response.status_code, 400) + def test_invalid_choice_color(self): + self.add_permissions('extras.add_customfieldchoiceset') + data = { + 'name': 'test', + 'extra_choices': [ + ['choice1', 'Choice 1'], + ['choice2', 'Choice 2'], + ], + 'choice_colors': { + 'choice1': 'magenta', + }, + } + + response = self.client.post(self._get_list_url(), data, format='json', **self.header) + self.assertEqual(response.status_code, 400) + + def test_invalid_choice_color_reference(self): + self.add_permissions('extras.add_customfieldchoiceset') + data = { + 'name': 'test', + 'extra_choices': [ + ['choice1', 'Choice 1'], + ['choice2', 'Choice 2'], + ], + 'choice_colors': { + 'choice3': 'red', + }, + } + + response = self.client.post(self._get_list_url(), data, format='json', **self.header) + self.assertEqual(response.status_code, 400) + class CustomLinkTest(APIViewTestCases.APIViewTestCase): model = CustomLink diff --git a/netbox/extras/tests/test_customfields.py b/netbox/extras/tests/test_customfields.py index 99a268f37..0b9382701 100644 --- a/netbox/extras/tests/test_customfields.py +++ b/netbox/extras/tests/test_customfields.py @@ -399,6 +399,73 @@ class CustomFieldTest(TestCase): instance.refresh_from_db() self.assertIsNone(instance.custom_field_data.get(cf.name)) + def test_choice_set_colors(self): + choice_set = CustomFieldChoiceSet( + name='Test Choice Set', + extra_choices=( + ('a', 'Option A'), + ('b', 'Option B'), + ), + choice_colors={ + 'a': CustomFieldChoiceColorChoices.RED, + 'b': CustomFieldChoiceColorChoices.GREEN, + }, + ) + choice_set.full_clean() + + self.assertEqual( + choice_set.colors, + { + 'a': CustomFieldChoiceColorChoices.RED, + 'b': CustomFieldChoiceColorChoices.GREEN, + }, + ) + + def test_choice_set_invalid_color_mapping_value(self): + choice_set = CustomFieldChoiceSet( + name='Test Choice Set', + extra_choices=( + ('a', 'Option A'), + ('b', 'Option B'), + ), + choice_colors={'c': CustomFieldChoiceColorChoices.RED}, + ) + + with self.assertRaises(ValidationError) as cm: + choice_set.full_clean() + + self.assertIn('choice_colors', cm.exception.message_dict) + + def test_choice_set_invalid_color_value(self): + choice_set = CustomFieldChoiceSet( + name='Test Choice Set', + extra_choices=( + ('a', 'Option A'), + ('b', 'Option B'), + ), + choice_colors={'a': 'magenta'}, + ) + + with self.assertRaises(ValidationError) as cm: + choice_set.full_clean() + + self.assertIn('choice_colors', cm.exception.message_dict) + + def test_choice_set_invalid_color_mapping_structure(self): + choice_set = CustomFieldChoiceSet( + name='Test Choice Set', + extra_choices=( + ('a', 'Option A'), + ('b', 'Option B'), + ), + choice_colors=['a:red'], + ) + + with self.assertRaises(ValidationError) as cm: + choice_set.full_clean() + + self.assertIn('choice_colors', cm.exception.message_dict) + def test_remove_selected_choice(self): """ Removing a ChoiceSet choice that is referenced by an object should raise @@ -655,6 +722,45 @@ class CustomFieldTest(TestCase): default=["xxx"] ).full_clean() + def test_validation_schema_only_for_json_type(self): + schema = { + 'type': 'object', + 'properties': { + 'name': {'type': 'string'}, + }, + } + + # Valid: schema on a JSON field + CustomField(name='test', type=CustomFieldTypeChoices.TYPE_JSON, validation_schema=schema).full_clean() + + # Invalid: schema on a non-JSON field + with self.assertRaises(ValidationError): + CustomField(name='test', type=CustomFieldTypeChoices.TYPE_TEXT, validation_schema=schema).full_clean() + with self.assertRaises(ValidationError): + CustomField(name='test', type=CustomFieldTypeChoices.TYPE_INTEGER, validation_schema=schema).full_clean() + + def test_json_schema_default_validation(self): + schema = { + 'type': 'object', + 'properties': { + 'name': {'type': 'string'}, + }, + 'required': ['name'], + } + + # Valid default + CustomField( + name='test', type=CustomFieldTypeChoices.TYPE_JSON, + validation_schema=schema, default={'name': 'test'} + ).full_clean() + + # Invalid default (missing required 'name') + with self.assertRaises(ValidationError): + CustomField( + name='test', type=CustomFieldTypeChoices.TYPE_JSON, + validation_schema=schema, default={'age': 25} + ).full_clean() + class CustomFieldManagerTest(TestCase): @@ -1398,6 +1504,42 @@ class CustomFieldAPITest(APITestCase): response = self.client.patch(url, data, format='json', **self.header) self.assertHttpStatus(response, status.HTTP_200_OK) + def test_json_schema_validation(self): + site2 = Site.objects.get(name='Site 2') + url = reverse('dcim-api:site-detail', kwargs={'pk': site2.pk}) + self.add_permissions('dcim.change_site') + + cf_json = CustomField.objects.get(name='json_field') + cf_json.validation_schema = { + 'type': 'object', + 'properties': { + 'name': {'type': 'string'}, + 'age': {'type': 'integer'}, + }, + 'required': ['name'], + } + cf_json.save() + + # Invalid: missing required 'name' property + data = {'custom_fields': {'json_field': {'age': 25}}} + response = self.client.patch(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + + # Invalid: 'age' is not an integer + data = {'custom_fields': {'json_field': {'name': 'test', 'age': 'not_an_int'}}} + response = self.client.patch(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + + # Valid: conforms to schema + data = {'custom_fields': {'json_field': {'name': 'test', 'age': 25}}} + response = self.client.patch(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + + # Valid: null value (schema not enforced on empty) + data = {'custom_fields': {'json_field': None}} + response = self.client.patch(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + def test_uniqueness_validation(self): # Create a unique custom field cf_text = CustomField.objects.get(name='text_field') diff --git a/netbox/extras/tests/test_event_rules.py b/netbox/extras/tests/test_event_rules.py index 8426a3fbb..50095df22 100644 --- a/netbox/extras/tests/test_event_rules.py +++ b/netbox/extras/tests/test_event_rules.py @@ -348,6 +348,7 @@ class EventRuleTest(APITestCase): @skipIf('netbox.tests.dummy_plugin' not in settings.PLUGINS, 'dummy_plugin not in settings.PLUGINS') def test_send_webhook(self): request_id = uuid.uuid4() + url_path = reverse('dcim:site_add') def dummy_send(_, request, **kwargs): """ @@ -373,11 +374,15 @@ class EventRuleTest(APITestCase): self.assertEqual(body['data']['name'], 'Site 1') self.assertEqual(body['data']['foo'], 1) self.assertEqual(body['context']['foo'], 123) # From netbox.tests.dummy_plugin + self.assertEqual(body['request']['id'], str(request_id)) + self.assertEqual(body['request']['method'], 'GET') + self.assertEqual(body['request']['path'], url_path) + self.assertEqual(body['request']['user'], 'testuser') return HttpResponse() # Create a dummy request - request = RequestFactory().get(reverse('dcim:site_add')) + request = RequestFactory().get(url_path) request.id = request_id request.user = self.user diff --git a/netbox/extras/tests/test_filtersets.py b/netbox/extras/tests/test_filtersets.py index 4eb8533eb..c9727ad2f 100644 --- a/netbox/extras/tests/test_filtersets.py +++ b/netbox/extras/tests/test_filtersets.py @@ -22,7 +22,7 @@ from virtualization.models import Cluster, ClusterGroup, ClusterType class CustomFieldTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = CustomField.objects.all() filterset = CustomFieldFilterSet - ignore_fields = ('default', 'related_object_filter') + ignore_fields = ('default', 'related_object_filter', 'validation_schema') @classmethod def setUpTestData(cls): @@ -160,8 +160,21 @@ class CustomFieldChoiceSetTestCase(TestCase, ChangeLoggedFilterSetTests): @classmethod def setUpTestData(cls): choice_sets = ( - CustomFieldChoiceSet(name='Choice Set 1', extra_choices=['A', 'B', 'C'], description='foobar1'), - CustomFieldChoiceSet(name='Choice Set 2', extra_choices=['D', 'E', 'F'], description='foobar2'), + CustomFieldChoiceSet( + name='Choice Set 1', + extra_choices=['A', 'B', 'C'], + choice_colors={'A': CustomFieldChoiceColorChoices.RED}, + description='foobar1', + ), + CustomFieldChoiceSet( + name='Choice Set 2', + extra_choices=['D', 'E', 'F'], + choice_colors={ + 'D': CustomFieldChoiceColorChoices.GREEN, + 'E': CustomFieldChoiceColorChoices.RED, + }, + description='foobar2', + ), CustomFieldChoiceSet(name='Choice Set 3', extra_choices=['G', 'H', 'I'], description='foobar3'), ) CustomFieldChoiceSet.objects.bulk_create(choice_sets) @@ -178,6 +191,16 @@ class CustomFieldChoiceSetTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'choice': ['A', 'D']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_choice_colors(self): + params = {'choice_colors': [CustomFieldChoiceColorChoices.RED]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + params = {'choice_colors': [CustomFieldChoiceColorChoices.GREEN]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + params = {'choice_colors': [CustomFieldChoiceColorChoices.YELLOW]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) + def test_description(self): params = {'description': ['foobar1', 'foobar2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -1223,6 +1246,7 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests): 'asn', 'asnrange', 'cable', + 'cablebundle', 'circuit', 'circuitgroup', 'circuitgroupassignment', @@ -1279,6 +1303,7 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests): 'provideraccount', 'providernetwork', 'rack', + 'rackgroup', 'rackreservation', 'rackrole', 'racktype', @@ -1303,6 +1328,7 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests): 'virtualdevicecontext', 'virtualdisk', 'virtualmachine', + 'virtualmachinetype', 'vlan', 'vlangroup', 'vlantranslationpolicy', diff --git a/netbox/extras/tests/test_forms.py b/netbox/extras/tests/test_forms.py index b9f65c5e4..aea84e92c 100644 --- a/netbox/extras/tests/test_forms.py +++ b/netbox/extras/tests/test_forms.py @@ -115,6 +115,32 @@ class CustomFieldChoiceSetFormTest(TestCase): # cleaned extra choices are correct, which does actually mean a list of tuples self.assertEqual(updated.extra_choices, [('foo:bar', 'label'), ('value', 'label:with:colons')]) + def test_choice_colors_round_trip_on_edit(self): + choice_set = CustomFieldChoiceSet.objects.create( + name='Test Choice Set', + extra_choices=[['foo:bar', 'label'], ['choice2', 'Choice 2']], + choice_colors={'foo:bar': 'red', 'choice2': 'green'}, + ) + + form = CustomFieldChoiceSetForm(instance=choice_set) + initial_choices = form.initial['extra_choices'] + initial_choice_colors = form.initial['choice_colors'] + + self.assertEqual(initial_choice_colors, 'choice2:green\nfoo\\:bar:red') + + form = CustomFieldChoiceSetForm( + { + 'name': choice_set.name, + 'extra_choices': initial_choices, + 'choice_colors': initial_choice_colors, + }, + instance=choice_set, + ) + self.assertTrue(form.is_valid()) + updated = form.save() + + self.assertEqual(updated.choice_colors, {'choice2': 'green', 'foo:bar': 'red'}) + class SavedFilterFormTest(TestCase): diff --git a/netbox/extras/tests/test_models.py b/netbox/extras/tests/test_models.py index 626cd219e..a58bf0bca 100644 --- a/netbox/extras/tests/test_models.py +++ b/netbox/extras/tests/test_models.py @@ -19,6 +19,7 @@ from extras.models import ( ConfigContextProfile, ConfigTemplate, EventRule, + ExportTemplate, ImageAttachment, Tag, TaggedItem, @@ -771,15 +772,19 @@ class ConfigContextTest(TestCase): if hasattr(node, 'children'): for child in node.children: try: - if child.rhs.query.model is TaggedItem: - subqueries.append(child.rhs.query) + # In Django 6.0+, rhs is a Query directly; older Django wraps it in Subquery + rhs_query = getattr(child.rhs, 'query', child.rhs) + if rhs_query.model is TaggedItem: + subqueries.append(rhs_query) except AttributeError: traverse(child) traverse(where_node) return subqueries + # In Django 6.0+, the annotation is a Query directly; older Django wraps it in Subquery + annotation_query = getattr(config_annotation, 'query', config_annotation) # Find subqueries in the WHERE clause that should have DISTINCT - tag_subqueries = find_tag_subqueries(config_annotation.query.where) + tag_subqueries = find_tag_subqueries(annotation_query.where) distinct_subqueries = [sq for sq in tag_subqueries if sq.distinct] # Verify we found at least one DISTINCT subquery for tags @@ -900,6 +905,87 @@ class ConfigTemplateTest(TestCase): self.assertEqual(autosync_records.count(), 0, "AutoSyncRecord should be deleted after detaching") +class ConfigTemplateDebugTest(TestCase): + """ + Tests for the ConfigTemplate debug field and its effect on template rendering error output. + """ + + def _make_template(self, template_code, debug=False): + t = ConfigTemplate( + name=f"DebugTestTemplate-{debug}", + template_code=template_code, + debug=debug, + ) + t.save() + return t + + def test_debug_default_is_false(self): + t = ConfigTemplate(name="t", template_code="hello") + self.assertFalse(t.debug) + + def test_template_error_non_debug_no_traceback(self): + """In non-debug mode, a TemplateError raises with no traceback exposure.""" + from jinja2 import TemplateError + t = self._make_template("{{ unclosed", debug=False) + with self.assertRaises(TemplateError): + t.render({}) + + def test_template_error_debug_mode_raises(self): + """In debug mode, a TemplateError still raises (callers handle display).""" + from jinja2 import TemplateError + t = self._make_template("{{ unclosed", debug=True) + with self.assertRaises(TemplateError): + t.render({}) + + def test_render_jinja2_debug_extension_enabled(self): + """When debug=True, the Jinja2 debug extension is loaded in the environment.""" + from utilities.jinja2 import render_jinja2 + # The {% debug %} tag is only available when the debug extension is loaded. + output = render_jinja2("{% debug %}", {}, debug=True) + self.assertIsInstance(output, str) + + def test_render_jinja2_debug_extension_not_loaded_by_default(self): + """When debug=False, the {% debug %} tag is not available.""" + from jinja2 import TemplateSyntaxError + + from utilities.jinja2 import render_jinja2 + with self.assertRaises(TemplateSyntaxError): + render_jinja2("{% debug %}", {}, debug=False) + + +class ExportTemplateContextTest(TestCase): + """ + Tests for ExportTemplate.get_context() including public model population. + """ + + def test_get_context_includes_public_models(self): + et = ExportTemplate(name='test', template_code='test') + ctx = et.get_context() + + self.assertIs(ctx['dcim']['Site'], Site) + self.assertIs(ctx['dcim']['Device'], Device) + + def test_get_context_includes_queryset(self): + et = ExportTemplate(name='test', template_code='test') + qs = Site.objects.all() + ctx = et.get_context(queryset=qs) + + self.assertIs(ctx['queryset'], qs) + + def test_get_context_applies_extra_context(self): + et = ExportTemplate(name='test', template_code='test') + ctx = et.get_context(context={'custom_key': 'custom_value'}) + + self.assertEqual(ctx['custom_key'], 'custom_value') + self.assertIs(ctx['dcim']['Site'], Site) + + def test_config_template_get_context_includes_public_models(self): + ct = ConfigTemplate(name='test', template_code='test') + ctx = ct.get_context() + + self.assertIs(ctx['dcim']['Site'], Site) + + class EventRuleTest(TestCase): def test_action_data_clean_accepts_dict(self): diff --git a/netbox/extras/ui/panels.py b/netbox/extras/ui/panels.py index d347089e0..23a3da37b 100644 --- a/netbox/extras/ui/panels.py +++ b/netbox/extras/ui/panels.py @@ -1,5 +1,4 @@ from django.contrib.contenttypes.models import ContentType -from django.template.loader import render_to_string from django.utils.translation import gettext_lazy as _ from netbox.ui import actions, attrs, panels @@ -65,12 +64,8 @@ class CustomFieldsPanel(panels.ObjectPanel): 'custom_fields': obj.get_custom_fields_by_group(), } - def render(self, context): - ctx = self.get_context(context) - # Hide the panel if no custom fields exist - if not ctx['custom_fields']: - return '' - return render_to_string(self.template_name, self.get_context(context)) + def should_render(self, context): + return bool(context['custom_fields']) class ImageAttachmentsPanel(panels.ObjectsTablePanel): @@ -470,6 +465,7 @@ class ConfigTemplatePanel(panels.ObjectAttributesPanel): file_name = attrs.TextAttr('file_name') file_extension = attrs.TextAttr('file_extension') as_attachment = attrs.BooleanAttr('as_attachment', label=_('Attachment')) + debug = attrs.BooleanAttr('debug') data_source = attrs.RelatedObjectAttr('data_source', linkify=True) data_file = attrs.TemplatedAttr( 'data_path', diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 08b7d3ecf..080613950 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -12,7 +12,6 @@ from django.utils import timezone from django.utils.module_loading import import_string from django.utils.translation import gettext_lazy as _ from django.views.generic import View -from jinja2.exceptions import TemplateError from core.choices import ManagedFileRootPathChoices from core.models import Job @@ -66,6 +65,7 @@ class CustomFieldListView(generic.ObjectListView): @register_model_view(CustomField) class CustomFieldView(generic.ObjectView): queryset = CustomField.objects.select_related('choice_set') + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.CustomFieldPanel(), @@ -149,6 +149,7 @@ class CustomFieldChoiceSetListView(generic.ObjectListView): @register_model_view(CustomFieldChoiceSet) class CustomFieldChoiceSetView(generic.ObjectView): queryset = CustomFieldChoiceSet.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.CustomFieldChoiceSetPanel(), @@ -166,7 +167,15 @@ class CustomFieldChoiceSetView(generic.ObjectView): page_number = request.GET.get('page', 1) except ValueError: page_number = 1 - paginator = EnhancedPaginator(instance.choices, per_page) + choice_rows = [ + { + 'value': value, + 'label': label, + 'color': instance.get_choice_color(value), + } + for value, label in instance.choices + ] + paginator = EnhancedPaginator(choice_rows, per_page) try: choices = paginator.page(page_number) except EmptyPage: @@ -232,6 +241,7 @@ class CustomLinkListView(generic.ObjectListView): @register_model_view(CustomLink) class CustomLinkView(generic.ObjectView): queryset = CustomLink.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.CustomLinkPanel(), @@ -299,6 +309,7 @@ class ExportTemplateListView(generic.ObjectListView): @register_model_view(ExportTemplate) class ExportTemplateView(generic.ObjectView): queryset = ExportTemplate.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.ExportTemplatePanel(), @@ -373,6 +384,7 @@ class SavedFilterListView(SharedObjectViewMixin, generic.ObjectListView): @register_model_view(SavedFilter) class SavedFilterView(SharedObjectViewMixin, generic.ObjectView): queryset = SavedFilter.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.SavedFilterPanel(), @@ -444,6 +456,7 @@ class TableConfigListView(SharedObjectViewMixin, generic.ObjectListView): @register_model_view(TableConfig) class TableConfigView(SharedObjectViewMixin, generic.ObjectView): queryset = TableConfig.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.TableConfigPanel(), @@ -546,6 +559,7 @@ class NotificationGroupListView(generic.ObjectListView): @register_model_view(NotificationGroup) class NotificationGroupView(generic.ObjectView): queryset = NotificationGroup.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.NotificationGroupPanel(), @@ -739,6 +753,7 @@ class WebhookListView(generic.ObjectListView): @register_model_view(Webhook) class WebhookView(generic.ObjectView): queryset = Webhook.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.WebhookPanel(), @@ -808,6 +823,7 @@ class EventRuleListView(generic.ObjectListView): @register_model_view(EventRule) class EventRuleView(generic.ObjectView): queryset = EventRule.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.EventRulePanel(), @@ -879,6 +895,7 @@ class TagListView(generic.ObjectListView): @register_model_view(Tag) class TagView(generic.ObjectView): queryset = Tag.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.TagPanel(), @@ -970,6 +987,7 @@ class ConfigContextProfileListView(generic.ObjectListView): @register_model_view(ConfigContextProfile) class ConfigContextProfileView(generic.ObjectView): queryset = ConfigContextProfile.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.ConfigContextProfilePanel(), @@ -1044,6 +1062,7 @@ class ConfigContextListView(generic.ObjectListView): @register_model_view(ConfigContext) class ConfigContextView(generic.ObjectView): queryset = ConfigContext.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.ConfigContextPanel(), @@ -1173,6 +1192,7 @@ class ConfigTemplateListView(generic.ObjectListView): @register_model_view(ConfigTemplate) class ConfigTemplateView(generic.ObjectView): queryset = ConfigTemplate.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.ConfigTemplatePanel(), @@ -1284,8 +1304,8 @@ class ObjectRenderConfigView(generic.ObjectView): if config_template: try: rendered_config = config_template.render(context=context_data) - except TemplateError as e: - error_message = _("An error occurred while rendering the template: {error}").format(error=e) + except Exception as e: + error_message = config_template.format_render_error(e) return { 'base_template': self.base_template, @@ -1312,6 +1332,7 @@ class ImageAttachmentListView(generic.ObjectListView): @register_model_view(ImageAttachment) class ImageAttachmentView(generic.ObjectView): queryset = ImageAttachment.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.ImageAttachmentPanel(), @@ -1717,6 +1738,7 @@ class ScriptView(BaseScriptView): user=request.user, schedule_at=form.cleaned_data.pop('_schedule_at'), interval=form.cleaned_data.pop('_interval'), + notifications=form.cleaned_data.pop('_notifications'), data=form.cleaned_data, request=copy_safe_request(request), job_timeout=script.python_class.job_timeout, diff --git a/netbox/extras/webhooks.py b/netbox/extras/webhooks.py index e30fe976e..0a68216ea 100644 --- a/netbox/extras/webhooks.py +++ b/netbox/extras/webhooks.py @@ -57,6 +57,13 @@ def send_webhook(event_rule, object_type, event_type, data, timestamp, username, 'request_id': request.id if request else None, 'data': data, } + if request: + context['request'] = { + 'id': str(request.id) if request.id else None, + 'method': request.method, + 'path': request.path, + 'user': str(request.user), + } if snapshots: context.update({ 'snapshots': snapshots diff --git a/netbox/ipam/api/serializers_/asns.py b/netbox/ipam/api/serializers_/asns.py index 334755734..c5c95bf19 100644 --- a/netbox/ipam/api/serializers_/asns.py +++ b/netbox/ipam/api/serializers_/asns.py @@ -6,6 +6,8 @@ from netbox.api.fields import RelatedObjectCountField, SerializedPKRelatedField from netbox.api.serializers import OrganizationalModelSerializer, PrimaryModelSerializer from tenancy.api.serializers_.tenants import TenantSerializer +from .roles import RoleSerializer + __all__ = ( 'ASNRangeSerializer', 'ASNSerializer', @@ -56,6 +58,7 @@ class ASNSiteSerializer(PrimaryModelSerializer): class ASNSerializer(PrimaryModelSerializer): rir = RIRSerializer(nested=True, required=False, allow_null=True) + role = RoleSerializer(nested=True, required=False, allow_null=True) tenant = TenantSerializer(nested=True, required=False, allow_null=True) sites = SerializedPKRelatedField( queryset=Site.objects.all(), @@ -72,8 +75,8 @@ class ASNSerializer(PrimaryModelSerializer): class Meta: model = ASN fields = [ - 'id', 'url', 'display_url', 'display', 'asn', 'rir', 'tenant', 'description', 'owner', 'comments', 'tags', - 'custom_fields', 'created', 'last_updated', 'site_count', 'provider_count', 'sites', + 'id', 'url', 'display_url', 'display', 'asn', 'rir', 'role', 'tenant', 'description', 'owner', 'comments', + 'tags', 'custom_fields', 'created', 'last_updated', 'site_count', 'provider_count', 'sites', ] brief_fields = ('id', 'url', 'display', 'asn', 'description') diff --git a/netbox/ipam/api/serializers_/roles.py b/netbox/ipam/api/serializers_/roles.py index 80a892659..8e62e4095 100644 --- a/netbox/ipam/api/serializers_/roles.py +++ b/netbox/ipam/api/serializers_/roles.py @@ -12,11 +12,13 @@ class RoleSerializer(OrganizationalModelSerializer): # Related object counts prefix_count = RelatedObjectCountField('prefixes') vlan_count = RelatedObjectCountField('vlans') + asn_count = RelatedObjectCountField('asns') class Meta: model = Role fields = [ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'weight', 'description', 'owner', 'comments', 'tags', - 'custom_fields', 'created', 'last_updated', 'prefix_count', 'vlan_count', + 'custom_fields', 'created', 'last_updated', 'prefix_count', 'vlan_count', 'asn_count', ] - brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'prefix_count', 'vlan_count') + brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'prefix_count', 'vlan_count', + 'asn_count') diff --git a/netbox/ipam/api/serializers_/vlans.py b/netbox/ipam/api/serializers_/vlans.py index 5e03074cd..a5ae39077 100644 --- a/netbox/ipam/api/serializers_/vlans.py +++ b/netbox/ipam/api/serializers_/vlans.py @@ -36,6 +36,7 @@ class VLANGroupSerializer(OrganizationalModelSerializer): scope_id = serializers.IntegerField(allow_null=True, required=False, default=None) scope = GFKSerializerField(read_only=True) vid_ranges = IntegerRangeSerializer(many=True, required=False) + total_vlan_ids = serializers.IntegerField(read_only=True) utilization = serializers.CharField(read_only=True) tenant = TenantSerializer(nested=True, required=False, allow_null=True) @@ -46,7 +47,8 @@ class VLANGroupSerializer(OrganizationalModelSerializer): model = VLANGroup fields = [ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'scope_type', 'scope_id', 'scope', 'vid_ranges', - 'tenant', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', + 'total_vlan_ids', 'tenant', 'description', 'owner', 'comments', 'tags', 'custom_fields', + 'created', 'last_updated', 'vlan_count', 'utilization', ] brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'vlan_count') diff --git a/netbox/ipam/constants.py b/netbox/ipam/constants.py index 44c5e7d39..223b785b3 100644 --- a/netbox/ipam/constants.py +++ b/netbox/ipam/constants.py @@ -74,7 +74,7 @@ VLAN_VID_MAX = 4094 # models values for ContentTypes which may be VLANGroup scope types VLANGROUP_SCOPE_TYPES = ( - 'region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster', + 'region', 'sitegroup', 'site', 'location', 'rackgroup', 'rack', 'clustergroup', 'cluster', ) diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index 56d2fb2b7..bba050215 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -289,6 +289,18 @@ class ASNFilterSet(PrimaryModelFilterSet, TenancyFilterSet): to_field_name='slug', label=_('Provider (slug)'), ) + role_id = django_filters.ModelMultipleChoiceFilter( + queryset=Role.objects.all(), + distinct=False, + label=_('Role (ID)'), + ) + role = django_filters.ModelMultipleChoiceFilter( + field_name='role__slug', + queryset=Role.objects.all(), + distinct=False, + to_field_name='slug', + label=_('Role (slug)'), + ) class Meta: model = ASN @@ -946,6 +958,9 @@ class VLANGroupFilterSet(OrganizationalModelFilterSet, TenancyFilterSet): location = django_filters.NumberFilter( method='filter_scope' ) + rack_group = django_filters.NumberFilter( + method='filter_scope' + ) rack = django_filters.NumberFilter( method='filter_scope' ) @@ -962,7 +977,7 @@ class VLANGroupFilterSet(OrganizationalModelFilterSet, TenancyFilterSet): class Meta: model = VLANGroup - fields = ('id', 'name', 'slug', 'description', 'scope_id') + fields = ('id', 'name', 'slug', 'description', 'scope_id', 'total_vlan_ids') def search(self, queryset, name, value): if not value.strip(): diff --git a/netbox/ipam/forms/bulk_create.py b/netbox/ipam/forms/bulk_create.py index 856476786..763dbac9c 100644 --- a/netbox/ipam/forms/bulk_create.py +++ b/netbox/ipam/forms/bulk_create.py @@ -1,14 +1,17 @@ from django import forms from django.utils.translation import gettext_lazy as _ -from utilities.forms.fields import ExpandableIPAddressField +from utilities.forms.fields import ExpandableIPNetworkField __all__ = ( - 'IPAddressBulkCreateForm', + 'IPNetworkBulkCreateForm', ) -class IPAddressBulkCreateForm(forms.Form): - pattern = ExpandableIPAddressField( - label=_('Address pattern') +class IPNetworkBulkCreateForm(forms.Form): + """ + Pattern form for bulk-creating IP-based objects (addresses, prefixes). + """ + pattern = ExpandableIPNetworkField( + label=_('Pattern') ) diff --git a/netbox/ipam/forms/bulk_edit.py b/netbox/ipam/forms/bulk_edit.py index 02e18bbe3..fd0afffa1 100644 --- a/netbox/ipam/forms/bulk_edit.py +++ b/netbox/ipam/forms/bulk_edit.py @@ -121,6 +121,11 @@ class ASNBulkEditForm(PrimaryModelBulkEditForm): required=False, label=_('RIR') ) + role = DynamicModelChoiceField( + queryset=Role.objects.all(), + required=False, + label=_('Role') + ) tenant = DynamicModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), @@ -129,9 +134,9 @@ class ASNBulkEditForm(PrimaryModelBulkEditForm): model = ASN fieldsets = ( - FieldSet('sites', 'rir', 'tenant', 'description'), + FieldSet('sites', 'rir', 'role', 'tenant', 'description'), ) - nullable_fields = ('tenant', 'description', 'comments') + nullable_fields = ('role', 'tenant', 'description', 'comments') class AggregateBulkEditForm(PrimaryModelBulkEditForm): diff --git a/netbox/ipam/forms/bulk_import.py b/netbox/ipam/forms/bulk_import.py index dd6ac835d..ec68b54c4 100644 --- a/netbox/ipam/forms/bulk_import.py +++ b/netbox/ipam/forms/bulk_import.py @@ -138,6 +138,13 @@ class ASNImportForm(PrimaryModelImportForm): to_field_name='name', help_text=_('Assigned RIR') ) + role = CSVModelChoiceField( + label=_('Role'), + queryset=Role.objects.all(), + required=False, + to_field_name='name', + help_text=_('Functional role') + ) tenant = CSVModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), @@ -148,7 +155,7 @@ class ASNImportForm(PrimaryModelImportForm): class Meta: model = ASN - fields = ('asn', 'rir', 'tenant', 'description', 'owner', 'comments', 'tags') + fields = ('asn', 'rir', 'role', 'tenant', 'description', 'owner', 'comments', 'tags') class RoleImportForm(OrganizationalModelImportForm): diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py index 0bfbd53a4..9433f3cb3 100644 --- a/netbox/ipam/forms/filtersets.py +++ b/netbox/ipam/forms/filtersets.py @@ -1,7 +1,7 @@ from django import forms from django.utils.translation import gettext_lazy as _ -from dcim.models import Device, Location, Rack, Region, Site, SiteGroup +from dcim.models import Device, Location, Rack, RackGroup, Region, Site, SiteGroup from ipam.choices import * from ipam.constants import * from ipam.models import * @@ -151,7 +151,7 @@ class ASNFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm): model = ASN fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('rir_id', 'site_group_id', 'site_id', name=_('Assignment')), + FieldSet('rir_id', 'role_id', 'site_group_id', 'site_id', name=_('Assignment')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), FieldSet('owner_group_id', 'owner_id', name=_('Ownership')), ) @@ -160,6 +160,11 @@ class ASNFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm): required=False, label=_('RIR') ) + role_id = DynamicModelMultipleChoiceField( + queryset=Role.objects.all(), + required=False, + label=_('Role') + ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, @@ -453,9 +458,9 @@ class FHRPGroupFilterForm(PrimaryModelFilterSetForm): class VLANGroupFilterForm(TenancyFilterForm, OrganizationalModelFilterSetForm): fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('region', 'site_group', 'site', 'location', 'rack', name=_('Location')), + FieldSet('region', 'site_group', 'site', 'location', 'rack_group', 'rack', name=_('Location')), FieldSet('cluster_group', 'cluster', name=_('Cluster')), - FieldSet('contains_vid', name=_('VLANs')), + FieldSet('contains_vid', 'total_vlan_ids', name=_('VLANs')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), FieldSet('owner_group_id', 'owner_id', name=_('Ownership')), ) @@ -480,6 +485,11 @@ class VLANGroupFilterForm(TenancyFilterForm, OrganizationalModelFilterSetForm): required=False, label=_('Location') ) + rack_group = DynamicModelMultipleChoiceField( + queryset=RackGroup.objects.all(), + required=False, + label=_('Rack group') + ) rack = DynamicModelMultipleChoiceField( queryset=Rack.objects.all(), required=False, @@ -500,6 +510,11 @@ class VLANGroupFilterForm(TenancyFilterForm, OrganizationalModelFilterSetForm): required=False, label=_('Contains VLAN ID') ) + total_vlan_ids = forms.IntegerField( + min_value=0, + required=False, + label=_('Total VLAN IDs') + ) tag = TagFilterField(model) diff --git a/netbox/ipam/forms/model_forms.py b/netbox/ipam/forms/model_forms.py index 1bdcff2d8..bffe7a1eb 100644 --- a/netbox/ipam/forms/model_forms.py +++ b/netbox/ipam/forms/model_forms.py @@ -37,6 +37,7 @@ __all__ = ( 'IPAddressBulkAddForm', 'IPAddressForm', 'IPRangeForm', + 'PrefixBulkAddForm', 'PrefixForm', 'RIRForm', 'RoleForm', @@ -152,6 +153,12 @@ class ASNForm(TenancyForm, PrimaryModelForm): label=_('RIR'), quick_add=True ) + role = DynamicModelChoiceField( + queryset=Role.objects.all(), + label=_('Role'), + required=False, + quick_add=True + ) sites = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), label=_('Sites'), @@ -159,14 +166,14 @@ class ASNForm(TenancyForm, PrimaryModelForm): ) fieldsets = ( - FieldSet('asn', 'rir', 'sites', 'description', 'tags', name=_('ASN')), + FieldSet('asn', 'rir', 'role', 'sites', 'description', 'tags', name=_('ASN')), FieldSet('tenant_group', 'tenant', name=_('Tenancy')), ) class Meta: model = ASN fields = [ - 'asn', 'rir', 'sites', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags' + 'asn', 'rir', 'role', 'sites', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags' ] widgets = { 'date_added': DatePicker(), @@ -243,6 +250,23 @@ class PrefixForm(TenancyForm, ScopedForm, PrimaryModelForm): self.fields['vlan'].widget.attrs.pop('data-dynamic-params', None) +class PrefixBulkAddForm(PrefixForm): + """ + Subclass of PrefixForm for bulk creation. The prefix field is inherited + but excluded from fieldsets — it is populated programmatically by BulkCreateView + from the expanded pattern. + """ + + fieldsets = ( + FieldSet( + 'status', 'vrf', 'role', 'is_pool', 'mark_utilized', 'description', 'tags', name=_('Prefix') + ), + FieldSet('scope_type', 'scope', name=_('Scope')), + FieldSet('vlan', name=_('VLAN Assignment')), + FieldSet('tenant_group', 'tenant', name=_('Tenancy')), + ) + + class IPRangeForm(TenancyForm, PrimaryModelForm): vrf = DynamicModelChoiceField( queryset=VRF.objects.all(), @@ -459,17 +483,23 @@ class IPAddressForm(TenancyForm, PrimaryModelForm): return ipaddress -class IPAddressBulkAddForm(TenancyForm, NetBoxModelForm): +class IPAddressBulkAddForm(TenancyForm, PrimaryModelForm): vrf = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, label=_('VRF') ) + fieldsets = ( + FieldSet('status', 'role', 'vrf', 'dns_name', 'description', 'tags', name=_('IP Address')), + FieldSet('tenant_group', 'tenant', name=_('Tenancy')), + ) + class Meta: model = IPAddress fields = [ - 'address', 'vrf', 'status', 'role', 'dns_name', 'description', 'tenant_group', 'tenant', 'tags', + 'address', 'vrf', 'status', 'role', 'dns_name', 'tenant_group', 'tenant', 'description', 'owner', + 'comments', 'tags', ] diff --git a/netbox/ipam/graphql/filters.py b/netbox/ipam/graphql/filters.py index 6126c3c45..f3c6637fa 100644 --- a/netbox/ipam/graphql/filters.py +++ b/netbox/ipam/graphql/filters.py @@ -7,7 +7,7 @@ import strawberry_django from django.db.models import Q from netaddr.core import AddrFormatError from strawberry.scalars import ID -from strawberry_django import BaseFilterLookup, DateFilterLookup, FilterLookup, StrFilterLookup +from strawberry_django import BaseFilterLookup, ComparisonFilterLookup, DateFilterLookup, FilterLookup, StrFilterLookup from dcim.graphql.filter_mixins import ScopedFilterMixin from dcim.models import Device @@ -57,6 +57,8 @@ __all__ = ( class ASNFilter(TenancyFilterMixin, PrimaryModelFilter): rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() rir_id: ID | None = strawberry_django.filter_field() + role: Annotated['RoleFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + role_id: ID | None = strawberry_django.filter_field() asn: Annotated['BigIntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -397,6 +399,7 @@ class VLANGroupFilter(ScopedFilterMixin, OrganizationalModelFilter): vid_ranges: Annotated['IntegerRangeArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) + total_vlan_ids: ComparisonFilterLookup[int] | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.VLANTranslationPolicy, lookups=True) diff --git a/netbox/ipam/graphql/types.py b/netbox/ipam/graphql/types.py index 98aa91939..2bfdc7d65 100644 --- a/netbox/ipam/graphql/types.py +++ b/netbox/ipam/graphql/types.py @@ -77,6 +77,7 @@ class BaseIPAddressFamilyType: class ASNType(ContactsMixin, PrimaryObjectType): asn: BigInt rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None + role: Annotated["RoleType", strawberry.lazy('ipam.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None sites: list[SiteType] @@ -305,6 +306,7 @@ class VLANGroupType(OrganizationalObjectType): vlans: list[VLANType] vid_ranges: list[str] + total_vlan_ids: BigInt tenant: Annotated['TenantType', strawberry.lazy('tenancy.graphql.types')] | None @strawberry_django.field diff --git a/netbox/ipam/lookups.py b/netbox/ipam/lookups.py index 45f6fd82f..693903f3f 100644 --- a/netbox/ipam/lookups.py +++ b/netbox/ipam/lookups.py @@ -94,9 +94,11 @@ class NetHost(Lookup): rhs, rhs_params = self.process_rhs(qn, connection) # Query parameters are automatically converted to IPNetwork objects, which are then turned to strings. We need # to omit the mask portion of the object's string representation to match PostgreSQL's HOST() function. + # Note: params may be tuples (Django 6.0+) or lists (older Django), so convert before mutating. + rhs_params = list(rhs_params) if rhs_params: rhs_params[0] = rhs_params[0].split('/')[0] - params = lhs_params + rhs_params + params = list(lhs_params) + rhs_params return f'HOST({lhs}) = {rhs}', params diff --git a/netbox/ipam/migrations/0087_add_asn_role.py b/netbox/ipam/migrations/0087_add_asn_role.py new file mode 100644 index 000000000..890a8a40d --- /dev/null +++ b/netbox/ipam/migrations/0087_add_asn_role.py @@ -0,0 +1,21 @@ +# Generated by Django 5.2.11 on 2026-03-04 19:14 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ipam', '0086_gfk_indexes'), + ] + + operations = [ + migrations.AddField( + model_name='asn', + name='role', + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='asns', to='ipam.role' + ), + ), + ] diff --git a/netbox/ipam/migrations/0088_rename_vlangroup_total_vlan_ids.py b/netbox/ipam/migrations/0088_rename_vlangroup_total_vlan_ids.py new file mode 100644 index 000000000..c84624c50 --- /dev/null +++ b/netbox/ipam/migrations/0088_rename_vlangroup_total_vlan_ids.py @@ -0,0 +1,15 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ('ipam', '0087_add_asn_role'), + ] + + operations = [ + migrations.RenameField( + model_name='vlangroup', + old_name='_total_vlan_ids', + new_name='total_vlan_ids', + ), + ] diff --git a/netbox/ipam/migrations/0089_default_ordering_indexes.py b/netbox/ipam/migrations/0089_default_ordering_indexes.py new file mode 100644 index 000000000..a718aa3fc --- /dev/null +++ b/netbox/ipam/migrations/0089_default_ordering_indexes.py @@ -0,0 +1,51 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('dcim', '0232_default_ordering_indexes'), + ('extras', '0137_default_ordering_indexes'), + ('ipam', '0088_rename_vlangroup_total_vlan_ids'), + ('tenancy', '0023_add_mptt_tree_indexes'), + ('users', '0015_owner'), + ] + + operations = [ + migrations.AddIndex( + model_name='aggregate', + index=models.Index(fields=['prefix', 'id'], name='ipam_aggreg_prefix_89dd71_idx'), + ), + migrations.AddIndex( + model_name='fhrpgroup', + index=models.Index(fields=['protocol', 'group_id', 'id'], name='ipam_fhrpgr_protoco_0445ae_idx'), + ), + migrations.AddIndex( + model_name='fhrpgroupassignment', + index=models.Index(fields=['-priority', 'id'], name='ipam_fhrpgr_priorit_b76335_idx'), + ), + migrations.AddIndex( + model_name='ipaddress', + index=models.Index(fields=['address', 'id'], name='ipam_ipaddr_address_3ddeea_idx'), + ), + migrations.AddIndex( + model_name='role', + index=models.Index(fields=['weight', 'name'], name='ipam_role_weight_01396b_idx'), + ), + migrations.AddIndex( + model_name='service', + index=models.Index(fields=['protocol', 'ports', 'id'], name='ipam_servic_protoco_687d13_idx'), + ), + migrations.AddIndex( + model_name='vlangroup', + index=models.Index(fields=['name', 'id'], name='ipam_vlangr_name_ffa83e_idx'), + ), + migrations.AddIndex( + model_name='vlan', + index=models.Index(fields=['site', 'group', 'vid', 'id'], name='ipam_vlan_site_id_985573_idx'), + ), + migrations.AddIndex( + model_name='vrf', + index=models.Index(fields=['name', 'rd', 'id'], name='ipam_vrf_name_ec911d_idx'), + ), + ] diff --git a/netbox/ipam/models/asns.py b/netbox/ipam/models/asns.py index 1b4459921..709314944 100644 --- a/netbox/ipam/models/asns.py +++ b/netbox/ipam/models/asns.py @@ -137,6 +137,15 @@ class ASN(ContactsMixin, PrimaryModel): verbose_name=_('ASN'), help_text=_('16- or 32-bit autonomous system number') ) + role = models.ForeignKey( + to='ipam.Role', + on_delete=models.SET_NULL, + related_name='asns', + blank=True, + null=True, + verbose_name=_('role'), + help_text=_("The primary function of this ASN") + ) tenant = models.ForeignKey( to='tenancy.Tenant', on_delete=models.PROTECT, diff --git a/netbox/ipam/models/fhrp.py b/netbox/ipam/models/fhrp.py index 63a04d4d1..e34ad12bf 100644 --- a/netbox/ipam/models/fhrp.py +++ b/netbox/ipam/models/fhrp.py @@ -59,6 +59,9 @@ class FHRPGroup(PrimaryModel): class Meta: ordering = ['protocol', 'group_id', 'pk'] + indexes = ( + models.Index(fields=('protocol', 'group_id', 'id')), # Default ordering + ) verbose_name = _('FHRP group') verbose_name_plural = _('FHRP groups') @@ -105,6 +108,7 @@ class FHRPGroupAssignment(ChangeLoggedModel): class Meta: ordering = ('-priority', 'pk') indexes = ( + models.Index(fields=('-priority', 'id')), # Default ordering models.Index(fields=('interface_type', 'interface_id')), ) constraints = ( diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index 5d11622f6..a776aa9b3 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -110,6 +110,9 @@ class Aggregate(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel): class Meta: ordering = ('prefix', 'pk') # prefix may be non-unique + indexes = ( + models.Index(fields=('prefix', 'id')), # Default ordering + ) verbose_name = _('aggregate') verbose_name_plural = _('aggregates') @@ -200,6 +203,9 @@ class Role(OrganizationalModel): class Meta: ordering = ('weight', 'name') + indexes = ( + models.Index(fields=('weight', 'name')), # Default ordering + ) verbose_name = _('role') verbose_name_plural = _('roles') @@ -833,6 +839,7 @@ class IPAddress(ContactsMixin, PrimaryModel): class Meta: ordering = ('address', 'pk') # address may be non-unique indexes = ( + models.Index(fields=('address', 'id')), # Default ordering models.Index(Cast(Host('address'), output_field=IPAddressField()), name='ipam_ipaddress_host'), models.Index(fields=('assigned_object_type', 'assigned_object_id')), ) diff --git a/netbox/ipam/models/services.py b/netbox/ipam/models/services.py index 74504c5dc..d934465c3 100644 --- a/netbox/ipam/models/services.py +++ b/netbox/ipam/models/services.py @@ -93,6 +93,7 @@ class Service(ContactsMixin, ServiceBase, PrimaryModel): class Meta: indexes = ( + models.Index(fields=('protocol', 'ports', 'id')), # Default ordering models.Index(fields=('parent_object_type', 'parent_object_id')), ) ordering = ('protocol', 'ports', 'pk') # (protocol, port) may be non-unique diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py index b60b2cb97..554e7487a 100644 --- a/netbox/ipam/models/vlans.py +++ b/netbox/ipam/models/vlans.py @@ -24,9 +24,7 @@ __all__ = ( def default_vid_ranges(): - return [ - NumericRange(VLAN_VID_MIN, VLAN_VID_MAX, bounds='[]') - ] + return [NumericRange(VLAN_VID_MIN, VLAN_VID_MAX + 1)] class VLANGroup(OrganizationalModel): @@ -62,6 +60,9 @@ class VLANGroup(OrganizationalModel): verbose_name=_('VLAN ID ranges'), default=default_vid_ranges ) + total_vlan_ids = models.PositiveBigIntegerField( + default=VLAN_VID_MAX - VLAN_VID_MIN + 1, + ) tenant = models.ForeignKey( to='tenancy.Tenant', on_delete=models.PROTECT, @@ -69,15 +70,13 @@ class VLANGroup(OrganizationalModel): blank=True, null=True ) - _total_vlan_ids = models.PositiveBigIntegerField( - default=VLAN_VID_MAX - VLAN_VID_MIN + 1 - ) objects = VLANGroupQuerySet.as_manager() class Meta: ordering = ('name', 'pk') # Name may be non-unique indexes = ( + models.Index(fields=('name', 'id')), # Default ordering models.Index(fields=('scope_type', 'scope_id')), ) constraints = ( @@ -130,10 +129,10 @@ class VLANGroup(OrganizationalModel): raise ValidationError({'vid_ranges': _("Ranges cannot overlap.")}) def save(self, *args, **kwargs): - self._total_vlan_ids = 0 + self.total_vlan_ids = 0 for vid_range in self.vid_ranges: # VID range is inclusive on lower-bound, exclusive on upper-bound - self._total_vlan_ids += vid_range.upper - vid_range.lower + self.total_vlan_ids += vid_range.upper - vid_range.lower super().save(*args, **kwargs) @@ -269,6 +268,9 @@ class VLAN(PrimaryModel): class Meta: ordering = ('site', 'group', 'vid', 'pk') # (site, group, vid) may be non-unique + indexes = ( + models.Index(fields=('site', 'group', 'vid', 'id')), # Default ordering + ) constraints = ( models.UniqueConstraint( fields=('group', 'vid'), diff --git a/netbox/ipam/models/vrfs.py b/netbox/ipam/models/vrfs.py index 60ad760f9..d91fa02a4 100644 --- a/netbox/ipam/models/vrfs.py +++ b/netbox/ipam/models/vrfs.py @@ -58,6 +58,9 @@ class VRF(PrimaryModel): class Meta: ordering = ('name', 'rd', 'pk') # (name, rd) may be non-unique + indexes = ( + models.Index(fields=('name', 'rd', 'id')), # Default ordering + ) verbose_name = _('VRF') verbose_name_plural = _('VRFs') diff --git a/netbox/ipam/querysets.py b/netbox/ipam/querysets.py index 89013aa31..ae02b4922 100644 --- a/netbox/ipam/querysets.py +++ b/netbox/ipam/querysets.py @@ -64,7 +64,7 @@ class VLANGroupQuerySet(RestrictedQuerySet): return self.annotate( vlan_count=count_related(VLAN, 'group'), - utilization=Round(F('vlan_count') * 100.0 / F('_total_vlan_ids'), 2) + utilization=Round(F('vlan_count') * 100.0 / F('total_vlan_ids'), 2), ) diff --git a/netbox/ipam/search.py b/netbox/ipam/search.py index 17259071f..ea0d41db3 100644 --- a/netbox/ipam/search.py +++ b/netbox/ipam/search.py @@ -23,7 +23,7 @@ class ASNIndex(SearchIndex): ('prefixed_name', 110), ('description', 500), ) - display_attrs = ('rir', 'tenant', 'description') + display_attrs = ('rir', 'role', 'tenant', 'description') @register_search diff --git a/netbox/ipam/tables/asn.py b/netbox/ipam/tables/asn.py index 94e80bfdf..b8f2e4b60 100644 --- a/netbox/ipam/tables/asn.py +++ b/netbox/ipam/tables/asn.py @@ -71,6 +71,10 @@ class ASNTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable): url_params={'asn_id': 'pk'}, verbose_name=_('Provider Count') ) + role = tables.Column( + verbose_name=_('Role'), + linkify=True + ) sites = columns.ManyToManyColumn( linkify_item=True, verbose_name=_('Sites') @@ -82,9 +86,9 @@ class ASNTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable): class Meta(PrimaryModelTable.Meta): model = ASN fields = ( - 'pk', 'asn', 'asn_asdot', 'rir', 'site_count', 'provider_count', 'tenant', 'tenant_group', 'description', - 'contacts', 'comments', 'sites', 'tags', 'created', 'last_updated', 'actions', + 'pk', 'asn', 'asn_asdot', 'rir', 'role', 'site_count', 'provider_count', 'tenant', 'tenant_group', + 'description', 'contacts', 'comments', 'sites', 'tags', 'created', 'last_updated', 'actions', ) default_columns = ( - 'pk', 'asn', 'rir', 'site_count', 'provider_count', 'sites', 'description', 'tenant', + 'pk', 'asn', 'rir', 'role', 'site_count', 'provider_count', 'sites', 'description', 'tenant', ) diff --git a/netbox/ipam/tables/ip.py b/netbox/ipam/tables/ip.py index ee923c3d8..56f2771f0 100644 --- a/netbox/ipam/tables/ip.py +++ b/netbox/ipam/tables/ip.py @@ -120,6 +120,11 @@ class RoleTable(OrganizationalModelTable): url_params={'role_id': 'pk'}, verbose_name=_('VLANs') ) + asn_count = columns.LinkedCountColumn( + viewname='ipam:asn_list', + url_params={'role_id': 'pk'}, + verbose_name=_('ASNs') + ) tags = columns.TagColumn( url_name='ipam:role_list' ) @@ -127,10 +132,10 @@ class RoleTable(OrganizationalModelTable): class Meta(OrganizationalModelTable.Meta): model = Role fields = ( - 'pk', 'id', 'name', 'slug', 'prefix_count', 'iprange_count', 'vlan_count', 'description', 'weight', - 'comments', 'tags', 'created', 'last_updated', 'actions', + 'pk', 'id', 'name', 'slug', 'prefix_count', 'iprange_count', 'vlan_count', 'asn_count', 'description', + 'weight', 'comments', 'tags', 'created', 'last_updated', 'actions', ) - default_columns = ('pk', 'name', 'prefix_count', 'iprange_count', 'vlan_count', 'description') + default_columns = ('pk', 'name', 'prefix_count', 'iprange_count', 'vlan_count', 'asn_count', 'description') # diff --git a/netbox/ipam/tables/vlans.py b/netbox/ipam/tables/vlans.py index 1727faeaa..0297a2e5a 100644 --- a/netbox/ipam/tables/vlans.py +++ b/netbox/ipam/tables/vlans.py @@ -51,6 +51,9 @@ class VLANGroupTable(TenancyColumnsMixin, OrganizationalModelTable): url_params={'group_id': 'pk'}, verbose_name=_('VLANs') ) + total_vlan_ids = tables.Column( + verbose_name=_('Total VLAN IDs'), + ) utilization = columns.UtilizationColumn( orderable=False, verbose_name=_('Utilization') @@ -65,8 +68,9 @@ class VLANGroupTable(TenancyColumnsMixin, OrganizationalModelTable): class Meta(OrganizationalModelTable.Meta): model = VLANGroup fields = ( - 'pk', 'id', 'name', 'scope_type', 'scope', 'vid_ranges_list', 'vlan_count', 'slug', 'description', - 'tenant', 'tenant_group', 'comments', 'tags', 'created', 'last_updated', 'actions', 'utilization', + 'pk', 'id', 'name', 'slug', 'description', 'scope_type', 'scope', 'vid_ranges_list', 'vlan_count', + 'total_vlan_ids', 'tenant', 'tenant_group', 'comments', 'tags', 'created', 'last_updated', 'actions', + 'utilization', ) default_columns = ( 'pk', 'name', 'scope_type', 'scope', 'vlan_count', 'utilization', 'tenant', 'description' diff --git a/netbox/ipam/tests/test_api.py b/netbox/ipam/tests/test_api.py index 3a75347ae..87bfa54fb 100644 --- a/netbox/ipam/tests/test_api.py +++ b/netbox/ipam/tests/test_api.py @@ -151,6 +151,12 @@ class ASNTest(APIViewTestCases.APIViewTestCase): ) RIR.objects.bulk_create(rirs) + roles = ( + Role(name='Role 1', slug='role-1'), + Role(name='Role 2', slug='role-2'), + ) + Role.objects.bulk_create(roles) + sites = ( Site(name='Site 1', slug='site-1'), Site(name='Site 2', slug='site-2') @@ -164,10 +170,10 @@ class ASNTest(APIViewTestCases.APIViewTestCase): Tenant.objects.bulk_create(tenants) asns = ( - ASN(asn=65000, rir=rirs[0], tenant=tenants[0]), - ASN(asn=65001, rir=rirs[0], tenant=tenants[1]), - ASN(asn=4200000000, rir=rirs[1], tenant=tenants[0]), - ASN(asn=4200000001, rir=rirs[1], tenant=tenants[1]), + ASN(asn=65000, rir=rirs[0], role=roles[0], tenant=tenants[0]), + ASN(asn=65001, rir=rirs[0], role=roles[0], tenant=tenants[1]), + ASN(asn=4200000000, rir=rirs[1], role=roles[1], tenant=tenants[0]), + ASN(asn=4200000001, rir=rirs[1], role=roles[1], tenant=tenants[1]), ) ASN.objects.bulk_create(asns) @@ -180,10 +186,12 @@ class ASNTest(APIViewTestCases.APIViewTestCase): { 'asn': 64512, 'rir': rirs[0].pk, + 'role': roles[0].pk, }, { 'asn': 65002, 'rir': rirs[0].pk, + 'role': roles[1].pk, }, { 'asn': 4200000002, @@ -375,7 +383,7 @@ class AggregateTest(APIViewTestCases.APIViewTestCase): class RoleTest(APIViewTestCases.APIViewTestCase): model = Role - brief_fields = ['description', 'display', 'id', 'name', 'prefix_count', 'slug', 'url', 'vlan_count'] + brief_fields = ['asn_count', 'description', 'display', 'id', 'name', 'prefix_count', 'slug', 'url', 'vlan_count'] create_data = [ { 'name': 'Role 4', @@ -404,6 +412,17 @@ class RoleTest(APIViewTestCases.APIViewTestCase): ) Role.objects.bulk_create(roles) + rirs = ( + RIR(name='RIR 1', slug='rir-1', is_private=True), + ) + RIR.objects.bulk_create(rirs) + + asns = ( + ASN(asn=65000, rir=rirs[0], role=roles[0]), + ASN(asn=65001, rir=rirs[0], role=roles[0]), + ) + ASN.objects.bulk_create(asns) + class PrefixTest(APIViewTestCases.APIViewTestCase): model = Prefix diff --git a/netbox/ipam/tests/test_filtersets.py b/netbox/ipam/tests/test_filtersets.py index c77b05dcf..856f8d4b5 100644 --- a/netbox/ipam/tests/test_filtersets.py +++ b/netbox/ipam/tests/test_filtersets.py @@ -114,6 +114,13 @@ class ASNTestCase(TestCase, ChangeLoggedFilterSetTests): ] RIR.objects.bulk_create(rirs) + roles = [ + Role(name='Role 1', slug='role-1'), + Role(name='Role 2', slug='role-2'), + Role(name='Role 3', slug='role-3'), + ] + Role.objects.bulk_create(roles) + tenants = [ Tenant(name='Tenant 1', slug='tenant-1'), Tenant(name='Tenant 2', slug='tenant-2'), @@ -124,12 +131,12 @@ class ASNTestCase(TestCase, ChangeLoggedFilterSetTests): Tenant.objects.bulk_create(tenants) asns = ( - ASN(asn=65001, rir=rirs[0], tenant=tenants[0], description='foobar1'), - ASN(asn=65002, rir=rirs[1], tenant=tenants[1], description='foobar2'), - ASN(asn=65003, rir=rirs[2], tenant=tenants[2], description='foobar3'), - ASN(asn=4200000000, rir=rirs[0], tenant=tenants[0]), - ASN(asn=4200000001, rir=rirs[1], tenant=tenants[1]), - ASN(asn=4200000002, rir=rirs[2], tenant=tenants[2]), + ASN(asn=65001, rir=rirs[0], role=roles[0], tenant=tenants[0], description='foobar1'), + ASN(asn=65002, rir=rirs[1], role=roles[1], tenant=tenants[1], description='foobar2'), + ASN(asn=65003, rir=rirs[2], role=roles[2], tenant=tenants[2], description='foobar3'), + ASN(asn=4200000000, rir=rirs[0], role=roles[0], tenant=tenants[0]), + ASN(asn=4200000001, rir=rirs[1], role=roles[1], tenant=tenants[1]), + ASN(asn=4200000002, rir=rirs[2], role=roles[2], tenant=tenants[2]), ) ASN.objects.bulk_create(asns) @@ -186,6 +193,13 @@ class ASNTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'rir': [rirs[0].slug, rirs[1].slug]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + def test_role(self): + roles = Role.objects.all()[:2] + params = {'role_id': [roles[0].pk, roles[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + params = {'role': [roles[0].slug, roles[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + def test_site_group(self): site_groups = SiteGroup.objects.all()[:2] params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]} @@ -1700,7 +1714,9 @@ class VLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests): slug='vlan-group-8' ), ) - VLANGroup.objects.bulk_create(vlan_groups) + # Ensure the total_vlan_ids field is populated + for vlan_group in vlan_groups: + vlan_group.save() def test_q(self): params = {'q': 'foobar1'} @@ -1728,6 +1744,12 @@ class VLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'contains_vid': 4095} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) + def test_total_vlan_ids(self): + params = {'total_vlan_ids': [110]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 7) + params = {'total_vlan_ids': [4094]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_region(self): params = {'region': Region.objects.first().pk} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) diff --git a/netbox/ipam/tests/test_models.py b/netbox/ipam/tests/test_models.py index e81900ec9..d7a1a1267 100644 --- a/netbox/ipam/tests/test_models.py +++ b/netbox/ipam/tests/test_models.py @@ -698,7 +698,7 @@ class TestVLANGroup(TestCase): def test_total_vlan_ids(self): vlangroup = VLANGroup.objects.first() - self.assertEqual(vlangroup._total_vlan_ids, 100) + self.assertEqual(vlangroup.total_vlan_ids, 100) class TestVLAN(TestCase): diff --git a/netbox/ipam/tests/test_views.py b/netbox/ipam/tests/test_views.py index 289625493..e77d88b68 100644 --- a/netbox/ipam/tests/test_views.py +++ b/netbox/ipam/tests/test_views.py @@ -5,7 +5,8 @@ from django.test import override_settings from django.urls import reverse from netaddr import IPNetwork -from core.models import ObjectType +from core.choices import ObjectChangeActionChoices +from core.models import ObjectChange, ObjectType from dcim.constants import InterfaceTypeChoices from dcim.models import Device, DeviceRole, DeviceType, Interface, Manufacturer, Site from ipam.choices import * @@ -84,6 +85,12 @@ class ASNTestCase(ViewTestCases.PrimaryObjectViewTestCase): ] RIR.objects.bulk_create(rirs) + roles = ( + Role(name='Role 1', slug='role-1'), + Role(name='Role 2', slug='role-2'), + ) + Role.objects.bulk_create(roles) + sites = ( Site(name='Site 1', slug='site-1'), Site(name='Site 2', slug='site-2') @@ -97,10 +104,10 @@ class ASNTestCase(ViewTestCases.PrimaryObjectViewTestCase): Tenant.objects.bulk_create(tenants) asns = ( - ASN(asn=65001, rir=rirs[0], tenant=tenants[0]), - ASN(asn=65002, rir=rirs[1], tenant=tenants[1]), - ASN(asn=4200000001, rir=rirs[0], tenant=tenants[0]), - ASN(asn=4200000002, rir=rirs[1], tenant=tenants[1]), + ASN(asn=65001, rir=rirs[0], role=roles[0], tenant=tenants[0]), + ASN(asn=65002, rir=rirs[1], role=roles[1], tenant=tenants[1]), + ASN(asn=4200000001, rir=rirs[0], role=roles[0], tenant=tenants[0]), + ASN(asn=4200000002, rir=rirs[1], role=roles[1], tenant=tenants[1]), ) ASN.objects.bulk_create(asns) @@ -114,6 +121,7 @@ class ASNTestCase(ViewTestCases.PrimaryObjectViewTestCase): cls.form_data = { 'asn': 65000, 'rir': rirs[0].pk, + 'role': roles[0].pk, 'tenant': tenants[0].pk, 'site': sites[0].pk, 'description': 'A new ASN', @@ -121,11 +129,11 @@ class ASNTestCase(ViewTestCases.PrimaryObjectViewTestCase): } cls.csv_data = ( - "asn,rir", - "65003,RIR 1", - "65004,RIR 2", - "4200000003,RIR 1", - "4200000004,RIR 2", + "asn,rir,role", + f"65003,RIR 1,{roles[0].name}", + f"65004,RIR 2,{roles[1].name}", + f"4200000003,RIR 1,{roles[0].name}", + f"4200000004,RIR 2,{roles[1].name}", ) cls.csv_update_data = ( @@ -137,6 +145,7 @@ class ASNTestCase(ViewTestCases.PrimaryObjectViewTestCase): cls.bulk_edit_data = { 'rir': rirs[1].pk, + 'role': roles[1].pk, 'description': 'Next description', } @@ -467,6 +476,105 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'description': 'New description', } + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_bulk_add_ipv4_prefixes(self): + """Test bulk creating IPv4 prefixes using a pattern.""" + obj_perm = ObjectPermission(name='Test permission', actions=['add']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix)) + + initial_count = Prefix.objects.count() + url = reverse('ipam:prefix_bulk_add') + data = { + 'pattern': '10.0.[0-2].0/24', + 'status': PrefixStatusChoices.STATUS_ACTIVE, + } + response = self.client.post(url, data) + self.assertHttpStatus(response, 302) + self.assertEqual(Prefix.objects.count(), initial_count + 3) + + for i in range(3): + self.assertTrue(Prefix.objects.filter(prefix=IPNetwork(f'10.0.{i}.0/24')).exists()) + + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_bulk_add_ipv6_prefixes(self): + """Test bulk creating IPv6 prefixes using a pattern.""" + obj_perm = ObjectPermission(name='Test permission', actions=['add']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix)) + + initial_count = Prefix.objects.count() + url = reverse('ipam:prefix_bulk_add') + data = { + 'pattern': 'fd00:db8:[0-3]::/48', + 'status': PrefixStatusChoices.STATUS_ACTIVE, + } + response = self.client.post(url, data) + self.assertHttpStatus(response, 302) + self.assertEqual(Prefix.objects.count(), initial_count + 4) + + for i in range(4): + self.assertTrue(Prefix.objects.filter(prefix=IPNetwork(f'fd00:db8:{i}::/48')).exists()) + + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_bulk_add_ipv6_prefixes_uppercase_hex(self): + """Test bulk creating IPv6 prefixes using uppercase hex in the pattern.""" + obj_perm = ObjectPermission(name='Test permission', actions=['add']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix)) + + initial_count = Prefix.objects.count() + url = reverse('ipam:prefix_bulk_add') + data = { + 'pattern': 'fd00:0:0:[48-4F]00::/56', + 'status': PrefixStatusChoices.STATUS_ACTIVE, + } + response = self.client.post(url, data) + self.assertHttpStatus(response, 302) + self.assertEqual(Prefix.objects.count(), initial_count + 8) + + expected_hex = ['48', '49', '4a', '4b', '4c', '4d', '4e', '4f'] + for h in expected_hex: + prefix_str = f'fd00:0:0:{h}00::/56' + self.assertTrue( + Prefix.objects.filter(prefix=IPNetwork(prefix_str)).exists(), + f'Expected prefix {prefix_str} was not created' + ) + + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_bulk_add_prefixes_with_changelog_message(self): + obj_perm = ObjectPermission(name='Test permission', actions=['add']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix)) + + changelog_message = 'Bulk-created prefixes' + prefixes = [IPNetwork(f'198.18.{i}.0/24') for i in range(3)] + url = reverse('ipam:prefix_bulk_add') + data = { + 'pattern': '198.18.[0-2].0/24', + 'status': PrefixStatusChoices.STATUS_ACTIVE, + 'changelog_message': changelog_message, + } + + response = self.client.post(url, data) + self.assertHttpStatus(response, 302) + + created_prefixes = list(Prefix.objects.filter(prefix__in=prefixes)) + self.assertEqual(len(created_prefixes), len(prefixes)) + + objectchanges = ObjectChange.objects.filter( + action=ObjectChangeActionChoices.ACTION_CREATE, + changed_object_type=ContentType.objects.get_for_model(Prefix), + changed_object_id__in=[obj.pk for obj in created_prefixes], + ) + self.assertEqual(objectchanges.count(), len(prefixes)) + for objectchange in objectchanges: + self.assertEqual(objectchange.message, changelog_message) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) def test_prefix_prefixes(self): prefixes = ( @@ -832,6 +940,39 @@ class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'description': 'New description', } + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + def test_bulk_add_ipaddresses_with_changelog_message(self): + obj_perm = ObjectPermission(name='Test permission', actions=['add']) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(IPAddress)) + + vrf = VRF.objects.get(name='VRF 1') + changelog_message = 'Bulk-created IP addresses' + addresses = [IPNetwork(f'198.51.100.{i}/24') for i in range(10, 13)] + url = reverse('ipam:ipaddress_bulk_add') + data = { + 'pattern': '198.51.100.[10-12]/24', + 'vrf': vrf.pk, + 'status': IPAddressStatusChoices.STATUS_ACTIVE, + 'changelog_message': changelog_message, + } + + response = self.client.post(url, data) + self.assertHttpStatus(response, 302) + + created_addresses = list(IPAddress.objects.filter(address__in=addresses, vrf=vrf)) + self.assertEqual(len(created_addresses), len(addresses)) + + objectchanges = ObjectChange.objects.filter( + action=ObjectChangeActionChoices.ACTION_CREATE, + changed_object_type=ContentType.objects.get_for_model(IPAddress), + changed_object_id__in=[obj.pk for obj in created_addresses], + ) + self.assertEqual(objectchanges.count(), len(addresses)) + for objectchange in objectchanges: + self.assertEqual(objectchange.message, changelog_message) + class FHRPGroupTestCase(ViewTestCases.PrimaryObjectViewTestCase): model = FHRPGroup diff --git a/netbox/ipam/ui/attrs.py b/netbox/ipam/ui/attrs.py index cd5cba0f2..aab23648b 100644 --- a/netbox/ipam/ui/attrs.py +++ b/netbox/ipam/ui/attrs.py @@ -7,6 +7,9 @@ class VRFDisplayAttr(attrs.ObjectAttribute): """ Renders a VRF reference, displaying 'Global' when no VRF is assigned. Optionally includes the route distinguisher (RD). + + Parameters: + show_rd (bool): If true, the VRF's RD will be included. (Default: False) """ template_name = 'ipam/attrs/vrf.html' @@ -14,11 +17,17 @@ class VRFDisplayAttr(attrs.ObjectAttribute): super().__init__(*args, **kwargs) self.show_rd = show_rd - def render(self, obj, context): - value = self.get_value(obj) - return render_to_string(self.template_name, { - **self.get_context(obj, context), - 'name': context['name'], - 'value': value, + def get_context(self, obj, attr, value, context): + return { 'show_rd': self.show_rd, + } + + def render(self, obj, context): + name = context['name'] + value = self.get_value(obj) + + return render_to_string(self.template_name, { + **self.get_context(obj, name, value, context), + 'name': name, + 'value': value, }) diff --git a/netbox/ipam/ui/panels.py b/netbox/ipam/ui/panels.py index c47791cc5..c8984352a 100644 --- a/netbox/ipam/ui/panels.py +++ b/netbox/ipam/ui/panels.py @@ -66,6 +66,7 @@ class ASNRangePanel(panels.ObjectAttributesPanel): class ASNPanel(panels.ObjectAttributesPanel): asn = attrs.TextAttr('asn_with_asdot', label=_('AS Number')) rir = attrs.RelatedObjectAttr('rir', linkify=True, label=_('RIR')) + role = attrs.RelatedObjectAttr('role', linkify=True) tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group') description = attrs.TextAttr('description') @@ -228,11 +229,9 @@ class VLANCustomerVLANsPanel(panels.ObjectsTablePanel): ], ) - def render(self, context): + def should_render(self, context): obj = context.get('object') - if not obj or obj.qinq_role != 'svlan': - return '' - return super().render(context) + return False if (obj is None or obj.qinq_role != 'svlan') else True class ServiceTemplatePanel(panels.ObjectAttributesPanel): diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 898db4b7e..41f5a1832 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -51,6 +51,7 @@ class VRFListView(generic.ObjectListView): @register_model_view(VRF) class VRFView(GetRelatedModelsMixin, generic.ObjectView): queryset = VRF.objects.all() + template_name = 'generic/object.html' layout = layout.Layout( layout.Row( layout.Column( @@ -172,6 +173,7 @@ class RouteTargetListView(generic.ObjectListView): @register_model_view(RouteTarget) class RouteTargetView(generic.ObjectView): queryset = RouteTarget.objects.all() + template_name = 'generic/object.html' layout = layout.Layout( layout.Row( layout.Column( @@ -627,7 +629,8 @@ class RoleListView(generic.ObjectListView): queryset = Role.objects.annotate( prefix_count=count_related(Prefix, 'role'), iprange_count=count_related(IPRange, 'role'), - vlan_count=count_related(VLAN, 'role') + vlan_count=count_related(VLAN, 'role'), + asn_count=count_related(ASN, 'role') ) filterset = filtersets.RoleFilterSet filterset_form = forms.RoleFilterForm @@ -864,6 +867,7 @@ class PrefixIPAddressesView(generic.ObjectChildrenView): class PrefixEditView(generic.ObjectEditView): queryset = Prefix.objects.all() form = forms.PrefixForm + template_name = 'ipam/prefix_edit.html' @register_model_view(Prefix, 'delete') @@ -871,6 +875,15 @@ class PrefixDeleteView(generic.ObjectDeleteView): queryset = Prefix.objects.all() +@register_model_view(Prefix, 'bulk_add', path='bulk-add', detail=False) +class PrefixBulkCreateView(generic.BulkCreateView): + queryset = Prefix.objects.all() + form = forms.IPNetworkBulkCreateForm + model_form = forms.PrefixBulkAddForm + pattern_target = 'prefix' + template_name = 'ipam/prefix_bulk_add.html' + + @register_model_view(Prefix, 'bulk_import', path='import', detail=False) class PrefixBulkImportView(generic.BulkImportView): queryset = Prefix.objects.all() @@ -1161,7 +1174,7 @@ class IPAddressDeleteView(generic.ObjectDeleteView): @register_model_view(IPAddress, 'bulk_add', path='bulk-add', detail=False) class IPAddressBulkCreateView(generic.BulkCreateView): queryset = IPAddress.objects.all() - form = forms.IPAddressBulkCreateForm + form = forms.IPNetworkBulkCreateForm model_form = forms.IPAddressBulkAddForm pattern_target = 'address' template_name = 'ipam/ipaddress_bulk_add.html' @@ -1321,6 +1334,7 @@ class VLANTranslationPolicyListView(generic.ObjectListView): @register_model_view(VLANTranslationPolicy) class VLANTranslationPolicyView(generic.ObjectView): queryset = VLANTranslationPolicy.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.VLANTranslationPolicyPanel(), @@ -1335,6 +1349,7 @@ class VLANTranslationPolicyView(generic.ObjectView): 'ipam.vlantranslationrule', filters={'policy_id': lambda ctx: ctx['object'].pk}, title=_('VLAN translation rules'), + exclude_columns=['policy'], actions=[ actions.AddObject( 'ipam.vlantranslationrule', @@ -1402,6 +1417,7 @@ class VLANTranslationRuleListView(generic.ObjectListView): @register_model_view(VLANTranslationRule) class VLANTranslationRuleView(generic.ObjectView): queryset = VLANTranslationRule.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.VLANTranslationRulePanel(), @@ -1632,6 +1648,7 @@ class VLANView(generic.ObjectView): 'ipam.prefix', filters={'vlan_id': lambda ctx: ctx['object'].pk}, title=_('Prefixes'), + exclude_columns=['vlan'], actions=[ actions.AddObject( 'ipam.prefix', @@ -1743,6 +1760,7 @@ class ServiceTemplateListView(generic.ObjectListView): @register_model_view(ServiceTemplate) class ServiceTemplateView(generic.ObjectView): queryset = ServiceTemplate.objects.all() + template_name = 'generic/object.html' layout = layout.SimpleLayout( left_panels=[ panels.ServiceTemplatePanel(), diff --git a/netbox/netbox/api/pagination.py b/netbox/netbox/api/pagination.py index 15fb49da8..bbdc83256 100644 --- a/netbox/netbox/api/pagination.py +++ b/netbox/netbox/api/pagination.py @@ -1,18 +1,42 @@ +import warnings + from django.db.models import QuerySet +from django.utils.translation import gettext_lazy as _ +from rest_framework.exceptions import ValidationError from rest_framework.pagination import LimitOffsetPagination +from rest_framework.utils.urls import remove_query_param, replace_query_param from netbox.api.exceptions import QuerySetNotOrdered from netbox.config import get_config -class OptionalLimitOffsetPagination(LimitOffsetPagination): +class NetBoxPagination(LimitOffsetPagination): """ - Override the stock paginator to allow setting limit=0 to disable pagination for a request. This returns all objects - matching a query, but retains the same format as a paginated request. The limit can only be disabled if - MAX_PAGE_SIZE has been set to 0 or None. + Provides two mutually exclusive pagination mechanisms: offset-based and cursor-based. + + Offset-based pagination employs `offset` and (optionally) `limit` parameters to page through results following the + model's natural order. `offset` indicates the number of results to skip. This provides very human-friendly behavior, + but performance can suffer when querying very large data sets due the overhead required to determine the starting + point in the database. + + Cursor-based pagination employs `start` and (optionally) `limit` parameters to page through results as ordered by + the model's primary key (i.e. `id`). `start` indicates the numeric ID of the first object to return; `limit` + indicates the maximum number of objects to return beginning with the specified ID. Objects *must* be ordered by ID + to ensure pagination is consistent. This approach is less human-friendly but offers superior performance to + offset-based pagination. In cursor mode, `count` is omitted (null) for performance. + + Offset- and cursor-based pagination are mutually exclusive: Only `offset` _or_ `start` is permitted for a request. + + `limit` may be set to zero (`?limit=0`). This returns all objects matching a query, but retains the same format as + a paginated request. The limit can only be disabled if `MAX_PAGE_SIZE` has been set to 0 or None. """ + start_query_param = 'start' + def __init__(self): self.default_limit = get_config().PAGINATE_COUNT + self.start = None + self._page_length = 0 + self._last_pk = None def paginate_queryset(self, queryset, request, view=None): @@ -22,15 +46,42 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination): "ordering has been applied to the queryset for this API endpoint." ) + self.start = self.get_start(request) + self.limit = self.get_limit(request) + self.request = request + + # Cursor-based pagination + if self.start is not None: + if self.offset_query_param in request.query_params: + raise ValidationError( + _("'{start_param}' and '{offset_param}' are mutually exclusive.").format( + start_param=self.start_query_param, + offset_param=self.offset_query_param, + ) + ) + if 'ordering' in request.query_params: + raise ValidationError(_("Ordering cannot be specified in conjunction with cursor-based pagination.")) + + self.count = None + self.offset = 0 + + queryset = queryset.filter(pk__gte=self.start).order_by('pk') + results = list(queryset[:self.limit]) if self.limit else list(queryset) + + self._page_length = len(results) + if results: + self._last_pk = results[-1].pk if hasattr(results[-1], 'pk') else results[-1]['pk'] + + return results + + # Offset-based pagination if isinstance(queryset, QuerySet): self.count = self.get_queryset_count(queryset) else: # We're dealing with an iterable, not a QuerySet self.count = len(queryset) - self.limit = self.get_limit(request) self.offset = self.get_offset(request) - self.request = request if self.limit and self.count > self.limit and self.template is not None: self.display_page_controls = True @@ -42,6 +93,25 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination): return list(queryset[self.offset:self.offset + self.limit]) return list(queryset[self.offset:]) + def get_start(self, request): + try: + value = int(request.query_params[self.start_query_param]) + if value < 0: + raise ValidationError( + _("Invalid '{param}' parameter: must be a non-negative integer.").format( + param=self.start_query_param, + ) + ) + return value + except KeyError: + return None + except (ValueError, TypeError): + raise ValidationError( + _("Invalid '{param}' parameter: must be a non-negative integer.").format( + param=self.start_query_param, + ) + ) + def get_limit(self, request): max_limit = self.default_limit MAX_PAGE_SIZE = get_config().MAX_PAGE_SIZE @@ -75,6 +145,16 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination): if not self.limit: return None + # Cursor mode + if self.start is not None: + if self._page_length < self.limit: + return None + url = self.request.build_absolute_uri() + url = replace_query_param(url, self.start_query_param, self._last_pk + 1) + url = replace_query_param(url, self.limit_query_param, self.limit) + url = remove_query_param(url, self.offset_query_param) + return url + return super().get_next_link() def get_previous_link(self): @@ -83,10 +163,30 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination): if not self.limit: return None + # Cursor mode: forward-only + if self.start is not None: + return None + return super().get_previous_link() + def get_schema_operation_parameters(self, view): + parameters = super().get_schema_operation_parameters(view) + parameters.append({ + 'name': self.start_query_param, + 'required': False, + 'in': 'query', + 'description': ( + 'Cursor-based pagination: return results with pk >= start, ordered by pk. ' + 'Mutually exclusive with offset.' + ), + 'schema': { + 'type': 'integer', + }, + }) + return parameters -class StripCountAnnotationsPaginator(OptionalLimitOffsetPagination): + +class StripCountAnnotationsPaginator(NetBoxPagination): """ Strips the annotations on the queryset before getting the count to optimize pagination of complex queries. @@ -122,3 +222,15 @@ class LimitOffsetListPagination(LimitOffsetPagination): self.display_page_controls = True return data[self.offset:self.offset + self.limit] + + +# TODO: Remove in NetBox v4.7.0 +def __getattr__(name): + if name == 'OptionalLimitOffsetPagination': + warnings.warn( + "OptionalLimitOffsetPagination has been renamed to NetBoxPagination. " + "OptionalLimitOffsetPagination will be removed in NetBox v4.7.0.", + DeprecationWarning, + ) + return NetBoxPagination + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/netbox/netbox/api/serializers/features.py b/netbox/netbox/api/serializers/features.py index 34dce1e16..550840ad0 100644 --- a/netbox/netbox/api/serializers/features.py +++ b/netbox/netbox/api/serializers/features.py @@ -53,17 +53,78 @@ class TaggableModelSerializer(serializers.Serializer): on create() and update(). """ tags = NestedTagSerializer(many=True, required=False) + add_tags = NestedTagSerializer(many=True, required=False, write_only=True) + remove_tags = NestedTagSerializer(many=True, required=False, write_only=True) + + def to_internal_value(self, data): + ret = super().to_internal_value(data) + + # Workaround to bypass requirement to include add_tags/remove_tags in Meta.fields on every serializer + if type(data) is dict: + tag_serializer = NestedTagSerializer(many=True) + for field_name in ('add_tags', 'remove_tags'): + if field_name in data: + ret[field_name] = tag_serializer.to_internal_value(data[field_name]) + + return ret + + def validate(self, data): + # Skip validation for nested serializer representations (e.g. when used as a related field) + if type(data) is not dict: + return super().validate(data) + + if data.get('tags') and (data.get('add_tags') or data.get('remove_tags')): + raise serializers.ValidationError({ + 'tags': 'Cannot specify "tags" together with "add_tags" or "remove_tags".' + }) + + if self.instance is None and data.get('remove_tags'): + raise serializers.ValidationError({ + 'remove_tags': 'Cannot use "remove_tags" when creating a new object.' + }) + + if data.get('add_tags') and data.get('remove_tags'): + add_pks = {t.pk for t in data['add_tags']} + remove_pks = {t.pk for t in data['remove_tags']} + overlap = [t for t in data['add_tags'] if t.pk in (add_pks & remove_pks)] + if overlap: + raise serializers.ValidationError({ + 'remove_tags': + f'Tags may not be present in both "add_tags" and "remove_tags": ' + f'{", ".join(t.name for t in overlap)}' + }) + + # Pop add_tags/remove_tags before calling super() to prevent them from being passed + # to the model constructor during ValidatedModelSerializer validation + add_tags = data.pop('add_tags', None) + remove_tags = data.pop('remove_tags', None) + + data = super().validate(data) + + # Restore for use in create()/update() + if add_tags is not None: + data['add_tags'] = add_tags + if remove_tags is not None: + data['remove_tags'] = remove_tags + + return data def create(self, validated_data): tags = validated_data.pop('tags', None) + add_tags = validated_data.pop('add_tags', None) + validated_data.pop('remove_tags', None) instance = super().create(validated_data) if tags is not None: return self._save_tags(instance, tags) + if add_tags is not None: + instance.tags.add(*[t.name for t in add_tags]) return instance def update(self, instance, validated_data): tags = validated_data.pop('tags', None) + add_tags = validated_data.pop('add_tags', None) + remove_tags = validated_data.pop('remove_tags', None) # Cache tags on instance for change logging instance._tags = tags or [] @@ -72,6 +133,13 @@ class TaggableModelSerializer(serializers.Serializer): if tags is not None: return self._save_tags(instance, tags) + if add_tags is not None: + instance.tags.add(*[t.name for t in add_tags]) + if remove_tags is not None: + instance.tags.remove(*[t.name for t in remove_tags]) + if add_tags is not None or remove_tags is not None: + instance._tags = instance.tags.all() + return instance def _save_tags(self, instance, tags): diff --git a/netbox/netbox/api/viewsets/__init__.py b/netbox/netbox/api/viewsets/__init__.py index bf19bf35e..95b2351d3 100644 --- a/netbox/netbox/api/viewsets/__init__.py +++ b/netbox/netbox/api/viewsets/__init__.py @@ -13,7 +13,7 @@ from rest_framework.viewsets import GenericViewSet from netbox.api.serializers.features import ChangeLogMessageSerializer from netbox.constants import ADVISORY_LOCK_KEYS from utilities.api import get_annotations_for_serializer, get_prefetches_for_serializer -from utilities.exceptions import AbortRequest +from utilities.exceptions import AbortRequest, PreconditionFailed from utilities.query import reapply_model_ordering from . import mixins @@ -34,6 +34,50 @@ HTTP_ACTIONS = { } +class ETagMixin: + """ + Adds ETag header support to ViewSets. Generates weak ETags (W/ prefix per + RFC 7232 §2.1) from `last_updated` (or `created` if unavailable). Weak ETags + are appropriate here because the tag is derived from a modification timestamp + rather than a hash of the serialized payload. + """ + + @staticmethod + def _get_etag(obj): + """Return a weak ETag string for the given object, or None.""" + if ts := getattr(obj, 'last_updated', None) or getattr(obj, 'created', None): + return f'W/"{ts.isoformat()}"' + return None + + @staticmethod + def _get_if_match(request): + """Return the list of If-Match header values (if specified).""" + if (if_match := request.META.get('HTTP_IF_MATCH')) and if_match != '*': + return [e.strip() for e in if_match.split(',')] + return [] + + def _validate_etag(self, request, instance): + """Validate the request's ETag""" + if provided := self._get_if_match(request): + current_etag = self._get_etag(instance) + if current_etag and current_etag not in provided: + raise PreconditionFailed(etag=current_etag) + + def handle_exception(self, exc): + response = super().handle_exception(exc) + if isinstance(exc, PreconditionFailed) and exc.etag: + response['ETag'] = exc.etag + return response + + def retrieve(self, request, *args, **kwargs): + instance = self.get_object() + serializer = self.get_serializer(instance) + response = Response(serializer.data) + if etag := self._get_etag(instance): + response['ETag'] = etag + return response + + class BaseViewSet(GenericViewSet): """ Base class for all API ViewSets. This is responsible for the enforcement of object-based permissions. @@ -95,6 +139,7 @@ class BaseViewSet(GenericViewSet): class NetBoxReadOnlyModelViewSet( + ETagMixin, mixins.CustomFieldsMixin, mixins.ExportTemplatesMixin, drf_mixins.RetrieveModelMixin, @@ -105,6 +150,7 @@ class NetBoxReadOnlyModelViewSet( class NetBoxModelViewSet( + ETagMixin, mixins.BulkUpdateModelMixin, mixins.BulkDestroyModelMixin, mixins.ObjectValidationMixin, @@ -191,7 +237,14 @@ class NetBoxModelViewSet( serializer = self.get_serializer(qs, many=bulk_create) headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + response = Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + + # Add ETag for single-object creation only (bulk returns a list, no single ETag) + if not bulk_create: + if etag := self._get_etag(qs): + response['ETag'] = etag + + return response def perform_create(self, serializer): model = self.queryset.model @@ -211,6 +264,10 @@ class NetBoxModelViewSet( def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object_with_snapshot() + + # Enforce If-Match precondition (RFC 9110 §13.1.1) + self._validate_etag(self.request, instance) + serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) @@ -221,8 +278,12 @@ class NetBoxModelViewSet( # Re-serialize the instance(s) with prefetched data serializer = self.get_serializer(qs) + response = Response(serializer.data) - return Response(serializer.data) + if etag := self._get_etag(qs): + response['ETag'] = etag + + return response def perform_update(self, serializer): model = self.queryset.model @@ -232,6 +293,11 @@ class NetBoxModelViewSet( # Enforce object-level permissions on save() try: with transaction.atomic(using=router.db_for_write(model)): + # Re-check the If-Match ETag under a row-level lock to close the TOCTOU window + # between the initial check in update() and the actual write. + if self._get_if_match(self.request): + locked = model.objects.select_for_update().get(pk=serializer.instance.pk) + self._validate_etag(self.request, locked) instance = serializer.save() self._validate_objects(instance) except ObjectDoesNotExist: @@ -242,6 +308,9 @@ class NetBoxModelViewSet( def destroy(self, request, *args, **kwargs): instance = self.get_object_with_snapshot() + # Enforce If-Match precondition (RFC 9110 §13.1.1) + self._validate_etag(request, instance) + # Attach changelog message (if any) serializer = ChangeLogMessageSerializer(data=request.data) serializer.is_valid(raise_exception=True) @@ -256,7 +325,16 @@ class NetBoxModelViewSet( logger = logging.getLogger(f'netbox.api.views.{self.__class__.__name__}') logger.info(f"Deleting {model._meta.verbose_name} {instance} (PK: {instance.pk})") - return super().perform_destroy(instance) + try: + with transaction.atomic(using=router.db_for_write(model)): + # Re-check the If-Match ETag under a row-level lock to close the TOCTOU window + # between the initial check in destroy() and the actual delete. + if self._get_if_match(self.request): + locked = model.objects.select_for_update().get(pk=instance.pk) + self._validate_etag(self.request, locked) + super().perform_destroy(instance) + except ObjectDoesNotExist: + raise PermissionDenied() class MPTTLockedMixin: diff --git a/netbox/netbox/config/__init__.py b/netbox/netbox/config/__init__.py index 3c084949a..b6ca4f90c 100644 --- a/netbox/netbox/config/__init__.py +++ b/netbox/netbox/config/__init__.py @@ -10,6 +10,7 @@ from .parameters import PARAMS __all__ = ( 'PARAMS', + 'Config', 'ConfigItem', 'clear_config', 'get_config', diff --git a/netbox/netbox/config/parameters.py b/netbox/netbox/config/parameters.py index cc87b58bb..0c9975941 100644 --- a/netbox/netbox/config/parameters.py +++ b/netbox/netbox/config/parameters.py @@ -175,6 +175,25 @@ PARAMS = ( field=forms.JSONField ), + # Change log + ConfigParam( + name='CHANGELOG_RETENTION', + label=_('Changelog retention'), + default=90, + description=_("Days to retain changelog history (set to zero for unlimited)"), + field=forms.IntegerField, + ), + ConfigParam( + name='CHANGELOG_RETAIN_CREATE_LAST_UPDATE', + label=_('Retain create & last update changelog records'), + default=False, + description=_( + "Retain each object's create record and most recent update record when pruning expired changelog entries " + "(excluding objects with a delete record)." + ), + field=forms.BooleanField, + ), + # Miscellaneous ConfigParam( name='MAINTENANCE_MODE', @@ -199,13 +218,6 @@ PARAMS = ( description=_("Enable the GraphQL API"), field=forms.BooleanField ), - ConfigParam( - name='CHANGELOG_RETENTION', - label=_('Changelog retention'), - default=90, - description=_("Days to retain changelog history (set to zero for unlimited)"), - field=forms.IntegerField - ), ConfigParam( name='JOB_RETENTION', label=_('Job result retention'), diff --git a/netbox/netbox/configuration_example.py b/netbox/netbox/configuration_example.py index 61e8c6df2..ad73b11c7 100644 --- a/netbox/netbox/configuration_example.py +++ b/netbox/netbox/configuration_example.py @@ -140,6 +140,11 @@ EMAIL = { 'FROM_EMAIL': '', } +# Return CSV bulk exports as a streaming HTTP response, which avoids buffering the entire dataset in memory before +# sending it to the client. This is recommended for very large exports, but it alters the response behavior so it is +# disabled by default. +STREAMING_EXPORTS = False + # Exempt certain models from the enforcement of view permissions. Models listed here will be viewable by all users and # by anonymous users. List models in the form `.`. Add '*' to this list to exempt all models. EXEMPT_VIEW_PERMISSIONS = [ @@ -154,10 +159,6 @@ EXEMPT_VIEW_PERMISSIONS = [ # 'https': 'http://10.10.1.10:1080', # } -# IP addresses recognized as internal to the system. The debugging toolbar will be available only to clients accessing -# NetBox from an internal IP. -INTERNAL_IPS = ('127.0.0.1', '::1') - # Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs: # https://docs.djangoproject.com/en/stable/topics/logging/ LOGGING = {} @@ -166,9 +167,6 @@ LOGGING = {} # authenticated to NetBox indefinitely. LOGIN_PERSISTENCE = False -# Setting this to False will permit unauthenticated users to access most areas of NetBox (but not make any changes). -LOGIN_REQUIRED = True - # The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to # re-authenticate. (Default: 1209600 [14 days]) LOGIN_TIMEOUT = None diff --git a/netbox/netbox/constants.py b/netbox/netbox/constants.py index a0ba966d7..efe42c383 100644 --- a/netbox/netbox/constants.py +++ b/netbox/netbox/constants.py @@ -44,9 +44,9 @@ ADVISORY_LOCK_KEYS = { 'job-schedules': 110100, } -# TODO: Remove in NetBox v4.5 +# TODO: Remove in NetBox v4.7 # Legacy default view action permission mapping -DEFAULT_ACTION_PERMISSIONS = { +_DEFAULT_ACTION_PERMISSIONS = { 'add': {'add'}, 'export': {'view'}, 'bulk_import': {'add'}, @@ -54,6 +54,20 @@ DEFAULT_ACTION_PERMISSIONS = { 'bulk_delete': {'delete'}, } + +def __getattr__(name): + if name == 'DEFAULT_ACTION_PERMISSIONS': + import warnings + warnings.warn( + f"{name} is deprecated and will be removed in NetBox v4.7. " + "Define action permissions via ObjectAction subclasses instead.", + DeprecationWarning, + stacklevel=2, + ) + return _DEFAULT_ACTION_PERMISSIONS + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + # General-purpose tokens CENSOR_TOKEN = '********' CENSOR_TOKEN_CHANGED = '***CHANGED***' diff --git a/netbox/netbox/jobs.py b/netbox/netbox/jobs.py index 30afba6fc..e9511b33c 100644 --- a/netbox/netbox/jobs.py +++ b/netbox/netbox/jobs.py @@ -1,6 +1,9 @@ import logging +import os +import traceback from abc import ABC, abstractmethod from datetime import timedelta +from pathlib import Path from django.core.exceptions import ImproperlyConfigured from django.utils import timezone @@ -21,6 +24,11 @@ __all__ = ( 'system_job', ) +# The installation root, e.g. "/opt/netbox/". Used to strip absolute path +# prefixes from traceback file paths before recording them in the job log. +# jobs.py lives at /netbox/netbox/jobs.py, so parents[2] is the root. +_INSTALL_ROOT = str(Path(__file__).resolve().parents[2]) + os.sep + def system_job(interval): """ @@ -107,6 +115,13 @@ class JobRunner(ABC): job.terminate(status=JobStatusChoices.STATUS_FAILED) except Exception as e: + tb_str = traceback.format_exc().replace(_INSTALL_ROOT, '') + tb_record = logging.makeLogRecord({ + 'levelno': logging.ERROR, + 'levelname': 'ERROR', + 'msg': tb_str, + }) + job.log(tb_record) job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=repr(e)) if type(e) is JobTimeoutException: logger.error(e) @@ -127,6 +142,7 @@ class JobRunner(ABC): user=job.user, schedule_at=new_scheduled_time, interval=job.interval, + notifications=job.notifications, **kwargs, ) diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index 920c8e0c1..993828c1b 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -25,6 +25,7 @@ from netbox.registry import registry from netbox.signals import post_clean from netbox.utils import register_model_feature from utilities.json import CustomFieldJSONEncoder +from utilities.permissions import ModelAction, register_model_actions from utilities.serialization import serialize_object __all__ = ( @@ -722,10 +723,10 @@ def register_models(*models): for model in models: app_label, model_name = model._meta.label_lower.split('.') - # TODO: Remove in NetBox v4.5 - # Register public models + # TODO: Remove in NetBox v4.7 + # Register public models (access the underlying dict directly to avoid triggering the deprecation warning) if not getattr(model, '_netbox_private', False): - registry['models'][app_label].add(model_name) + dict.__getitem__(registry, 'models')[app_label].add(model_name) # Register applicable feature views for the model if issubclass(model, ContactsMixin): @@ -752,3 +753,12 @@ def register_models(*models): register_model_view(model, 'sync', kwargs={'model': model})( 'netbox.views.generic.ObjectSyncDataView' ) + + # Auto-register custom permission actions declared in Meta.permissions + if meta_permissions := getattr(model._meta, 'permissions', None): + actions = [ + ModelAction(codename, help_text=_(name)) + for codename, name in meta_permissions + ] + if actions: + register_model_actions(model, actions) diff --git a/netbox/netbox/navigation/menu.py b/netbox/netbox/navigation/menu.py index 1de2df0aa..7b34c2f1d 100644 --- a/netbox/netbox/navigation/menu.py +++ b/netbox/netbox/navigation/menu.py @@ -50,7 +50,6 @@ RACKS_MENU = Menu( label=_('Racks'), items=( get_model_item('dcim', 'rack', _('Racks')), - get_model_item('dcim', 'rackrole', _('Rack Roles')), get_model_item('dcim', 'rackreservation', _('Reservations')), MenuItem( link='dcim:rack_elevation_list', @@ -59,6 +58,13 @@ RACKS_MENU = Menu( ), ), ), + MenuGroup( + label=_('Rack Organization'), + items=( + get_model_item('dcim', 'rackgroup', _('Rack Groups')), + get_model_item('dcim', 'rackrole', _('Rack Roles')), + ), + ), MenuGroup( label=_('Rack Types'), items=( @@ -125,6 +131,7 @@ CONNECTIONS_MENU = Menu( label=_('Connections'), items=( get_model_item('dcim', 'cable', _('Cables')), + get_model_item('dcim', 'cablebundle', _('Cable Bundles')), get_model_item('wireless', 'wirelesslink', _('Wireless Links')), MenuItem( link='dcim:interface_connections_list', @@ -261,6 +268,7 @@ VIRTUALIZATION_MENU = Menu( get_model_item('virtualization', 'virtualmachine', _('Virtual Machines')), get_model_item('virtualization', 'vminterface', _('Interfaces')), get_model_item('virtualization', 'virtualdisk', _('Virtual Disks')), + get_model_item('virtualization', 'virtualmachinetype', _('Virtual Machine Types')), ), ), MenuGroup( diff --git a/netbox/netbox/registry.py b/netbox/netbox/registry.py index 829d0de93..f894a11b0 100644 --- a/netbox/netbox/registry.py +++ b/netbox/netbox/registry.py @@ -9,6 +9,15 @@ class Registry(dict): removed (though the value of each key is mutable). """ def __getitem__(self, key): + # TODO: Remove in NetBox v4.7 + if key == 'models': + import warnings + warnings.warn( + 'The "models" registry key is deprecated and will be removed in NetBox v4.7. Registered models can be ' + 'obtained by calling ObjectType.objects.public().', + DeprecationWarning, + stacklevel=2, + ) try: return super().__getitem__(key) except KeyError: @@ -28,7 +37,9 @@ registry = Registry({ 'denormalized_fields': collections.defaultdict(list), 'event_types': dict(), 'filtersets': dict(), + 'model_actions': collections.defaultdict(set), 'model_features': dict(), + # TODO: Remove in NetBox v4.7 'models': collections.defaultdict(set), 'plugins': dict(), 'request_processors': list(), diff --git a/netbox/netbox/search/backends.py b/netbox/netbox/search/backends.py index 12dfbdcff..661134c95 100644 --- a/netbox/netbox/search/backends.py +++ b/netbox/netbox/search/backends.py @@ -1,9 +1,11 @@ +import logging from collections import defaultdict import netaddr from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ImproperlyConfigured +from django.db import ProgrammingError from django.db.models import F, Q, Window, prefetch_related_objects from django.db.models.fields.related import ForeignKey from django.db.models.functions import window @@ -24,6 +26,8 @@ from . import FieldTypes, LookupTypes, get_indexer DEFAULT_LOOKUP_TYPE = LookupTypes.PARTIAL MAX_RESULTS = 1000 +logger = logging.getLogger(__name__) + class SearchBackend: """ @@ -63,7 +67,12 @@ class SearchBackend: """ Receiver for the post_save signal, responsible for caching object creation/changes. """ - self.cache(instance, remove_existing=not created) + try: + self.cache(instance, remove_existing=not created) + except ProgrammingError as e: + # The schema may be incomplete during migrations; skip caching. + logger.warning(f"Skipping search cache update due to schema error: {e}") + pass def removal_handler(self, sender, instance, **kwargs): """ diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 592309e82..33631f943 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -121,6 +121,7 @@ DEFAULT_PERMISSIONS = getattr(configuration, 'DEFAULT_PERMISSIONS', { DEVELOPER = getattr(configuration, 'DEVELOPER', False) DOCS_ROOT = getattr(configuration, 'DOCS_ROOT', os.path.join(os.path.dirname(BASE_DIR), 'docs')) EMAIL = getattr(configuration, 'EMAIL', {}) +STREAMING_EXPORTS = getattr(configuration, 'STREAMING_EXPORTS', False) EVENTS_PIPELINE = getattr(configuration, 'EVENTS_PIPELINE', [ 'extras.events.process_event_queue', ]) @@ -180,15 +181,15 @@ SECURE_HSTS_PRELOAD = getattr(configuration, 'SECURE_HSTS_PRELOAD', False) SECURE_HSTS_SECONDS = getattr(configuration, 'SECURE_HSTS_SECONDS', 0) SECURE_SSL_REDIRECT = getattr(configuration, 'SECURE_SSL_REDIRECT', False) SENTRY_CONFIG = getattr(configuration, 'SENTRY_CONFIG', {}) -# TODO: Remove in NetBox v4.5 +# TODO: Remove in NetBox v4.7 SENTRY_DSN = getattr(configuration, 'SENTRY_DSN', None) SENTRY_ENABLED = getattr(configuration, 'SENTRY_ENABLED', False) -# TODO: Remove in NetBox v4.5 +# TODO: Remove in NetBox v4.7 SENTRY_SAMPLE_RATE = getattr(configuration, 'SENTRY_SAMPLE_RATE', 1.0) -# TODO: Remove in NetBox v4.5 +# TODO: Remove in NetBox v4.7 SENTRY_SEND_DEFAULT_PII = getattr(configuration, 'SENTRY_SEND_DEFAULT_PII', False) SENTRY_TAGS = getattr(configuration, 'SENTRY_TAGS', {}) -# TODO: Remove in NetBox v4.5 +# TODO: Remove in NetBox v4.7 SENTRY_TRACES_SAMPLE_RATE = getattr(configuration, 'SENTRY_TRACES_SAMPLE_RATE', 0) SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid') SESSION_COOKIE_PATH = CSRF_COOKIE_PATH @@ -243,6 +244,20 @@ for path in PROXY_ROUTERS: except ImportError: raise ImproperlyConfigured(f"Invalid path in PROXY_ROUTERS: {path}") +# Warn on the presence of deprecated configuration parameters +if not LOGIN_REQUIRED: + warnings.warn( + "LOGIN_REQUIRED is deprecated and will be removed in NetBox v5.0. Unauthenticated access to the application " + "will no longer be supported. Please plan to require authentication for all users before upgrading.", + DeprecationWarning, + ) +elif hasattr(configuration, 'LOGIN_REQUIRED'): + warnings.warn( + "LOGIN_REQUIRED is deprecated and will be removed in NetBox v5.0. This parameter can be removed from your " + "configuration file.", + DeprecationWarning, + ) + # # Database @@ -270,12 +285,14 @@ if STORAGE_BACKEND is not None: ) else: warnings.warn( - "STORAGE_BACKEND is deprecated, use the new STORAGES setting instead." + "STORAGE_BACKEND is deprecated, use the new STORAGES setting instead.", + DeprecationWarning, ) if STORAGE_CONFIG is not None: warnings.warn( - "STORAGE_CONFIG is deprecated, use the new STORAGES setting instead." + "STORAGE_CONFIG is deprecated, use the new STORAGES setting instead.", + DeprecationWarning, ) # Default STORAGES for Django @@ -436,6 +453,7 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.humanize', + 'django.contrib.postgres', 'django.forms', 'corsheaders', 'debug_toolbar', @@ -587,6 +605,10 @@ SERIALIZATION_MODULES = { 'json': 'utilities.serializers.json', } +DEBUG_TOOLBAR_CONFIG = { + 'SHOW_TOOLBAR_CALLBACK': 'utilities.debug.show_toolbar', +} + # # Permissions & authentication @@ -615,6 +637,14 @@ MAINTENANCE_EXEMPT_PATHS = ( # Sentry # +# Warn on the presence of deprecated Sentry config parameters +for config_param in ('SENTRY_DSN', 'SENTRY_SAMPLE_RATE', 'SENTRY_SEND_DEFAULT_PII', 'SENTRY_TRACES_SAMPLE_RATE'): + if hasattr(configuration, config_param): + warnings.warn( + f"{config_param} is deprecated and will be removed in NetBox v4.7. Use SENTRY_CONFIG instead.", + DeprecationWarning, + ) + if SENTRY_ENABLED: try: import sentry_sdk @@ -724,7 +754,7 @@ REST_FRAMEWORK = { 'rest_framework.filters.OrderingFilter', ), 'DEFAULT_METADATA_CLASS': 'netbox.api.metadata.BulkOperationMetadata', - 'DEFAULT_PAGINATION_CLASS': 'netbox.api.pagination.OptionalLimitOffsetPagination', + 'DEFAULT_PAGINATION_CLASS': 'netbox.api.pagination.NetBoxPagination', 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.MultiPartParser', diff --git a/netbox/netbox/tables/tables.py b/netbox/netbox/tables/tables.py index e3a752156..7c7275f21 100644 --- a/netbox/netbox/tables/tables.py +++ b/netbox/netbox/tables/tables.py @@ -192,6 +192,18 @@ class BaseTable(tables.Table): columns = getattr(self.Meta, 'default_columns', self.Meta.fields) self._set_columns(columns) + + # Apply column inclusion/exclusion (overrides user preferences) + if columns_param := request.GET.get('include_columns'): + for column_name in columns_param.split(','): + if column_name in self.columns.names(): + self.columns.show(column_name) + if exclude_columns := request.GET.get('exclude_columns'): + exclude_columns = exclude_columns.split(',') + for column_name in exclude_columns: + if column_name in self.columns.names() and column_name not in self.exempt_columns: + self.columns.hide(column_name) + self._apply_prefetching() if ordering is not None: self.order_by = ordering diff --git a/netbox/netbox/tests/test_api.py b/netbox/netbox/tests/test_api.py index 5cc2bf060..0cff906a4 100644 --- a/netbox/netbox/tests/test_api.py +++ b/netbox/netbox/tests/test_api.py @@ -2,10 +2,11 @@ import uuid from django.test import RequestFactory, TestCase from django.urls import reverse +from rest_framework.exceptions import ValidationError from rest_framework.request import Request from netbox.api.exceptions import QuerySetNotOrdered -from netbox.api.pagination import OptionalLimitOffsetPagination +from netbox.api.pagination import NetBoxPagination from users.models import Token from utilities.testing import APITestCase @@ -48,7 +49,7 @@ class AppTest(APITestCase): class OptionalLimitOffsetPaginationTest(TestCase): def setUp(self): - self.paginator = OptionalLimitOffsetPagination() + self.paginator = NetBoxPagination() self.factory = RequestFactory() def _make_drf_request(self, path='/', query_params=None): @@ -80,3 +81,33 @@ class OptionalLimitOffsetPaginationTest(TestCase): request = self._make_drf_request() self.paginator.paginate_queryset(iterable, request) # Should not raise exception + + def test_get_start_returns_none_when_absent(self): + """get_start() returns None when start param is not in the request""" + request = self._make_drf_request() + self.assertIsNone(self.paginator.get_start(request)) + + def test_get_start_returns_integer(self): + """get_start() returns an integer when start param is present""" + request = self._make_drf_request(query_params={'start': '42'}) + self.assertEqual(self.paginator.get_start(request), 42) + + def test_get_start_raises_for_negative(self): + """get_start() raises ValidationError for negative values""" + request = self._make_drf_request(query_params={'start': '-1'}) + with self.assertRaises(ValidationError): + self.paginator.get_start(request) + + def test_cursor_and_offset_conflict_raises_validation_error(self): + """paginate_queryset() raises ValidationError when both start and offset are specified""" + queryset = Token.objects.all().order_by('created') + request = self._make_drf_request(query_params={'start': '1', 'offset': '10'}) + with self.assertRaises(ValidationError): + self.paginator.paginate_queryset(queryset, request) + + def test_cursor_and_ordering_conflict_raises_validation_error(self): + """paginate_queryset() raises ValidationError when both start and ordering are specified""" + queryset = Token.objects.all().order_by('created') + request = self._make_drf_request(query_params={'start': '1', 'ordering': 'created'}) + with self.assertRaises(ValidationError): + self.paginator.paginate_queryset(queryset, request) diff --git a/netbox/netbox/tests/test_jobs.py b/netbox/netbox/tests/test_jobs.py index 085047bfc..5bb073951 100644 --- a/netbox/netbox/tests/test_jobs.py +++ b/netbox/netbox/tests/test_jobs.py @@ -10,6 +10,7 @@ from core.models import DataSource, Job from utilities.testing import disable_warnings from ..jobs import * +from ..jobs import _INSTALL_ROOT class TestJobRunner(JobRunner): @@ -83,6 +84,12 @@ class JobRunnerTest(JobRunnerTestCase): self.assertEqual(job.status, JobStatusChoices.STATUS_ERRORED) self.assertEqual(job.error, repr(ErroredJobRunner.EXP)) + self.assertEqual(len(job.log_entries), 1) + self.assertEqual(job.log_entries[0]['level'], 'error') + tb_message = job.log_entries[0]['message'] + self.assertIn('Traceback', tb_message) + self.assertIn('Test error', tb_message) + self.assertNotIn(_INSTALL_ROOT, tb_message) class EnqueueTest(JobRunnerTestCase): diff --git a/netbox/netbox/tests/test_ui.py b/netbox/netbox/tests/test_ui.py index 8a328793f..31c38bf81 100644 --- a/netbox/netbox/tests/test_ui.py +++ b/netbox/netbox/tests/test_ui.py @@ -1,3 +1,5 @@ +from types import SimpleNamespace + from django.test import RequestFactory, TestCase from circuits.choices import CircuitStatusChoices, VirtualCircuitTerminationRoleChoices @@ -79,7 +81,7 @@ class ChoiceAttrTest(TestCase): self.termination.get_role_display(), ) self.assertEqual( - attr.get_context(self.termination, {}), + attr.get_context(self.termination, 'role', attr.get_value(self.termination), {}), {'bg_color': self.termination.get_role_color()}, ) @@ -91,7 +93,7 @@ class ChoiceAttrTest(TestCase): self.termination.interface.get_type_display(), ) self.assertEqual( - attr.get_context(self.termination, {}), + attr.get_context(self.termination, 'interface.type', attr.get_value(self.termination), {}), {'bg_color': None}, ) @@ -103,7 +105,9 @@ class ChoiceAttrTest(TestCase): self.termination.virtual_circuit.get_status_display(), ) self.assertEqual( - attr.get_context(self.termination, {}), + attr.get_context( + self.termination, 'virtual_circuit.status', attr.get_value(self.termination), {} + ), {'bg_color': self.termination.virtual_circuit.get_status_color()}, ) @@ -218,6 +222,179 @@ class RelatedObjectListAttrTest(TestCase): self.assertIn('…', rendered) +class TextAttrTest(TestCase): + + def test_get_value_with_format_string(self): + attr = attrs.TextAttr('asn', format_string='AS{}') + obj = SimpleNamespace(asn=65000) + self.assertEqual(attr.get_value(obj), 'AS65000') + + def test_get_value_without_format_string(self): + attr = attrs.TextAttr('name') + obj = SimpleNamespace(name='foo') + self.assertEqual(attr.get_value(obj), 'foo') + + def test_get_value_none_skips_format_string(self): + attr = attrs.TextAttr('name', format_string='prefix-{}') + obj = SimpleNamespace(name=None) + self.assertIsNone(attr.get_value(obj)) + + def test_get_context(self): + attr = attrs.TextAttr('name', style='text-monospace', copy_button=True) + obj = SimpleNamespace(name='bar') + context = attr.get_context(obj, 'name', 'bar', {}) + self.assertEqual(context['style'], 'text-monospace') + self.assertTrue(context['copy_button']) + + +class NumericAttrTest(TestCase): + + def test_get_context_with_unit_accessor(self): + attr = attrs.NumericAttr('speed', unit_accessor='speed_unit') + obj = SimpleNamespace(speed=1000, speed_unit='Mbps') + context = attr.get_context(obj, 'speed', 1000, {}) + self.assertEqual(context['unit'], 'Mbps') + + def test_get_context_without_unit_accessor(self): + attr = attrs.NumericAttr('speed') + obj = SimpleNamespace(speed=1000) + context = attr.get_context(obj, 'speed', 1000, {}) + self.assertIsNone(context['unit']) + + def test_get_context_copy_button(self): + attr = attrs.NumericAttr('speed', copy_button=True) + obj = SimpleNamespace(speed=1000) + context = attr.get_context(obj, 'speed', 1000, {}) + self.assertTrue(context['copy_button']) + + +class BooleanAttrTest(TestCase): + + def test_false_value_shown_by_default(self): + attr = attrs.BooleanAttr('enabled') + obj = SimpleNamespace(enabled=False) + self.assertIs(attr.get_value(obj), False) + + def test_false_value_hidden_when_display_false_disabled(self): + attr = attrs.BooleanAttr('enabled', display_false=False) + obj = SimpleNamespace(enabled=False) + self.assertIsNone(attr.get_value(obj)) + + def test_true_value_always_shown(self): + attr = attrs.BooleanAttr('enabled', display_false=False) + obj = SimpleNamespace(enabled=True) + self.assertIs(attr.get_value(obj), True) + + +class ImageAttrTest(TestCase): + + def test_invalid_decoding_raises_value_error(self): + with self.assertRaises(ValueError): + attrs.ImageAttr('image', decoding='invalid') + + def test_default_decoding_for_lazy_image(self): + attr = attrs.ImageAttr('image') + self.assertTrue(attr.load_lazy) + self.assertEqual(attr.decoding, 'async') + + def test_default_decoding_for_non_lazy_image(self): + attr = attrs.ImageAttr('image', load_lazy=False) + self.assertFalse(attr.load_lazy) + self.assertIsNone(attr.decoding) + + def test_explicit_decoding_value(self): + attr = attrs.ImageAttr('image', load_lazy=False, decoding='sync') + self.assertEqual(attr.decoding, 'sync') + + def test_get_context(self): + attr = attrs.ImageAttr('image', load_lazy=False, decoding='async') + obj = SimpleNamespace(image='test.png') + context = attr.get_context(obj, 'image', 'test.png', {}) + self.assertEqual(context['decoding'], 'async') + self.assertFalse(context['load_lazy']) + + +class RelatedObjectAttrTest(TestCase): + + def test_get_context_with_grouped_by(self): + region = SimpleNamespace(name='Region 1') + site = SimpleNamespace(name='Site 1', region=region) + obj = SimpleNamespace(site=site) + attr = attrs.RelatedObjectAttr('site', grouped_by='region') + context = attr.get_context(obj, 'site', site, {}) + self.assertEqual(context['group'], region) + + def test_get_context_without_grouped_by(self): + site = SimpleNamespace(name='Site 1') + obj = SimpleNamespace(site=site) + attr = attrs.RelatedObjectAttr('site') + context = attr.get_context(obj, 'site', site, {}) + self.assertIsNone(context['group']) + + def test_get_context_linkify(self): + site = SimpleNamespace(name='Site 1') + obj = SimpleNamespace(site=site) + attr = attrs.RelatedObjectAttr('site', linkify=True) + context = attr.get_context(obj, 'site', site, {}) + self.assertTrue(context['linkify']) + + +class GenericForeignKeyAttrTest(TestCase): + + def test_get_context_content_type(self): + value = SimpleNamespace(_meta=SimpleNamespace(verbose_name='provider')) + obj = SimpleNamespace() + attr = attrs.GenericForeignKeyAttr('assigned_object') + context = attr.get_context(obj, 'assigned_object', value, {}) + self.assertEqual(context['content_type'], 'provider') + + def test_get_context_linkify(self): + value = SimpleNamespace(_meta=SimpleNamespace(verbose_name='provider')) + obj = SimpleNamespace() + attr = attrs.GenericForeignKeyAttr('assigned_object', linkify=True) + context = attr.get_context(obj, 'assigned_object', value, {}) + self.assertTrue(context['linkify']) + + +class GPSCoordinatesAttrTest(TestCase): + + def test_missing_latitude_returns_placeholder(self): + attr = attrs.GPSCoordinatesAttr() + obj = SimpleNamespace(latitude=None, longitude=-74.006) + self.assertEqual(attr.render(obj, {'name': 'coordinates'}), attr.placeholder) + + def test_missing_longitude_returns_placeholder(self): + attr = attrs.GPSCoordinatesAttr() + obj = SimpleNamespace(latitude=40.712, longitude=None) + self.assertEqual(attr.render(obj, {'name': 'coordinates'}), attr.placeholder) + + def test_both_missing_returns_placeholder(self): + attr = attrs.GPSCoordinatesAttr() + obj = SimpleNamespace(latitude=None, longitude=None) + self.assertEqual(attr.render(obj, {'name': 'coordinates'}), attr.placeholder) + + +class DateTimeAttrTest(TestCase): + + def test_default_spec(self): + attr = attrs.DateTimeAttr('created') + obj = SimpleNamespace(created='2024-01-01') + context = attr.get_context(obj, 'created', '2024-01-01', {}) + self.assertEqual(context['spec'], 'seconds') + + def test_date_spec(self): + attr = attrs.DateTimeAttr('created', spec='date') + obj = SimpleNamespace(created='2024-01-01') + context = attr.get_context(obj, 'created', '2024-01-01', {}) + self.assertEqual(context['spec'], 'date') + + def test_minutes_spec(self): + attr = attrs.DateTimeAttr('created', spec='minutes') + obj = SimpleNamespace(created='2024-01-01') + context = attr.get_context(obj, 'created', '2024-01-01', {}) + self.assertEqual(context['spec'], 'minutes') + + class ObjectsTablePanelTest(TestCase): """ Verify that ObjectsTablePanel.should_render() hides the panel when diff --git a/netbox/netbox/ui/actions.py b/netbox/netbox/ui/actions.py index ef0b0f66f..8eb4ffbf1 100644 --- a/netbox/netbox/ui/actions.py +++ b/netbox/netbox/ui/actions.py @@ -59,7 +59,7 @@ class PanelAction: """ # Enforce permissions user = context['request'].user - if not user.has_perms(self.permissions): + if self.permissions and not user.has_perms(self.permissions): return '' return render_to_string(self.template_name, self.get_context(context)) @@ -122,19 +122,19 @@ class AddObject(LinkAction): url_params (dict): A dictionary of arbitrary URL parameters to append to the resolved URL """ def __init__(self, model, url_params=None, **kwargs): - # Resolve the model class from its app.name label - try: - app_label, model_name = model.split('.') - model = apps.get_model(app_label, model_name) - except (ValueError, LookupError): + # Resolve the model from its label + if '.' not in model: + raise ValueError(f"Invalid model label: {model}") + try: + self.model = apps.get_model(model) + except LookupError: raise ValueError(f"Invalid model label: {model}") - view_name = get_viewname(model, 'add') kwargs.setdefault('label', _('Add')) kwargs.setdefault('button_icon', 'plus-thick') - kwargs.setdefault('permissions', [get_permission_for_model(model, 'add')]) + kwargs.setdefault('permissions', [get_permission_for_model(self.model, 'add')]) - super().__init__(view_name=view_name, url_params=url_params, **kwargs) + super().__init__(view_name=get_viewname(self.model, 'add'), url_params=url_params, **kwargs) class CopyContent(PanelAction): @@ -152,10 +152,8 @@ class CopyContent(PanelAction): super().__init__(**kwargs) self.target_id = target_id - def render(self, context): - return render_to_string(self.template_name, { + def get_context(self, context): + return { + **super().get_context(context), 'target_id': self.target_id, - 'label': self.label, - 'button_class': self.button_class, - 'button_icon': self.button_icon, - }) + } diff --git a/netbox/netbox/ui/attrs.py b/netbox/netbox/ui/attrs.py index 992ac6f2a..d5f066ddd 100644 --- a/netbox/netbox/ui/attrs.py +++ b/netbox/netbox/ui/attrs.py @@ -29,11 +29,27 @@ PLACEHOLDER_HTML = '' IMAGE_DECODING_CHOICES = ('auto', 'async', 'sync') + +# +# Mixins +# + +class MapURLMixin: + _map_url = None + + @property + def map_url(self): + if self._map_url is True: + return get_config().MAPS_URL + if self._map_url: + return self._map_url + return None + + # # Attributes # - class ObjectAttribute: """ Base class for representing an attribute of an object. @@ -64,17 +80,20 @@ class ObjectAttribute: """ return resolve_attr_path(obj, self.accessor) - def get_context(self, obj, context): + def get_context(self, obj, attr, value, context): """ Return any additional template context used to render the attribute value. Parameters: obj (object): The object for which the attribute is being rendered - context (dict): The root template context + attr (str): The name of the attribute being rendered + value: The value of the attribute on the object + context (dict): The panel template context """ return {} def render(self, obj, context): + name = context['name'] value = self.get_value(obj) # If the value is empty, render a placeholder @@ -82,8 +101,8 @@ class ObjectAttribute: return self.placeholder return render_to_string(self.template_name, { - **self.get_context(obj, context), - 'name': context['name'], + **self.get_context(obj, name, value, context), + 'name': name, 'value': value, }) @@ -112,7 +131,7 @@ class TextAttr(ObjectAttribute): return self.format_string.format(value) return value - def get_context(self, obj, context): + def get_context(self, obj, attr, value, context): return { 'style': self.style, 'copy_button': self.copy_button, @@ -134,7 +153,7 @@ class NumericAttr(ObjectAttribute): self.unit_accessor = unit_accessor self.copy_button = copy_button - def get_context(self, obj, context): + def get_context(self, obj, attr, value, context): unit = resolve_attr_path(obj, self.unit_accessor) if self.unit_accessor else None return { 'unit': unit, @@ -172,7 +191,7 @@ class ChoiceAttr(ObjectAttribute): return resolve_attr_path(target, field_name) - def get_context(self, obj, context): + def get_context(self, obj, attr, value, context): target, field_name = self._resolve_target(obj) if target is None: return {'bg_color': None} @@ -241,7 +260,7 @@ class ImageAttr(ObjectAttribute): decoding = 'async' self.decoding = decoding - def get_context(self, obj, context): + def get_context(self, obj, attr, value, context): return { 'decoding': self.decoding, 'load_lazy': self.load_lazy, @@ -266,8 +285,7 @@ class RelatedObjectAttr(ObjectAttribute): self.grouped_by = grouped_by self.colored = colored - def get_context(self, obj, context): - value = self.get_value(obj) + def get_context(self, obj, attr, value, context): group = getattr(value, self.grouped_by, None) if self.grouped_by else None return { 'linkify': self.linkify, @@ -303,14 +321,13 @@ class RelatedObjectListAttr(RelatedObjectAttr): self.max_items = max_items self.overflow_indicator = overflow_indicator - def _get_items(self, obj): + def _get_items(self, items): """ Retrieve items from the given object using the accessor path. Returns a tuple of (items, has_more) where items is a list of resolved objects and has_more indicates whether additional items exist beyond the max_items limit. """ - items = resolve_attr_path(obj, self.accessor) if items is None: return [], False @@ -325,8 +342,8 @@ class RelatedObjectListAttr(RelatedObjectAttr): return items[:self.max_items], has_more - def get_context(self, obj, context): - items, has_more = self._get_items(obj) + def get_context(self, obj, attr, value, context): + items, has_more = self._get_items(value) return { 'linkify': self.linkify, @@ -342,14 +359,15 @@ class RelatedObjectListAttr(RelatedObjectAttr): } def render(self, obj, context): - context = context or {} - context_data = self.get_context(obj, context) + name = context['name'] + value = self.get_value(obj) + context_data = self.get_context(obj, name, value, context) if not context_data['items']: return self.placeholder return render_to_string(self.template_name, { - 'name': context.get('name'), + 'name': name, **context_data, }) @@ -372,11 +390,12 @@ class NestedObjectAttr(ObjectAttribute): self.max_depth = max_depth self.colored = colored - def get_context(self, obj, context): - value = self.get_value(obj) - nodes = value.get_ancestors(include_self=True) - if self.max_depth: - nodes = list(nodes)[-self.max_depth:] + def get_context(self, obj, attr, value, context): + nodes = [] + if value is not None: + nodes = value.get_ancestors(include_self=True) + if self.max_depth: + nodes = list(nodes)[-self.max_depth:] return { 'nodes': nodes, 'linkify': self.linkify, @@ -401,40 +420,35 @@ class GenericForeignKeyAttr(ObjectAttribute): super().__init__(*args, **kwargs) self.linkify = linkify - def get_context(self, obj, context): - value = self.get_value(obj) - content_type = value._meta.verbose_name + def get_context(self, obj, attr, value, context): + content_type = value._meta.verbose_name if value is not None else None return { 'content_type': content_type, 'linkify': self.linkify, } -class AddressAttr(ObjectAttribute): +class AddressAttr(MapURLMixin, ObjectAttribute): """ A physical or mailing address. Parameters: - map_url (bool): If true, the address will render as a hyperlink using settings.MAPS_URL + map_url (bool/str): The URL to use when rendering the address. If True, the address will render as a + hyperlink using settings.MAPS_URL. """ template_name = 'ui/attrs/address.html' def __init__(self, *args, map_url=True, **kwargs): super().__init__(*args, **kwargs) - if map_url is True: - self.map_url = get_config().MAPS_URL - elif map_url: - self.map_url = map_url - else: - self.map_url = None + self._map_url = map_url - def get_context(self, obj, context): + def get_context(self, obj, attr, value, context): return { 'map_url': self.map_url, } -class GPSCoordinatesAttr(ObjectAttribute): +class GPSCoordinatesAttr(MapURLMixin, ObjectAttribute): """ A GPS coordinates pair comprising latitude and longitude values. @@ -447,24 +461,18 @@ class GPSCoordinatesAttr(ObjectAttribute): label = _('GPS coordinates') def __init__(self, latitude_attr='latitude', longitude_attr='longitude', map_url=True, **kwargs): - super().__init__(accessor=None, **kwargs) + super().__init__(accessor=latitude_attr, **kwargs) self.latitude_attr = latitude_attr self.longitude_attr = longitude_attr - if map_url is True: - self.map_url = get_config().MAPS_URL - elif map_url: - self.map_url = map_url - else: - self.map_url = None + self._map_url = map_url - def render(self, obj, context=None): - context = context or {} + def render(self, obj, context): latitude = resolve_attr_path(obj, self.latitude_attr) longitude = resolve_attr_path(obj, self.longitude_attr) if latitude is None or longitude is None: return self.placeholder return render_to_string(self.template_name, { - **context, + 'name': context['name'], 'latitude': latitude, 'longitude': longitude, 'map_url': self.map_url, @@ -485,7 +493,7 @@ class DateTimeAttr(ObjectAttribute): super().__init__(*args, **kwargs) self.spec = spec - def get_context(self, obj, context): + def get_context(self, obj, attr, value, context): return { 'spec': self.spec, } @@ -511,8 +519,9 @@ class TemplatedAttr(ObjectAttribute): self.template_name = template_name self.context = context or {} - def get_context(self, obj, context): + def get_context(self, obj, attr, value, context): return { + **context, **self.context, 'object': obj, } diff --git a/netbox/netbox/ui/layout.py b/netbox/netbox/ui/layout.py index b59fd7b34..375417288 100644 --- a/netbox/netbox/ui/layout.py +++ b/netbox/netbox/ui/layout.py @@ -21,10 +21,16 @@ class Layout: """ def __init__(self, *rows): for i, row in enumerate(rows): - if type(row) is not Row: + if not isinstance(row, Row): raise TypeError(f"Row {i} must be a Row instance, not {type(row)}.") self.rows = rows + def __iter__(self): + return iter(self.rows) + + def __repr__(self): + return f"Layout({len(self.rows)} rows)" + class Row: """ @@ -35,10 +41,16 @@ class Row: """ def __init__(self, *columns): for i, column in enumerate(columns): - if type(column) is not Column: + if not isinstance(column, Column): raise TypeError(f"Column {i} must be a Column instance, not {type(column)}.") self.columns = columns + def __iter__(self): + return iter(self.columns) + + def __repr__(self): + return f"Row({len(self.columns)} columns)" + class Column: """ @@ -46,12 +58,25 @@ class Column: Parameters: *panels: One or more Panel instances + width: Bootstrap grid column width (1-12). If unset, the column will expand to fill available space. """ - def __init__(self, *panels): + def __init__(self, *panels, width=None): for i, panel in enumerate(panels): if not isinstance(panel, Panel): raise TypeError(f"Panel {i} must be an instance of a Panel, not {type(panel)}.") + if width is not None: + if type(width) is not int: + raise ValueError(f"Column width must be an integer, not {type(width)}") + if width not in range(1, 13): + raise ValueError(f"Column width must be an integer between 1 and 12 (got {width}).") self.panels = panels + self.width = width + + def __iter__(self): + return iter(self.panels) + + def __repr__(self): + return f"Column({len(self.panels)} panels)" # @@ -62,7 +87,7 @@ class SimpleLayout(Layout): """ A layout with one row of two columns and a second row with one column. - Plugin content registered for `left_page`, `right_page`, or `full_width_path` is included automatically. Most object + Plugin content registered for `left_page`, `right_page`, or `full_width_page` is included automatically. Most object views in NetBox utilize this layout. ``` diff --git a/netbox/netbox/ui/panels.py b/netbox/netbox/ui/panels.py index f1afa963b..dadcd285a 100644 --- a/netbox/netbox/ui/panels.py +++ b/netbox/netbox/ui/panels.py @@ -46,18 +46,17 @@ class Panel: Parameters: title (str): The human-friendly title of the panel actions (list): An iterable of PanelActions to include in the panel header - template_name (str): Overrides the default template name, if defined """ template_name = None title = None actions = None - def __init__(self, title=None, actions=None, template_name=None): + def __init__(self, title=None, actions=None): if title is not None: self.title = title - self.actions = actions or self.actions or [] - if template_name is not None: - self.template_name = template_name + if actions is not None: + self.actions = actions + self.actions = list(self.actions) if self.actions else [] def get_context(self, context): """ @@ -118,9 +117,15 @@ class ObjectPanel(Panel): def get_context(self, context): obj = resolve_attr_path(context, self.accessor) + if self.title is not None: + title_ = self.title + elif obj is not None: + title_ = title(obj._meta.verbose_name) + else: + title_ = None return { **super().get_context(context), - 'title': self.title or title(obj._meta.verbose_name), + 'title': title_, 'object': obj, } @@ -200,7 +205,7 @@ class ObjectAttributesPanel(ObjectPanel, metaclass=ObjectAttributesPanelMeta): 'attrs': [ { 'label': attr.label or self._name_to_label(name), - 'value': attr.render(ctx['object'], {'name': name}), + 'value': attr.render(ctx['object'], {'name': name, 'perms': ctx['perms']}), } for name, attr in self._attrs.items() if name in attr_names ], } @@ -238,9 +243,10 @@ class CommentsPanel(ObjectPanel): self.field_name = field_name def get_context(self, context): + ctx = super().get_context(context) return { - **super().get_context(context), - 'comments': getattr(context['object'], self.field_name), + **ctx, + 'comments': getattr(ctx['object'], self.field_name, None), } @@ -262,9 +268,10 @@ class JSONPanel(ObjectPanel): self.actions.append(CopyContent(f'panel_{field_name}')) def get_context(self, context): + ctx = super().get_context(context) return { - **super().get_context(context), - 'data': getattr(context['object'], self.field_name), + **ctx, + 'data': getattr(ctx['object'], self.field_name, None), 'field_name': self.field_name, } @@ -295,35 +302,49 @@ class ObjectsTablePanel(Panel): model (str): The dotted label of the model to be added (e.g. "dcim.site") filters (dict): A dictionary of arbitrary URL parameters to append to the table's URL. If the value of a key is a callable, it will be passed the current template context. + include_columns (list): A list of column names to always display (overrides user preferences) + exclude_columns (list): A list of column names to hide from the table (overrides user preferences) """ template_name = 'ui/panels/objects_table.html' title = None - def __init__(self, model, filters=None, **kwargs): + def __init__(self, model, filters=None, include_columns=None, exclude_columns=None, **kwargs): super().__init__(**kwargs) - # Resolve the model class from its app.name label - try: - app_label, model_name = model.split('.') - self.model = apps.get_model(app_label, model_name) - except (ValueError, LookupError): + # Validate the model label format + if '.' not in model: raise ValueError(f"Invalid model label: {model}") - + self.model_label = model self.filters = filters or {} + self.include_columns = include_columns or [] + self.exclude_columns = exclude_columns or [] - # If no title is specified, derive one from the model name - if self.title is None: - self.title = title(self.model._meta.verbose_name_plural) + @property + def model(self): + try: + return apps.get_model(self.model_label) + except LookupError: + raise ValueError(f"Invalid model label: {self.model_label}") def get_context(self, context): + model = self.model + + # If no title is specified, derive one from the model name + panel_title = self.title or title(model._meta.verbose_name_plural) + url_params = { k: v(context) if callable(v) else v for k, v in self.filters.items() } if 'return_url' not in url_params and 'object' in context: url_params['return_url'] = context['object'].get_absolute_url() + if self.include_columns: + url_params['include_columns'] = ','.join(self.include_columns) + if self.exclude_columns: + url_params['exclude_columns'] = ','.join(self.exclude_columns) return { **super().get_context(context), - 'viewname': get_viewname(self.model, 'list'), + 'title': panel_title, + 'viewname': get_viewname(model, 'list'), 'url_params': dict_to_querydict(url_params), } @@ -345,12 +366,17 @@ class TemplatePanel(Panel): Parameters: template_name (str): The name of the template to render """ - def __init__(self, template_name): - super().__init__(template_name=template_name) + def __init__(self, template_name, **kwargs): + self.template_name = template_name + super().__init__(**kwargs) - def render(self, context): - # Pass the entire context to the template - return render_to_string(self.template_name, context.flatten()) + def get_context(self, context): + # Pass the entire context to the template, but let the panel's own context take precedence + # for panel-specific variables (title, actions, panel_class) + return { + **context.flatten(), + **super().get_context(context) + } class TextCodePanel(ObjectPanel): @@ -365,10 +391,11 @@ class TextCodePanel(ObjectPanel): self.show_sync_warning = show_sync_warning def get_context(self, context): + ctx = super().get_context(context) return { - **super().get_context(context), + **ctx, 'show_sync_warning': self.show_sync_warning, - 'value': getattr(context.get('object'), self.field_name, None), + 'value': getattr(ctx['object'], self.field_name, None), } @@ -384,6 +411,7 @@ class PluginContentPanel(Panel): self.method = method def render(self, context): + # Override the default render() method to simply embed rendered plugin content obj = context.get('object') return _get_registered_content(obj, self.method, context) @@ -414,14 +442,10 @@ class ContextTablePanel(ObjectPanel): return context.get(self.table) def get_context(self, context): - table = self._resolve_table(context) return { **super().get_context(context), - 'table': table, + 'table': self._resolve_table(context), } - def render(self, context): - table = self._resolve_table(context) - if table is None: - return '' - return super().render(context) + def should_render(self, context): + return context.get('table') is not None diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index d59c465d3..8f0a98b50 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -3,6 +3,7 @@ import re from collections import Counter from copy import deepcopy +from django.conf import settings from django.contrib import messages from django.contrib.contenttypes.fields import GenericForeignKey, GenericRel from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError @@ -25,7 +26,7 @@ from netbox.models.features import ChangeLoggingMixin from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename from utilities.error_handlers import handle_protectederror from utilities.exceptions import AbortRequest, PermissionsViolation -from utilities.export import TableExport +from utilities.export import TableExport, stream_table_csv_response from utilities.forms import BulkDeleteForm, BulkRenameForm, restrict_form_fields from utilities.forms.bulk_import import BulkImportForm from utilities.htmx import htmx_partial @@ -103,15 +104,23 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin): # those currently visible in the configured table view. table._apply_prefetching(columns=[c for c in all_columns if c not in exclude_columns]) + filename = filename or f'netbox_{self.queryset.model._meta.verbose_name_plural}.csv' + + if settings.STREAMING_EXPORTS: + return stream_table_csv_response( + table=table, + exclude_columns=exclude_columns, + filename=filename, + delimiter=delimiter, + ) + exporter = TableExport( export_format=TableExport.CSV, table=table, exclude_columns=exclude_columns, delimiter=delimiter, ) - return exporter.response( - filename=filename or f'netbox_{self.queryset.model._meta.verbose_name_plural}.csv' - ) + return exporter.response(filename=filename) def export_template(self, template, request): """ @@ -230,6 +239,7 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): form = None model_form = None pattern_target = '' + htmx_template_name = 'htmx/bulk_add_form.html' def get_required_permission(self): return get_permission_for_model(self.queryset.model, 'add') @@ -247,6 +257,7 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): # Validate each new object independently. if model_form.is_valid(): + model_form.instance._changelog_message = model_form.cleaned_data.get('changelog_message', '') obj = model_form.save() new_objects.append(obj) else: @@ -259,6 +270,19 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): return new_objects + def _get_context(self, request, form, model_form): + model = self.queryset.model + return { + 'object': None, + 'obj_type': model._meta.verbose_name, + 'obj_type_plural': model._meta.verbose_name_plural, + 'form': form, + 'model_form': model_form, + 'return_url': self.get_return_url(request), + 'add_url': get_action_url(model, 'add'), + **self.get_extra_context(request), + } + # # Request handlers # @@ -273,13 +297,13 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): form = self.form() model_form = self.model_form(initial=initial) - return render(request, self.template_name, { - 'obj_type': self.model_form._meta.model._meta.verbose_name, - 'form': form, - 'model_form': model_form, - 'return_url': self.get_return_url(request), - **self.get_extra_context(request), - }) + # HTMX partial: only re-render the model form fields + if htmx_partial(request): + return render(request, self.htmx_template_name, { + 'model_form': model_form, + }) + + return render(request, self.template_name, self._get_context(request, form, model_form)) def post(self, request): logger = logging.getLogger('netbox.views.BulkCreateView') @@ -287,6 +311,12 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): form = self.form(request.POST) model_form = self.model_form(request.POST) + # HTMX partial: only re-render the model form fields + if htmx_partial(request): + return render(request, self.htmx_template_name, { + 'model_form': model_form, + }) + if form.is_valid(): logger.debug("Form validation was successful") @@ -318,13 +348,7 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): else: logger.debug("Form validation failed") - return render(request, self.template_name, { - 'form': form, - 'model_form': model_form, - 'obj_type': model._meta.verbose_name, - 'return_url': self.get_return_url(request), - **self.get_extra_context(request), - }) + return render(request, self.template_name, self._get_context(request, form, model_form)) class BulkImportView(GetReturnURLMixin, BaseMultiObjectView): @@ -1128,8 +1152,19 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView): if form.is_valid(): logger.debug("Form validation was successful") + # If indicated, defer this request to a background job & redirect the user + if form.cleaned_data['background_job']: + job_name = _('Bulk add {count} {object_type}').format( + count=len(form.cleaned_data['pk']), + object_type=self.queryset.model._meta.verbose_name_plural, + ) + if process_request_as_job(self.__class__, request, name=job_name): + return redirect(self.get_return_url(request)) + new_components = [] data = deepcopy(form.cleaned_data) + changelog_message = data.pop('changelog_message', '') + data.pop('background_job', None) replication_data = { field: data.pop(field) for field in form.replication_fields } @@ -1151,13 +1186,18 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView): component_form = self.model_form(component_data) if component_form.is_valid(): + if changelog_message: + component_form.instance._changelog_message = changelog_message instance = component_form.save() logger.debug(f"Created {instance} on {instance.parent_object}") new_components.append(instance) else: for field, errors in component_form.errors.as_data().items(): for e in errors: - form.add_error(field, '{}: {}'.format(obj, ', '.join(e))) + err_msg = '{}: {}'.format(obj, ', '.join(e)) + form.add_error(field, err_msg) + if is_background_request(request): + request.job.logger.error(err_msg) # Enforce object-level permissions component_ids = [obj.pk for obj in new_components] @@ -1166,20 +1206,32 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView): except IntegrityError: clear_events.send(sender=self) + if is_background_request(request): + request.job.logger.error(_("An integrity error occurred while creating components")) + raise JobFailed except (AbortRequest, PermissionsViolation) as e: logger.debug(e.message) form.add_error(None, e.message) clear_events.send(sender=self) + if is_background_request(request): + request.job.logger.error(e.message) + raise JobFailed if not form.errors: - msg = "Added {} {} to {} {}.".format( - len(new_components), - model_name, - len(form.cleaned_data['pk']), - parent_model_name + msg = _("Added {count} {component} to {parent_count} {parent}.").format( + count=len(new_components), + component=model_name, + parent_count=len(form.cleaned_data['pk']), + parent=parent_model_name, ) logger.info(msg) + + # Handle background job + if is_background_request(request): + request.job.logger.info(msg) + return None + messages.success(request, msg) return redirect(self.get_return_url(request)) diff --git a/netbox/netbox/views/generic/feature_views.py b/netbox/netbox/views/generic/feature_views.py index 8e07f8ad4..a12e357cf 100644 --- a/netbox/netbox/views/generic/feature_views.py +++ b/netbox/netbox/views/generic/feature_views.py @@ -17,7 +17,7 @@ from tenancy.forms import ContactAssignmentFilterForm from tenancy.models import ContactAssignment from tenancy.tables import ContactAssignmentTable from utilities.permissions import get_permission_for_model -from utilities.views import ConditionalLoginRequiredMixin, GetReturnURLMixin, ViewTab +from utilities.views import ConditionalLoginRequiredMixin, GetReturnURLMixin, ViewTab, get_default_template from .base import BaseMultiObjectView from .object_views import ObjectChildrenView @@ -72,15 +72,10 @@ class ObjectChangeLogView(ConditionalLoginRequiredMixin, View): ) objectchanges_table.configure(request) - # Default to using "/.html" as the template, if it exists. Otherwise, - # fall back to using base.html. - if self.base_template is None: - self.base_template = f"{model._meta.app_label}/{model._meta.model_name}.html" - return render(request, 'extras/object_changelog.html', { 'object': obj, 'table': objectchanges_table, - 'base_template': self.base_template, + 'base_template': self.base_template or get_default_template(model), 'tab': self.tab, }) @@ -107,15 +102,10 @@ class ObjectImageAttachmentsView(ConditionalLoginRequiredMixin, View): object_id=obj.pk, ) - # Default to using "/.html" as the template, if it exists. Otherwise, - # fall back to using base.html. - if self.base_template is None: - self.base_template = f"{model._meta.app_label}/{model._meta.model_name}.html" - return render(request, 'extras/object_imageattachments.html', { 'object': obj, 'image_attachments': image_attachments, - 'base_template': self.base_template, + 'base_template': self.base_template or get_default_template(model), 'tab': self.tab, }) @@ -167,16 +157,11 @@ class ObjectJournalView(ConditionalLoginRequiredMixin, View): else: form = None - # Default to using "/.html" as the template, if it exists. Otherwise, - # fall back to using base.html. - if self.base_template is None: - self.base_template = f"{model._meta.app_label}/{model._meta.model_name}.html" - return render(request, 'extras/object_journal.html', { 'object': obj, 'form': form, 'table': journalentry_table, - 'base_template': self.base_template, + 'base_template': self.base_template or get_default_template(model), 'tab': self.tab, }) @@ -222,15 +207,10 @@ class ObjectJobsView(ConditionalLoginRequiredMixin, View): jobs_table = JobTable(data=jobs, orderable=False) jobs_table.configure(request) - # Default to using "/.html" as the template, if it exists. Otherwise, - # fall back to using base.html. - if self.base_template is None: - self.base_template = f"{model._meta.app_label}/{model._meta.model_name}.html" - return render(request, 'core/object_jobs.html', { 'object': obj, 'table': jobs_table, - 'base_template': self.base_template, + 'base_template': self.base_template or get_default_template(model), 'tab': self.tab, }) diff --git a/netbox/netbox/views/generic/mixins.py b/netbox/netbox/views/generic/mixins.py index 6e40e4175..2945e0e33 100644 --- a/netbox/netbox/views/generic/mixins.py +++ b/netbox/netbox/views/generic/mixins.py @@ -9,7 +9,7 @@ __all__ = ( 'TableMixin', ) -# TODO: Remove in NetBox v4.5 +# TODO: Remove in NetBox v4.7 LEGACY_ACTIONS = { 'add': object_actions.AddObject, 'edit': object_actions.EditObject, @@ -33,7 +33,7 @@ class ActionsMixin: """ actions = tuple() - # TODO: Remove in NetBox v4.5 + # TODO: Remove in NetBox v4.7 def _convert_legacy_actions(self): """ Convert a legacy dictionary mapping action name to required permissions to a list of ObjectAction subclasses. @@ -41,6 +41,14 @@ class ActionsMixin: if type(self.actions) is not dict: return + import warnings + warnings.warn( + f"{self.__class__.__name__}.actions is defined as a dictionary, which is deprecated and will be removed " + "in NetBox v4.7. Define actions as a list of ObjectAction subclasses instead.", + DeprecationWarning, + stacklevel=2, + ) + actions = [] for name in self.actions.keys(): try: @@ -56,7 +64,7 @@ class ActionsMixin: """ model = model or self.queryset.model - # TODO: Remove in NetBox v4.5 + # TODO: Remove in NetBox v4.7 # Handle legacy action sets self._convert_legacy_actions() diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index c6630d341..0d4db8539 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -22,7 +22,7 @@ from utilities.permissions import get_permission_for_model from utilities.querydict import normalize_querydict, prepare_cloned_fields from utilities.request import safe_for_redirect from utilities.tables import get_table_configs -from utilities.views import GetReturnURLMixin, get_action_url +from utilities.views import GetReturnURLMixin, get_action_url, get_default_template from .base import BaseObjectView from .mixins import ActionsMixin, TableMixin @@ -163,7 +163,7 @@ class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin): 'object': instance, 'model': self.child_model, 'child_model': self.child_model, - 'base_template': f'{instance._meta.app_label}/{instance._meta.model_name}.html', + 'base_template': get_default_template(instance), 'table': table, 'table_config': f'{table.name}_config', 'table_configs': get_table_configs(table, request.user), diff --git a/netbox/project-static/dist/netbox.css b/netbox/project-static/dist/netbox.css index 229225a75..d4019c186 100644 --- a/netbox/project-static/dist/netbox.css +++ b/netbox/project-static/dist/netbox.css @@ -1 +1 @@ -@charset "UTF-8";:root,[data-bs-theme=light]{--tblr-black: #000000;--tblr-white: #ffffff;--tblr-gray: #4b5563;--tblr-gray-dark: #1f2937;--tblr-gray-100: #f3f4f6;--tblr-gray-200: #e5e7eb;--tblr-gray-300: #d1d5db;--tblr-gray-400: #9ca3af;--tblr-gray-500: #6b7280;--tblr-gray-600: #4b5563;--tblr-gray-700: #374151;--tblr-gray-800: #1f2937;--tblr-gray-900: #111827;--tblr-primary: #00857D;--tblr-secondary: #6b7280;--tblr-success: #2fb344;--tblr-info: #4299e1;--tblr-warning: #f59f00;--tblr-danger: #d63939;--tblr-light: #f9fafb;--tblr-dark: #1f2937;--tblr-muted: #6b7280;--tblr-blue: #066fd1;--tblr-azure: #4299e1;--tblr-indigo: #4263eb;--tblr-purple: #ae3ec9;--tblr-pink: #d6336c;--tblr-red: #d63939;--tblr-orange: #f76707;--tblr-yellow: #f59f00;--tblr-lime: #74b816;--tblr-green: #2fb344;--tblr-teal: #0ca678;--tblr-cyan: #17a2b8;--tblr-primary-rgb: 0, 133, 125;--tblr-secondary-rgb: 107, 114, 128;--tblr-success-rgb: 47, 179, 68;--tblr-info-rgb: 66, 153, 225;--tblr-warning-rgb: 245, 159, 0;--tblr-danger-rgb: 214, 57, 57;--tblr-light-rgb: 249, 250, 251;--tblr-dark-rgb: 31, 41, 55;--tblr-muted-rgb: 107, 114, 128;--tblr-blue-rgb: 6, 111, 209;--tblr-azure-rgb: 66, 153, 225;--tblr-indigo-rgb: 66, 99, 235;--tblr-purple-rgb: 174, 62, 201;--tblr-pink-rgb: 214, 51, 108;--tblr-red-rgb: 214, 57, 57;--tblr-orange-rgb: 247, 103, 7;--tblr-yellow-rgb: 245, 159, 0;--tblr-lime-rgb: 116, 184, 22;--tblr-green-rgb: 47, 179, 68;--tblr-teal-rgb: 12, 166, 120;--tblr-cyan-rgb: 23, 162, 184;--tblr-primary-text-emphasis: rgb(0, 53.2, 50);--tblr-secondary-text-emphasis: rgb(42.8, 45.6, 51.2);--tblr-success-text-emphasis: rgb(18.8, 71.6, 27.2);--tblr-info-text-emphasis: rgb(26.4, 61.2, 90);--tblr-warning-text-emphasis: rgb(98, 63.6, 0);--tblr-danger-text-emphasis: rgb(85.6, 22.8, 22.8);--tblr-light-text-emphasis: #374151;--tblr-dark-text-emphasis: #374151;--tblr-primary-bg-subtle: rgb(204, 230.6, 229);--tblr-secondary-bg-subtle: rgb(225.4, 226.8, 229.6);--tblr-success-bg-subtle: rgb(213.4, 239.8, 217.6);--tblr-info-bg-subtle: rgb(217.2, 234.6, 249);--tblr-warning-bg-subtle: rgb(253, 235.8, 204);--tblr-danger-bg-subtle: rgb(246.8, 215.4, 215.4);--tblr-light-bg-subtle: rgb(249, 249.5, 250.5);--tblr-dark-bg-subtle: #9ca3af;--tblr-primary-border-subtle: rgb(153, 206.2, 203);--tblr-secondary-border-subtle: rgb(195.8, 198.6, 204.2);--tblr-success-border-subtle: rgb(171.8, 224.6, 180.2);--tblr-info-border-subtle: rgb(179.4, 214.2, 243);--tblr-warning-border-subtle: rgb(251, 216.6, 153);--tblr-danger-border-subtle: rgb(238.6, 175.8, 175.8);--tblr-light-border-subtle: #e5e7eb;--tblr-dark-border-subtle: #6b7280;--tblr-white-rgb: 255, 255, 255;--tblr-black-rgb: 0, 0, 0;--tblr-font-sans-serif: "Inter", system-ui, sans-serif;--tblr-font-monospace: "Roboto Mono";--tblr-gradient: linear-gradient(180deg, rgba(255, 255, 255, .15), rgba(255, 255, 255, 0));--tblr-body-font-family: var(--tblr-font-sans-serif);--tblr-body-font-size: .875rem;--tblr-body-font-weight: 400;--tblr-body-line-height: 1.4285714286;--tblr-body-color: #1f2937;--tblr-body-color-rgb: 31, 41, 55;--tblr-body-bg: #f9fafb;--tblr-body-bg-rgb: 249, 250, 251;--tblr-emphasis-color: #374151;--tblr-emphasis-color-rgb: 55, 65, 81;--tblr-secondary-color: rgba(31, 41, 55, .75);--tblr-secondary-color-rgb: 31, 41, 55;--tblr-secondary-bg: #e5e7eb;--tblr-secondary-bg-rgb: 229, 231, 235;--tblr-tertiary-color: rgba(31, 41, 55, .5);--tblr-tertiary-color-rgb: 31, 41, 55;--tblr-tertiary-bg: #f3f4f6;--tblr-tertiary-bg-rgb: 243, 244, 246;--tblr-heading-color: inherit;--tblr-link-color: #00857D;--tblr-link-color-rgb: 0, 133, 125;--tblr-link-decoration: none;--tblr-link-hover-color: rgb(0, 106.4, 100);--tblr-link-hover-color-rgb: 0, 106, 100;--tblr-link-hover-decoration: underline;--tblr-code-color: light-dark(var(--tblr-gray-600), var(--tblr-gray-400));--tblr-highlight-color: #1f2937;--tblr-highlight-bg: rgb(253, 235.8, 204);--tblr-border-width: 1px;--tblr-border-style: solid;--tblr-border-color: #e5e7eb;--tblr-border-color-translucent: rgba(4, 32, 69, .1);--tblr-border-radius: 6px;--tblr-border-radius-sm: 4px;--tblr-border-radius-lg: 8px;--tblr-border-radius-xl: 1rem;--tblr-border-radius-xxl: 2rem;--tblr-border-radius-2xl: var(--tblr-border-radius-xxl);--tblr-border-radius-pill: 100rem;--tblr-box-shadow: rgba(var(--tblr-body-color-rgb), .04) 0 2px 4px 0;--tblr-box-shadow-sm: 0 .125rem .25rem rgba(0, 0, 0, .075);--tblr-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, .175);--tblr-box-shadow-inset: 0 0 transparent;--tblr-focus-ring-width: .25rem;--tblr-focus-ring-opacity: .25;--tblr-focus-ring-color: rgba(var(--tblr-primary-rgb), .25);--tblr-form-valid-color: #2fb344;--tblr-form-valid-border-color: #2fb344;--tblr-form-invalid-color: #d63939;--tblr-form-invalid-border-color: #d63939}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{color-scheme:dark;--tblr-body-color: #e5e7eb;--tblr-body-color-rgb: 229, 231, 235;--tblr-body-bg: #111827;--tblr-body-bg-rgb: 17, 24, 39;--tblr-emphasis-color: #ffffff;--tblr-emphasis-color-rgb: 255, 255, 255;--tblr-secondary-color: rgba(229, 231, 235, .75);--tblr-secondary-color-rgb: 229, 231, 235;--tblr-secondary-bg: #1f2937;--tblr-secondary-bg-rgb: 31, 41, 55;--tblr-tertiary-color: rgba(229, 231, 235, .5);--tblr-tertiary-color-rgb: 229, 231, 235;--tblr-tertiary-bg: rgb(24, 32.5, 47);--tblr-tertiary-bg-rgb: 24, 33, 47;--tblr-primary-text-emphasis: rgb(102, 181.8, 177);--tblr-secondary-text-emphasis: rgb(166.2, 170.4, 178.8);--tblr-success-text-emphasis: rgb(130.2, 209.4, 142.8);--tblr-info-text-emphasis: rgb(141.6, 193.8, 237);--tblr-warning-text-emphasis: rgb(249, 197.4, 102);--tblr-danger-text-emphasis: rgb(230.4, 136.2, 136.2);--tblr-light-text-emphasis: #f3f4f6;--tblr-dark-text-emphasis: #d1d5db;--tblr-primary-bg-subtle: rgb(0, 26.6, 25);--tblr-secondary-bg-subtle: rgb(21.4, 22.8, 25.6);--tblr-success-bg-subtle: rgb(9.4, 35.8, 13.6);--tblr-info-bg-subtle: rgb(13.2, 30.6, 45);--tblr-warning-bg-subtle: rgb(49, 31.8, 0);--tblr-danger-bg-subtle: rgb(42.8, 11.4, 11.4);--tblr-light-bg-subtle: #1f2937;--tblr-dark-bg-subtle: rgb(15.5, 20.5, 27.5);--tblr-primary-border-subtle: rgb(0, 79.8, 75);--tblr-secondary-border-subtle: rgb(64.2, 68.4, 76.8);--tblr-success-border-subtle: rgb(28.2, 107.4, 40.8);--tblr-info-border-subtle: rgb(39.6, 91.8, 135);--tblr-warning-border-subtle: rgb(147, 95.4, 0);--tblr-danger-border-subtle: rgb(128.4, 34.2, 34.2);--tblr-light-border-subtle: #374151;--tblr-dark-border-subtle: #1f2937;--tblr-heading-color: inherit;--tblr-link-color: rgb(102, 181.8, 177);--tblr-link-hover-color: rgb(132.6, 196.44, 192.6);--tblr-link-color-rgb: 102, 182, 177;--tblr-link-hover-color-rgb: 133, 196, 193;--tblr-code-color: var(--tblr-gray-300);--tblr-highlight-color: #e5e7eb;--tblr-highlight-bg: rgb(98, 63.6, 0);--tblr-border-color: rgb(45.7069767442, 60.4511627907, 81.0930232558);--tblr-border-color-translucent: rgba(72, 110, 149, .14);--tblr-form-valid-color: rgb(130.2, 209.4, 142.8);--tblr-form-valid-border-color: rgb(130.2, 209.4, 142.8);--tblr-form-invalid-color: rgb(230.4, 136.2, 136.2);--tblr-form-invalid-border-color: rgb(230.4, 136.2, 136.2)}*,*:before,*:after{box-sizing:border-box}@media(prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--tblr-body-font-family);font-size:var(--tblr-body-font-size);font-weight:var(--tblr-body-font-weight);line-height:var(--tblr-body-line-height);color:var(--tblr-body-color);text-align:var(--tblr-body-text-align);background-color:var(--tblr-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}hr,.hr{margin:2rem 0;color:inherit;border:0;border-top:var(--tblr-border-width) solid;opacity:.16}h6,.h6,h5,.h5,h4,.h4,h3,.field-group h2,.field-group .h2,.h3,h2,.h2,h1,.h1{margin-top:0;margin-bottom:var(--tblr-spacer);font-weight:var(--tblr-font-weight-bold);line-height:1.2;color:var(--tblr-heading-color)}h1,.h1{font-size:1.5rem}h2,.h2{font-size:1.25rem}h3,.field-group h2,.field-group .h2,.h3{font-size:1rem}h4,.h4{font-size:.875rem}h5,.h5{font-size:.75rem}h6,.h6{font-size:.625rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{text-decoration:underline dotted;cursor:help;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:600}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small,.small{font-size:.875em}mark,.mark{padding:.1875em;color:var(--tblr-highlight-color);background-color:var(--tblr-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--tblr-link-color-rgb),var(--tblr-link-opacity, 1));text-decoration:none}a:hover{--tblr-link-color-rgb: var(--tblr-link-hover-color-rgb);text-decoration:underline}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}pre,code,kbd,samp{font-family:var(--tblr-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.85714285em;color:var(--tblr-light)}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.85714285em;color:var(--tblr-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.25rem .5rem;font-size:var(--tblr-font-size-h5);color:var(--tblr-text-secondary-dark);background-color:var(--tblr-code-bg);border-radius:4px}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--tblr-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}thead,tbody,tfoot,tr,td,th{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button:not(:disabled),[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;line-height:inherit;font-size:1.5rem}legend+*{clear:left}::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-text,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button{cursor:pointer;filter:grayscale(1)}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:.875rem;font-weight:var(--tblr-font-weight-normal)}.display-1{font-weight:300;line-height:1.2;font-size:5rem}.display-2{font-weight:300;line-height:1.2;font-size:4.5rem}.display-3{font-weight:300;line-height:1.2;font-size:4rem}.display-4{font-weight:300;line-height:1.2;font-size:3.5rem}.display-5{font-weight:300;line-height:1.2;font-size:3rem}.display-6{font-weight:300;line-height:1.2;font-size:2rem}.list-unstyled,.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:.875rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#4b5563}.blockquote-footer:before{content:"\2014\a0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--tblr-body-bg);border:var(--tblr-border-width) solid var(--tblr-border-color);border-radius:var(--tblr-border-radius);box-shadow:var(--tblr-box-shadow-sm);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--tblr-secondary-color)}.container,.container-fluid,.container-xxl,.container-xl,.container-lg,.container-md,.container-sm{--tblr-gutter-x: calc(var(--tblr-page-padding) * 2);--tblr-gutter-y: 0;width:100%;padding-right:calc(var(--tblr-gutter-x) * .5);padding-left:calc(var(--tblr-gutter-x) * .5);margin-right:auto;margin-left:auto}@media(min-width:576px){.container-sm,.container{max-width:540px}}@media(min-width:768px){.container-md,.container-sm,.container{max-width:720px}}@media(min-width:992px){.container-lg,.container-md,.container-sm,.container{max-width:960px}}@media(min-width:1200px){.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1140px}}@media(min-width:1400px){.container-xxl,.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1320px}}:root{--tblr-breakpoint-xs: 0;--tblr-breakpoint-sm: 576px;--tblr-breakpoint-md: 768px;--tblr-breakpoint-lg: 992px;--tblr-breakpoint-xl: 1200px;--tblr-breakpoint-xxl: 1400px}.row{--tblr-gutter-x: var(--tblr-page-padding);--tblr-gutter-y: 0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--tblr-gutter-y));margin-right:calc(-.5 * var(--tblr-gutter-x));margin-left:calc(-.5 * var(--tblr-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--tblr-gutter-x) * .5);padding-left:calc(var(--tblr-gutter-x) * .5);margin-top:var(--tblr-gutter-y)}.grid{display:grid;grid-template-rows:repeat(var(--tblr-rows, 1),1fr);grid-template-columns:repeat(var(--tblr-columns, 12),1fr);gap:var(--tblr-gap, var(--tblr-page-padding))}.grid .g-col-1{grid-column:auto/span 1}.grid .g-col-2{grid-column:auto/span 2}.grid .g-col-3{grid-column:auto/span 3}.grid .g-col-4{grid-column:auto/span 4}.grid .g-col-5{grid-column:auto/span 5}.grid .g-col-6{grid-column:auto/span 6}.grid .g-col-7{grid-column:auto/span 7}.grid .g-col-8{grid-column:auto/span 8}.grid .g-col-9{grid-column:auto/span 9}.grid .g-col-10{grid-column:auto/span 10}.grid .g-col-11{grid-column:auto/span 11}.grid .g-col-12{grid-column:auto/span 12}.grid .g-start-1{grid-column-start:1}.grid .g-start-2{grid-column-start:2}.grid .g-start-3{grid-column-start:3}.grid .g-start-4{grid-column-start:4}.grid .g-start-5{grid-column-start:5}.grid .g-start-6{grid-column-start:6}.grid .g-start-7{grid-column-start:7}.grid .g-start-8{grid-column-start:8}.grid .g-start-9{grid-column-start:9}.grid .g-start-10{grid-column-start:10}.grid .g-start-11{grid-column-start:11}@media(min-width:576px){.grid .g-col-sm-1{grid-column:auto/span 1}.grid .g-col-sm-2{grid-column:auto/span 2}.grid .g-col-sm-3{grid-column:auto/span 3}.grid .g-col-sm-4{grid-column:auto/span 4}.grid .g-col-sm-5{grid-column:auto/span 5}.grid .g-col-sm-6{grid-column:auto/span 6}.grid .g-col-sm-7{grid-column:auto/span 7}.grid .g-col-sm-8{grid-column:auto/span 8}.grid .g-col-sm-9{grid-column:auto/span 9}.grid .g-col-sm-10{grid-column:auto/span 10}.grid .g-col-sm-11{grid-column:auto/span 11}.grid .g-col-sm-12{grid-column:auto/span 12}.grid .g-start-sm-1{grid-column-start:1}.grid .g-start-sm-2{grid-column-start:2}.grid .g-start-sm-3{grid-column-start:3}.grid .g-start-sm-4{grid-column-start:4}.grid .g-start-sm-5{grid-column-start:5}.grid .g-start-sm-6{grid-column-start:6}.grid .g-start-sm-7{grid-column-start:7}.grid .g-start-sm-8{grid-column-start:8}.grid .g-start-sm-9{grid-column-start:9}.grid .g-start-sm-10{grid-column-start:10}.grid .g-start-sm-11{grid-column-start:11}}@media(min-width:768px){.grid .g-col-md-1{grid-column:auto/span 1}.grid .g-col-md-2{grid-column:auto/span 2}.grid .g-col-md-3{grid-column:auto/span 3}.grid .g-col-md-4{grid-column:auto/span 4}.grid .g-col-md-5{grid-column:auto/span 5}.grid .g-col-md-6{grid-column:auto/span 6}.grid .g-col-md-7{grid-column:auto/span 7}.grid .g-col-md-8{grid-column:auto/span 8}.grid .g-col-md-9{grid-column:auto/span 9}.grid .g-col-md-10{grid-column:auto/span 10}.grid .g-col-md-11{grid-column:auto/span 11}.grid .g-col-md-12{grid-column:auto/span 12}.grid .g-start-md-1{grid-column-start:1}.grid .g-start-md-2{grid-column-start:2}.grid .g-start-md-3{grid-column-start:3}.grid .g-start-md-4{grid-column-start:4}.grid .g-start-md-5{grid-column-start:5}.grid .g-start-md-6{grid-column-start:6}.grid .g-start-md-7{grid-column-start:7}.grid .g-start-md-8{grid-column-start:8}.grid .g-start-md-9{grid-column-start:9}.grid .g-start-md-10{grid-column-start:10}.grid .g-start-md-11{grid-column-start:11}}@media(min-width:992px){.grid .g-col-lg-1{grid-column:auto/span 1}.grid .g-col-lg-2{grid-column:auto/span 2}.grid .g-col-lg-3{grid-column:auto/span 3}.grid .g-col-lg-4{grid-column:auto/span 4}.grid .g-col-lg-5{grid-column:auto/span 5}.grid .g-col-lg-6{grid-column:auto/span 6}.grid .g-col-lg-7{grid-column:auto/span 7}.grid .g-col-lg-8{grid-column:auto/span 8}.grid .g-col-lg-9{grid-column:auto/span 9}.grid .g-col-lg-10{grid-column:auto/span 10}.grid .g-col-lg-11{grid-column:auto/span 11}.grid .g-col-lg-12{grid-column:auto/span 12}.grid .g-start-lg-1{grid-column-start:1}.grid .g-start-lg-2{grid-column-start:2}.grid .g-start-lg-3{grid-column-start:3}.grid .g-start-lg-4{grid-column-start:4}.grid .g-start-lg-5{grid-column-start:5}.grid .g-start-lg-6{grid-column-start:6}.grid .g-start-lg-7{grid-column-start:7}.grid .g-start-lg-8{grid-column-start:8}.grid .g-start-lg-9{grid-column-start:9}.grid .g-start-lg-10{grid-column-start:10}.grid .g-start-lg-11{grid-column-start:11}}@media(min-width:1200px){.grid .g-col-xl-1{grid-column:auto/span 1}.grid .g-col-xl-2{grid-column:auto/span 2}.grid .g-col-xl-3{grid-column:auto/span 3}.grid .g-col-xl-4{grid-column:auto/span 4}.grid .g-col-xl-5{grid-column:auto/span 5}.grid .g-col-xl-6{grid-column:auto/span 6}.grid .g-col-xl-7{grid-column:auto/span 7}.grid .g-col-xl-8{grid-column:auto/span 8}.grid .g-col-xl-9{grid-column:auto/span 9}.grid .g-col-xl-10{grid-column:auto/span 10}.grid .g-col-xl-11{grid-column:auto/span 11}.grid .g-col-xl-12{grid-column:auto/span 12}.grid .g-start-xl-1{grid-column-start:1}.grid .g-start-xl-2{grid-column-start:2}.grid .g-start-xl-3{grid-column-start:3}.grid .g-start-xl-4{grid-column-start:4}.grid .g-start-xl-5{grid-column-start:5}.grid .g-start-xl-6{grid-column-start:6}.grid .g-start-xl-7{grid-column-start:7}.grid .g-start-xl-8{grid-column-start:8}.grid .g-start-xl-9{grid-column-start:9}.grid .g-start-xl-10{grid-column-start:10}.grid .g-start-xl-11{grid-column-start:11}}@media(min-width:1400px){.grid .g-col-xxl-1{grid-column:auto/span 1}.grid .g-col-xxl-2{grid-column:auto/span 2}.grid .g-col-xxl-3{grid-column:auto/span 3}.grid .g-col-xxl-4{grid-column:auto/span 4}.grid .g-col-xxl-5{grid-column:auto/span 5}.grid .g-col-xxl-6{grid-column:auto/span 6}.grid .g-col-xxl-7{grid-column:auto/span 7}.grid .g-col-xxl-8{grid-column:auto/span 8}.grid .g-col-xxl-9{grid-column:auto/span 9}.grid .g-col-xxl-10{grid-column:auto/span 10}.grid .g-col-xxl-11{grid-column:auto/span 11}.grid .g-col-xxl-12{grid-column:auto/span 12}.grid .g-start-xxl-1{grid-column-start:1}.grid .g-start-xxl-2{grid-column-start:2}.grid .g-start-xxl-3{grid-column-start:3}.grid .g-start-xxl-4{grid-column-start:4}.grid .g-start-xxl-5{grid-column-start:5}.grid .g-start-xxl-6{grid-column-start:6}.grid .g-start-xxl-7{grid-column-start:7}.grid .g-start-xxl-8{grid-column-start:8}.grid .g-start-xxl-9{grid-column-start:9}.grid .g-start-xxl-10{grid-column-start:10}.grid .g-start-xxl-11{grid-column-start:11}}.col{flex:1 0 0}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--tblr-gutter-x: 0}.g-0,.gy-0{--tblr-gutter-y: 0}.g-1,.gx-1{--tblr-gutter-x: .25rem}.g-1,.gy-1{--tblr-gutter-y: .25rem}.g-2,.gx-2{--tblr-gutter-x: .5rem}.g-2,.gy-2{--tblr-gutter-y: .5rem}.g-3,.gx-3{--tblr-gutter-x: 1rem}.g-3,.gy-3{--tblr-gutter-y: 1rem}.g-4,.gx-4{--tblr-gutter-x: 1.5rem}.g-4,.gy-4{--tblr-gutter-y: 1.5rem}.g-5,.gx-5{--tblr-gutter-x: 2rem}.g-5,.gy-5{--tblr-gutter-y: 2rem}.g-6,.gx-6{--tblr-gutter-x: 2.5rem}.g-6,.gy-6{--tblr-gutter-y: 2.5rem}@media(min-width:576px){.col-sm{flex:1 0 0}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--tblr-gutter-x: 0}.g-sm-0,.gy-sm-0{--tblr-gutter-y: 0}.g-sm-1,.gx-sm-1{--tblr-gutter-x: .25rem}.g-sm-1,.gy-sm-1{--tblr-gutter-y: .25rem}.g-sm-2,.gx-sm-2{--tblr-gutter-x: .5rem}.g-sm-2,.gy-sm-2{--tblr-gutter-y: .5rem}.g-sm-3,.gx-sm-3{--tblr-gutter-x: 1rem}.g-sm-3,.gy-sm-3{--tblr-gutter-y: 1rem}.g-sm-4,.gx-sm-4{--tblr-gutter-x: 1.5rem}.g-sm-4,.gy-sm-4{--tblr-gutter-y: 1.5rem}.g-sm-5,.gx-sm-5{--tblr-gutter-x: 2rem}.g-sm-5,.gy-sm-5{--tblr-gutter-y: 2rem}.g-sm-6,.gx-sm-6{--tblr-gutter-x: 2.5rem}.g-sm-6,.gy-sm-6{--tblr-gutter-y: 2.5rem}}@media(min-width:768px){.col-md{flex:1 0 0}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--tblr-gutter-x: 0}.g-md-0,.gy-md-0{--tblr-gutter-y: 0}.g-md-1,.gx-md-1{--tblr-gutter-x: .25rem}.g-md-1,.gy-md-1{--tblr-gutter-y: .25rem}.g-md-2,.gx-md-2{--tblr-gutter-x: .5rem}.g-md-2,.gy-md-2{--tblr-gutter-y: .5rem}.g-md-3,.gx-md-3{--tblr-gutter-x: 1rem}.g-md-3,.gy-md-3{--tblr-gutter-y: 1rem}.g-md-4,.gx-md-4{--tblr-gutter-x: 1.5rem}.g-md-4,.gy-md-4{--tblr-gutter-y: 1.5rem}.g-md-5,.gx-md-5{--tblr-gutter-x: 2rem}.g-md-5,.gy-md-5{--tblr-gutter-y: 2rem}.g-md-6,.gx-md-6{--tblr-gutter-x: 2.5rem}.g-md-6,.gy-md-6{--tblr-gutter-y: 2.5rem}}@media(min-width:992px){.col-lg{flex:1 0 0}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--tblr-gutter-x: 0}.g-lg-0,.gy-lg-0{--tblr-gutter-y: 0}.g-lg-1,.gx-lg-1{--tblr-gutter-x: .25rem}.g-lg-1,.gy-lg-1{--tblr-gutter-y: .25rem}.g-lg-2,.gx-lg-2{--tblr-gutter-x: .5rem}.g-lg-2,.gy-lg-2{--tblr-gutter-y: .5rem}.g-lg-3,.gx-lg-3{--tblr-gutter-x: 1rem}.g-lg-3,.gy-lg-3{--tblr-gutter-y: 1rem}.g-lg-4,.gx-lg-4{--tblr-gutter-x: 1.5rem}.g-lg-4,.gy-lg-4{--tblr-gutter-y: 1.5rem}.g-lg-5,.gx-lg-5{--tblr-gutter-x: 2rem}.g-lg-5,.gy-lg-5{--tblr-gutter-y: 2rem}.g-lg-6,.gx-lg-6{--tblr-gutter-x: 2.5rem}.g-lg-6,.gy-lg-6{--tblr-gutter-y: 2.5rem}}@media(min-width:1200px){.col-xl{flex:1 0 0}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--tblr-gutter-x: 0}.g-xl-0,.gy-xl-0{--tblr-gutter-y: 0}.g-xl-1,.gx-xl-1{--tblr-gutter-x: .25rem}.g-xl-1,.gy-xl-1{--tblr-gutter-y: .25rem}.g-xl-2,.gx-xl-2{--tblr-gutter-x: .5rem}.g-xl-2,.gy-xl-2{--tblr-gutter-y: .5rem}.g-xl-3,.gx-xl-3{--tblr-gutter-x: 1rem}.g-xl-3,.gy-xl-3{--tblr-gutter-y: 1rem}.g-xl-4,.gx-xl-4{--tblr-gutter-x: 1.5rem}.g-xl-4,.gy-xl-4{--tblr-gutter-y: 1.5rem}.g-xl-5,.gx-xl-5{--tblr-gutter-x: 2rem}.g-xl-5,.gy-xl-5{--tblr-gutter-y: 2rem}.g-xl-6,.gx-xl-6{--tblr-gutter-x: 2.5rem}.g-xl-6,.gy-xl-6{--tblr-gutter-y: 2.5rem}}@media(min-width:1400px){.col-xxl{flex:1 0 0}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--tblr-gutter-x: 0}.g-xxl-0,.gy-xxl-0{--tblr-gutter-y: 0}.g-xxl-1,.gx-xxl-1{--tblr-gutter-x: .25rem}.g-xxl-1,.gy-xxl-1{--tblr-gutter-y: .25rem}.g-xxl-2,.gx-xxl-2{--tblr-gutter-x: .5rem}.g-xxl-2,.gy-xxl-2{--tblr-gutter-y: .5rem}.g-xxl-3,.gx-xxl-3{--tblr-gutter-x: 1rem}.g-xxl-3,.gy-xxl-3{--tblr-gutter-y: 1rem}.g-xxl-4,.gx-xxl-4{--tblr-gutter-x: 1.5rem}.g-xxl-4,.gy-xxl-4{--tblr-gutter-y: 1.5rem}.g-xxl-5,.gx-xxl-5{--tblr-gutter-x: 2rem}.g-xxl-5,.gy-xxl-5{--tblr-gutter-y: 2rem}.g-xxl-6,.gx-xxl-6{--tblr-gutter-x: 2.5rem}.g-xxl-6,.gy-xxl-6{--tblr-gutter-y: 2.5rem}}.table,.markdown>table{--tblr-table-color-type: initial;--tblr-table-bg-type: initial;--tblr-table-color-state: initial;--tblr-table-bg-state: initial;--tblr-table-color: inherit;--tblr-table-bg: transparent;--tblr-table-border-color: var(--tblr-border-color-translucent);--tblr-table-accent-bg: transparent;--tblr-table-striped-color: inherit;--tblr-table-striped-bg: var(--tblr-bg-surface-tertiary);--tblr-table-active-color: inherit;--tblr-table-active-bg: var(--tblr-active-bg);--tblr-table-hover-color: inherit;--tblr-table-hover-bg: rgba(var(--tblr-emphasis-color-rgb), .075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--tblr-table-border-color)}.table>:not(caption)>*>*,.markdown>table>:not(caption)>*>*{padding:.5rem;color:var(--tblr-table-color-state, var(--tblr-table-color-type, var(--tblr-table-color)));background-color:var(--tblr-table-bg);border-bottom-width:var(--tblr-border-width);box-shadow:inset 0 0 0 9999px var(--tblr-table-bg-state, var(--tblr-table-bg-type, var(--tblr-table-accent-bg)))}.table>tbody,.markdown>table>tbody{vertical-align:inherit}.table>thead,.markdown>table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--tblr-border-width) * 2) solid var(--tblr-border-color-translucent)}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*,.markdown>table>:not(caption)>*>*{padding:.25rem}.table-bordered>:not(caption)>*,.markdown>table>:not(caption)>*{border-width:var(--tblr-border-width) 0}.table-bordered>:not(caption)>*>*,.markdown>table>:not(caption)>*>*{border-width:0 var(--tblr-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(2n)>*{--tblr-table-color-type: var(--tblr-table-striped-color);--tblr-table-bg-type: var(--tblr-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--tblr-table-color-type: var(--tblr-table-striped-color);--tblr-table-bg-type: var(--tblr-table-striped-bg)}.table-active{--tblr-table-color-state: var(--tblr-table-active-color);--tblr-table-bg-state: var(--tblr-table-active-bg)}.table-hover>tbody>tr:hover>*{--tblr-table-color-state: var(--tblr-table-hover-color);--tblr-table-bg-state: var(--tblr-table-hover-bg)}.table-primary{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(204, 230.6, 229);--tblr-table-border-color: rgb(169.4, 192.68, 194.2);--tblr-table-striped-bg: rgb(195.35, 221.12, 220.3);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(186.7, 211.64, 211.6);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(191.025, 216.38, 215.95);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-secondary{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(225.4, 226.8, 229.6);--tblr-table-border-color: rgb(186.52, 189.64, 194.68);--tblr-table-striped-bg: rgb(215.68, 217.51, 220.87);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(205.96, 208.22, 212.14);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(210.82, 212.865, 216.505);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-success{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(213.4, 239.8, 217.6);--tblr-table-border-color: rgb(176.92, 200.04, 185.08);--tblr-table-striped-bg: rgb(204.28, 229.86, 209.47);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(195.16, 219.92, 201.34);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(199.72, 224.89, 205.405);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-info{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(217.2, 234.6, 249);--tblr-table-border-color: rgb(179.96, 195.88, 210.2);--tblr-table-striped-bg: rgb(207.89, 224.92, 239.3);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(198.58, 215.24, 229.6);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(203.235, 220.08, 234.45);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-warning{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(253, 235.8, 204);--tblr-table-border-color: rgb(208.6, 196.84, 174.2);--tblr-table-striped-bg: rgb(241.9, 226.06, 196.55);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(230.8, 216.32, 189.1);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(236.35, 221.19, 192.825);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-danger{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(246.8, 215.4, 215.4);--tblr-table-border-color: rgb(203.64, 180.52, 183.32);--tblr-table-striped-bg: rgb(236.01, 206.68, 207.38);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(225.22, 197.96, 199.36);--tblr-table-active-color: #f9fafb;--tblr-table-hover-bg: rgb(230.615, 202.32, 203.37);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-light{--tblr-table-color: #1f2937;--tblr-table-bg: #f9fafb;--tblr-table-border-color: rgb(205.4, 208.2, 211.8);--tblr-table-striped-bg: rgb(238.1, 239.55, 241.2);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(227.2, 229.1, 231.4);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(232.65, 234.325, 236.3);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-dark{--tblr-table-color: #f9fafb;--tblr-table-bg: #1f2937;--tblr-table-border-color: rgb(74.6, 82.8, 94.2);--tblr-table-striped-bg: rgb(41.9, 51.45, 64.8);--tblr-table-striped-color: #f9fafb;--tblr-table-active-bg: rgb(52.8, 61.9, 74.6);--tblr-table-active-color: #f9fafb;--tblr-table-hover-bg: rgb(47.35, 56.675, 69.7);--tblr-table-hover-color: #f9fafb;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem;font-size:.875rem;font-weight:var(--tblr-font-weight-medium)}.col-form-label{padding-top:calc(.5625rem + var(--tblr-border-width));padding-bottom:calc(.5625rem + var(--tblr-border-width));margin-bottom:0;font-size:inherit;font-weight:var(--tblr-font-weight-medium);line-height:1.25rem}.col-form-label-lg{padding-top:calc(.6875rem + var(--tblr-border-width));padding-bottom:calc(.6875rem + var(--tblr-border-width));font-size:1rem}.col-form-label-sm{padding-top:calc(.3125rem + var(--tblr-border-width));padding-bottom:calc(.3125rem + var(--tblr-border-width));font-size:.75rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--tblr-secondary-color)}.form-control{display:block;width:100%;padding:.5625rem 1rem;font-family:var(--tblr-body-font-family);font-size:.875rem;font-weight:400;line-height:1.25rem;color:var(--tblr-body-color);appearance:none;background-color:var(--tblr-bg-forms);background-clip:padding-box;border:var(--tblr-border-width) solid var(--tblr-border-color);border-radius:var(--tblr-border-radius);box-shadow:var(--tblr-shadow-input);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--tblr-body-color);background-color:var(--tblr-bg-forms);border-color:#80c2be;outline:0;box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.25rem;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::placeholder{color:var(--tblr-tertiary);opacity:1}.form-control:disabled{background-color:var(--tblr-bg-surface-secondary);opacity:1}.form-control::file-selector-button{padding:.5625rem 1rem;margin:-.5625rem -1rem;margin-inline-end:1rem;color:var(--tblr-body-color);background-color:var(--tblr-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--tblr-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--tblr-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.5625rem 0;margin-bottom:0;line-height:1.25rem;color:var(--tblr-body-color);background-color:transparent;border:solid transparent;border-width:var(--tblr-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.25rem + .625rem + calc(var(--tblr-border-width) * 2));padding:.3125rem .5rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.form-control-sm::file-selector-button{padding:.3125rem .5rem;margin:-.3125rem -.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.25rem + 1.375rem + calc(var(--tblr-border-width) * 2));padding:.6875rem 1.5rem;font-size:1rem;border-radius:var(--tblr-border-radius-lg)}.form-control-lg::file-selector-button{padding:.6875rem 1.5rem;margin:-.6875rem -1.5rem;margin-inline-end:1.5rem}textarea.form-control{min-height:calc(1.25rem + 1.125rem + calc(var(--tblr-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.25rem + .625rem + calc(var(--tblr-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.25rem + 1.375rem + calc(var(--tblr-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.25rem + 1.125rem + calc(var(--tblr-border-width) * 2));padding:.5625rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--tblr-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--tblr-border-radius)}.form-control-color.form-control-sm{height:calc(1.25rem + .625rem + calc(var(--tblr-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.25rem + 1.375rem + calc(var(--tblr-border-width) * 2))}.form-select{--tblr-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.5625rem 3rem .5625rem 1rem;font-family:var(--tblr-body-font-family);font-size:.875rem;font-weight:400;line-height:1.25rem;color:var(--tblr-body-color);appearance:none;background-color:var(--tblr-bg-forms);background-image:var(--tblr-form-select-bg-img),var(--tblr-form-select-bg-icon, none);background-repeat:no-repeat;background-position:right 1rem center;background-size:16px 12px;border:var(--tblr-border-width) solid var(--tblr-border-color);border-radius:var(--tblr-border-radius);box-shadow:var(--tblr-shadow-input);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#80c2be;outline:0;box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:1rem;background-image:none}.form-select:disabled{background-color:var(--tblr-bg-surface-secondary)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--tblr-body-color)}.form-select-sm{padding-top:.3125rem;padding-bottom:.3125rem;padding-left:.5rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.form-select-lg{padding-top:.6875rem;padding-bottom:.6875rem;padding-left:1.5rem;font-size:1rem;border-radius:var(--tblr-border-radius-lg)}[data-bs-theme=dark] .form-select,body[data-bs-theme=dark] [data-bs-theme=light] .form-select{--tblr-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23e5e7eb' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.25rem;padding-left:2rem;margin-bottom:.75rem}.form-check .form-check-input{float:left;margin-left:-2rem}.form-check-reverse{padding-right:2rem;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-2rem;margin-left:0}.form-check-input{--tblr-form-check-bg: var(--tblr-bg-forms);flex-shrink:0;width:1.25rem;height:1.25rem;margin-top:.0892857143rem;vertical-align:top;appearance:none;background-color:var(--tblr-form-check-bg);background-image:var(--tblr-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:var(--tblr-border-radius)}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#80c2be;outline:0;box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-check-input:checked{background-color:var(--tblr-primary);border-color:var(--tblr-border-color-translucent)}.form-check-input:checked[type=checkbox]{--tblr-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--tblr-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3ccircle r='3' fill='%23ffffff' cx='8' cy='8' /%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:var(--tblr-primary);border-color:var(--tblr-primary);--tblr-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input[disabled]~.form-check-label,.form-check-input:disabled~.form-check-label{cursor:default;opacity:.7}.form-switch{padding-left:2.5rem}.form-switch .form-check-input{--tblr-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23e5e7eb'/%3e%3c/svg%3e");width:2rem;margin-left:-2.5rem;background-image:var(--tblr-form-switch-bg);background-position:left center;border-radius:2rem;transition:background-position .15s ease-in-out}.form-switch .form-check-input:focus{--tblr-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgb%28127.5, 194, 190%29'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--tblr-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23ffffff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5rem;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5rem;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check[disabled]+.btn,.btn-check:disabled+.btn{pointer-events:none;filter:none;opacity:.4}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--tblr-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.25rem;padding:0;appearance:none;background-color:transparent}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #f9fafb,0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #f9fafb,0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.375rem;appearance:none;background-color:var(--tblr-primary);border:2px var(--tblr-border-style) #ffffff;border-radius:1rem;box-shadow:0 .1rem .25rem #0000001a;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b3dad8}.form-range::-webkit-slider-runnable-track{width:100%;height:.25rem;color:transparent;cursor:pointer;background-color:var(--tblr-border-color);border-color:transparent;border-radius:1rem;box-shadow:var(--tblr-box-shadow-inset)}.form-range::-moz-range-thumb{width:1rem;height:1rem;appearance:none;background-color:var(--tblr-primary);border:2px var(--tblr-border-style) #ffffff;border-radius:1rem;box-shadow:0 .1rem .25rem #0000001a;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{transition:none}}.form-range::-moz-range-thumb:active{background-color:#b3dad8}.form-range::-moz-range-track{width:100%;height:.25rem;color:transparent;cursor:pointer;background-color:var(--tblr-border-color);border-color:transparent;border-radius:1rem;box-shadow:var(--tblr-box-shadow-inset)}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--tblr-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--tblr-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--tblr-border-width) * 2));min-height:calc(3.5rem + calc(var(--tblr-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;max-width:100%;height:100%;padding:1rem;overflow:hidden;color:rgba(var(--tblr-body-color-rgb),.65);text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--tblr-border-width) solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media(prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem}.form-floating>.form-control::placeholder,.form-floating>.form-control-plaintext::placeholder{color:transparent}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown),.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill,.form-floating>.form-control-plaintext:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem;padding-left:1rem}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-control-plaintext~label,.form-floating>.form-select~label{transform:scale(.85) translateY(-.5rem) translate(.15rem)}.form-floating>.form-control:-webkit-autofill~label{transform:scale(.85) translateY(-.5rem) translate(.15rem)}.form-floating>textarea:focus~label:after,.form-floating>textarea:not(:placeholder-shown)~label:after{position:absolute;inset:1rem .5rem;z-index:-1;height:1.5em;content:"";background-color:var(--tblr-bg-forms);border-radius:var(--tblr-border-radius)}.form-floating>textarea:disabled~label:after{background-color:var(--tblr-bg-surface-secondary)}.form-floating>.form-control-plaintext~label{border-width:var(--tblr-border-width) 0}.form-floating>:disabled~label,.form-floating>.form-control:disabled~label{color:#4b5563}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select,.input-group>.form-floating{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus,.input-group>.form-floating:focus-within{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.5625rem 1rem;font-size:.875rem;font-weight:400;line-height:1.25rem;color:var(--tblr-gray-500);text-align:center;white-space:nowrap;background-color:var(--tblr-bg-surface-secondary);border:var(--tblr-border-width) solid var(--tblr-border-color);border-radius:var(--tblr-border-radius)}.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text,.input-group-lg>.btn{padding:.6875rem 1.5rem;font-size:1rem;border-radius:var(--tblr-border-radius-lg)}.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text,.input-group-sm>.btn{padding:.3125rem .5rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:4rem}.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(-1 * var(--tblr-border-width));border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--tblr-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:var(--tblr-spacer-1) var(--tblr-spacer-3);margin-top:.1rem;font-size:.765625rem;color:#fff;background-color:var(--tblr-success);border-radius:var(--tblr-border-radius)}.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip,.is-valid~.valid-feedback,.is-valid~.valid-tooltip{display:block}.was-validated .form-control:valid,.form-control.is-valid{border-color:var(--tblr-form-valid-border-color);padding-right:2.375rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%232fb344' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='20 6 9 17 4 12'%3e%3c/polyline%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right 1.53125rem center;background-size:1.8125rem 1.8125rem}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:var(--tblr-form-valid-border-color);box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:2.375rem;background-position:top 1.53125rem right 1.53125rem}.was-validated .form-select:valid,.form-select.is-valid{border-color:var(--tblr-form-valid-border-color)}.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"],.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"]{--tblr-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%232fb344' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='20 6 9 17 4 12'%3e%3c/polyline%3e%3c/svg%3e");padding-right:5.5rem;background-position:right 1rem center,center right 3rem;background-size:16px 12px,1.8125rem 1.8125rem}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:var(--tblr-form-valid-border-color);box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-success-rgb),.25)}.was-validated .form-control-color:valid,.form-control-color.is-valid{width:5.375rem}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:var(--tblr-form-valid-border-color)}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:var(--tblr-form-valid-color)}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .25rem rgba(var(--tblr-success-rgb),.25)}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:var(--tblr-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):valid,.input-group>.form-control:not(:focus).is-valid,.was-validated .input-group>.form-select:not(:focus):valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.input-group>.form-floating:not(:focus-within).is-valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--tblr-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:var(--tblr-spacer-1) var(--tblr-spacer-3);margin-top:.1rem;font-size:.765625rem;color:#fff;background-color:var(--tblr-danger);border-radius:var(--tblr-border-radius)}.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip,.is-invalid~.invalid-feedback,select.tomselected.is-invalid+div.ts-wrapper~.invalid-feedback,.is-invalid~.invalid-tooltip,select.tomselected.is-invalid+div.ts-wrapper~.invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid,select.tomselected.is-invalid+div.form-control.ts-wrapper{border-color:var(--tblr-form-invalid-border-color);padding-right:2.375rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23d63939' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='18' y1='6' x2='6' y2='18'%3e%3c/line%3e%3cline x1='6' y1='6' x2='18' y2='18'%3e%3c/line%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right 1.53125rem center;background-size:1.8125rem 1.8125rem}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus,select.tomselected.is-invalid+div.form-control.ts-wrapper:focus{border-color:var(--tblr-form-invalid-border-color);box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:2.375rem;background-position:top 1.53125rem right 1.53125rem}.was-validated .form-select:invalid,.form-select.is-invalid,select.tomselected.is-invalid+div.form-select.ts-wrapper{border-color:var(--tblr-form-invalid-border-color)}.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"],.form-select.is-invalid:not([multiple]):not([size]),select.tomselected.is-invalid+div.form-select.ts-wrapper:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],select.tomselected.is-invalid+div.form-select.ts-wrapper:not([multiple])[size="1"]{--tblr-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23d63939' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='18' y1='6' x2='6' y2='18'%3e%3c/line%3e%3cline x1='6' y1='6' x2='18' y2='18'%3e%3c/line%3e%3c/svg%3e");padding-right:5.5rem;background-position:right 1rem center,center right 3rem;background-size:16px 12px,1.8125rem 1.8125rem}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus,select.tomselected.is-invalid+div.form-select.ts-wrapper:focus{border-color:var(--tblr-form-invalid-border-color);box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-danger-rgb),.25)}.was-validated .form-control-color:invalid,.form-control-color.is-invalid,select.tomselected.is-invalid+div.form-control-color.ts-wrapper{width:5.375rem}.was-validated .form-check-input:invalid,.form-check-input.is-invalid,select.tomselected.is-invalid+div.form-check-input.ts-wrapper{border-color:var(--tblr-form-invalid-border-color)}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked,select.tomselected.is-invalid+div.form-check-input.ts-wrapper:checked{background-color:var(--tblr-form-invalid-color)}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus,select.tomselected.is-invalid+div.form-check-input.ts-wrapper:focus{box-shadow:0 0 0 .25rem rgba(var(--tblr-danger-rgb),.25)}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label,select.tomselected.is-invalid+div.form-check-input.ts-wrapper~.form-check-label{color:var(--tblr-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):invalid,.input-group>.form-control:not(:focus).is-invalid,.input-group>select.tomselected.is-invalid+div.form-control.ts-wrapper:not(:focus),.was-validated .input-group>.form-select:not(:focus):invalid,.input-group>.form-select:not(:focus).is-invalid,.input-group>select.tomselected.is-invalid+div.form-select.ts-wrapper:not(:focus),.was-validated .input-group>.form-floating:not(:focus-within):invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>select.tomselected.is-invalid+div.form-floating.ts-wrapper:not(:focus-within){z-index:4}.btn{--tblr-btn-padding-x: .5rem;--tblr-btn-padding-y: .25rem;--tblr-btn-font-family: var(--tblr-body-font-family);--tblr-btn-font-size: .875rem;--tblr-btn-font-weight: var(--tblr-font-weight-medium);--tblr-btn-line-height: 1.25rem;--tblr-btn-color: var(--tblr-body-color);--tblr-btn-bg: transparent;--tblr-btn-border-width: var(--tblr-border-width);--tblr-btn-border-color: transparent;--tblr-btn-border-radius: var(--tblr-border-radius);--tblr-btn-hover-border-color: transparent;--tblr-btn-box-shadow: var(--tblr-shadow-input);--tblr-btn-disabled-opacity: .4;--tblr-btn-focus-box-shadow: 0 0 0 .25rem rgba(var(--tblr-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--tblr-btn-padding-y) var(--tblr-btn-padding-x);font-family:var(--tblr-btn-font-family);font-size:var(--tblr-btn-font-size);font-weight:var(--tblr-btn-font-weight);line-height:var(--tblr-btn-line-height);color:var(--tblr-btn-color);text-align:center;vertical-align:middle;cursor:pointer;user-select:none;border:var(--tblr-btn-border-width) solid var(--tblr-btn-border-color);border-radius:var(--tblr-btn-border-radius);background-color:var(--tblr-btn-bg);box-shadow:var(--tblr-btn-box-shadow);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--tblr-btn-hover-color);text-decoration:none;background-color:var(--tblr-btn-hover-bg);border-color:var(--tblr-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--tblr-btn-color);background-color:var(--tblr-btn-bg);border-color:var(--tblr-btn-border-color)}.btn:focus-visible{color:var(--tblr-btn-hover-color);background-color:var(--tblr-btn-hover-bg);border-color:var(--tblr-btn-hover-border-color);outline:0;box-shadow:var(--tblr-btn-box-shadow),var(--tblr-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--tblr-btn-hover-border-color);outline:0;box-shadow:var(--tblr-btn-box-shadow),var(--tblr-btn-focus-box-shadow)}.btn-check:checked+.btn,:not(.btn-check)+.btn:active,.btn:first-child:active,.btn.active,.btn.show{color:var(--tblr-btn-active-color);background-color:var(--tblr-btn-active-bg);border-color:var(--tblr-btn-active-border-color);box-shadow:var(--tblr-btn-active-shadow)}.btn-check:checked+.btn:focus-visible,:not(.btn-check)+.btn:active:focus-visible,.btn:first-child:active:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible{box-shadow:var(--tblr-btn-active-shadow),var(--tblr-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--tblr-btn-active-shadow),var(--tblr-btn-focus-box-shadow)}.btn:disabled,.btn.disabled,fieldset:disabled .btn{color:var(--tblr-btn-disabled-color);pointer-events:none;background-color:var(--tblr-btn-disabled-bg);border-color:var(--tblr-btn-disabled-border-color);opacity:var(--tblr-btn-disabled-opacity);box-shadow:none}.btn-link{--tblr-btn-font-weight: 400;--tblr-btn-color: var(--tblr-link-color);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-link-hover-color);--tblr-btn-hover-border-color: transparent;--tblr-btn-active-color: var(--tblr-link-hover-color);--tblr-btn-active-border-color: transparent;--tblr-btn-disabled-color: #4b5563;--tblr-btn-disabled-border-color: transparent;--tblr-btn-box-shadow: 0 0 0 #000;--tblr-btn-focus-shadow-rgb: 37, 151, 144;text-decoration:none}.btn-link:hover,.btn-link:focus-visible{text-decoration:underline}.btn-link:focus-visible{color:var(--tblr-btn-color)}.btn-link:hover{color:var(--tblr-btn-hover-color)}.btn-lg,.btn-group-lg>.btn{--tblr-btn-padding-y: .6875rem;--tblr-btn-padding-x: 1.5rem;--tblr-btn-font-size: 1rem;--tblr-btn-border-radius: var(--tblr-border-radius-lg)}.btn-sm,.btn-group-sm>.btn{--tblr-btn-padding-y: .3125rem;--tblr-btn-padding-x: .5rem;--tblr-btn-font-size: .75rem;--tblr-btn-border-radius: var(--tblr-border-radius-sm)}.fade{transition:opacity .15s linear}@media(prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media(prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media(prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropup,.dropend,.dropdown,.dropstart,.dropup-center,.dropdown-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle:after{content:"";display:inline-block;vertical-align:.306em;width:.36em;height:.36em;border-bottom:1px var(--tblr-border-style);border-left:1px var(--tblr-border-style);margin-right:.1em;margin-left:.4em;transform:rotate(-45deg)}.dropdown-menu{--tblr-dropdown-zindex: 1000;--tblr-dropdown-min-width: 11rem;--tblr-dropdown-padding-x: 0;--tblr-dropdown-padding-y: .25rem;--tblr-dropdown-spacer: 1px;--tblr-dropdown-font-size: .875rem;--tblr-dropdown-color: var(--tblr-body-color);--tblr-dropdown-bg: var(--tblr-bg-surface);--tblr-dropdown-border-color: var(--tblr-border-color-translucent);--tblr-dropdown-border-radius: var(--tblr-border-radius);--tblr-dropdown-border-width: var(--tblr-border-width);--tblr-dropdown-inner-border-radius: calc(var(--tblr-border-radius) - var(--tblr-border-width));--tblr-dropdown-divider-bg: var(--tblr-border-color-translucent);--tblr-dropdown-divider-margin-y: var(--tblr-spacer-2);--tblr-dropdown-box-shadow: var(--tblr-shadow-dropdown);--tblr-dropdown-link-color: inherit;--tblr-dropdown-link-hover-color: inherit;--tblr-dropdown-link-hover-bg: rgba(var(--tblr-secondary-rgb), .08);--tblr-dropdown-link-active-color: var(--tblr-primary);--tblr-dropdown-link-active-bg: var(--tblr-active-bg);--tblr-dropdown-link-disabled-color: var(--tblr-tertiary-color);--tblr-dropdown-item-padding-x: .75rem;--tblr-dropdown-item-padding-y: .5rem;--tblr-dropdown-header-color: #4b5563;--tblr-dropdown-header-padding-x: .75rem;--tblr-dropdown-header-padding-y: .25rem;position:absolute;z-index:var(--tblr-dropdown-zindex);display:none;min-width:var(--tblr-dropdown-min-width);padding:var(--tblr-dropdown-padding-y) var(--tblr-dropdown-padding-x);margin:0;font-size:var(--tblr-dropdown-font-size);color:var(--tblr-dropdown-color);text-align:left;list-style:none;background-color:var(--tblr-dropdown-bg);background-clip:padding-box;border:var(--tblr-dropdown-border-width) solid var(--tblr-dropdown-border-color);border-radius:var(--tblr-dropdown-border-radius);box-shadow:var(--tblr-dropdown-box-shadow)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--tblr-dropdown-spacer)}.dropdown-menu-start{--bs-position: start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position: end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media(min-width:576px){.dropdown-menu-sm-start{--bs-position: start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position: end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media(min-width:768px){.dropdown-menu-md-start{--bs-position: start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position: end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media(min-width:992px){.dropdown-menu-lg-start{--bs-position: start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position: end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1200px){.dropdown-menu-xl-start{--bs-position: start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position: end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1400px){.dropdown-menu-xxl-start{--bs-position: start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position: end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--tblr-dropdown-spacer)}.dropup .dropdown-toggle:after{content:"";display:inline-block;vertical-align:.306em;width:.36em;height:.36em;border-bottom:1px var(--tblr-border-style);border-left:1px var(--tblr-border-style);margin-right:.1em;margin-left:.4em;transform:rotate(135deg)}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--tblr-dropdown-spacer)}.dropend .dropdown-toggle:after{content:"";display:inline-block;vertical-align:.306em;width:.36em;height:.36em;border-bottom:1px var(--tblr-border-style);border-left:1px var(--tblr-border-style);margin-right:.1em;margin-left:.4em;transform:rotate(-135deg)}.dropend .dropdown-toggle:after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--tblr-dropdown-spacer)}.dropstart .dropdown-toggle:after{content:"";display:inline-block;vertical-align:.306em;width:.36em;height:.36em;border-bottom:1px var(--tblr-border-style);border-left:1px var(--tblr-border-style);margin-right:.1em;margin-left:.4em;transform:rotate(45deg)}.dropstart .dropdown-toggle:before{vertical-align:0}.dropdown-divider{height:0;margin:var(--tblr-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--tblr-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--tblr-dropdown-item-padding-y) var(--tblr-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--tblr-dropdown-link-color);text-align:inherit;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--tblr-dropdown-item-border-radius, 0)}.dropdown-item:hover,.dropdown-item:focus{color:var(--tblr-dropdown-link-hover-color);text-decoration:none;background-color:var(--tblr-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--tblr-dropdown-link-active-color);text-decoration:none;background-color:var(--tblr-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--tblr-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--tblr-dropdown-header-padding-y) var(--tblr-dropdown-header-padding-x);margin-bottom:0;font-size:.765625rem;color:var(--tblr-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--tblr-dropdown-item-padding-y) var(--tblr-dropdown-item-padding-x);color:var(--tblr-dropdown-link-color)}.dropdown-menu-dark{--tblr-dropdown-color: #d1d5db;--tblr-dropdown-bg: #1f2937;--tblr-dropdown-border-color: var(--tblr-border-color-translucent);--tblr-dropdown-box-shadow: ;--tblr-dropdown-link-color: #d1d5db;--tblr-dropdown-link-hover-color: #ffffff;--tblr-dropdown-divider-bg: var(--tblr-border-color-translucent);--tblr-dropdown-link-hover-bg: rgba(255, 255, 255, .15);--tblr-dropdown-link-active-color: var(--tblr-primary);--tblr-dropdown-link-active-bg: var(--tblr-active-bg);--tblr-dropdown-link-disabled-color: #6b7280;--tblr-dropdown-header-color: #6b7280}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;flex:1 1 auto}.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--tblr-border-radius)}.btn-group>:not(.btn-check:first-child)+.btn,.btn-group>.btn-group:not(:first-child){margin-left:calc(-1 * var(--tblr-border-width))}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn,.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.dropdown-toggle-split:after,.dropup .dropdown-toggle-split:after,.dropend .dropdown-toggle-split:after{margin-left:0}.dropstart .dropdown-toggle-split:before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:1.125rem;padding-left:1.125rem}.btn-group.show .dropdown-toggle{box-shadow:inset 0 3px 5px #00000020}.btn-group.show .dropdown-toggle.btn-link{box-shadow:none}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:calc(-1 * var(--tblr-border-width))}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:nth-child(n+3),.btn-group-vertical>:not(.btn-check)+.btn,.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--tblr-nav-link-padding-x: .75rem;--tblr-nav-link-padding-y: .5rem;--tblr-nav-link-font-weight: ;--tblr-nav-link-color: var(--tblr-gray-500);--tblr-nav-link-hover-color: var(--tblr-link-hover-color);--tblr-nav-link-disabled-color: var(--tblr-disabled-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--tblr-nav-link-padding-y) var(--tblr-nav-link-padding-x);font-size:var(--tblr-nav-link-font-size);font-weight:var(--tblr-nav-link-font-weight);color:var(--tblr-nav-link-color);background:none;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}.nav-link:hover,.nav-link:focus{color:var(--tblr-nav-link-hover-color);text-decoration:none}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.nav-link.disabled,.nav-link:disabled{color:var(--tblr-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--tblr-nav-tabs-border-width: var(--tblr-border-width);--tblr-nav-tabs-border-color: var(--tblr-border-color);--tblr-nav-tabs-border-radius: var(--tblr-border-radius);--tblr-nav-tabs-link-hover-border-color: var(--tblr-border-color) var(--tblr-border-color) var(--tblr-border-color);--tblr-nav-tabs-link-active-color: var(--tblr-body-color);--tblr-nav-tabs-link-active-bg: var(--tblr-body-bg);--tblr-nav-tabs-link-active-border-color: var(--tblr-border-color) var(--tblr-border-color) var(--tblr-border-color);border-bottom:var(--tblr-nav-tabs-border-width) solid var(--tblr-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--tblr-nav-tabs-border-width));border:var(--tblr-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--tblr-nav-tabs-border-radius);border-top-right-radius:var(--tblr-nav-tabs-border-radius)}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{isolation:isolate;border-color:var(--tblr-nav-tabs-link-hover-border-color)}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:var(--tblr-nav-tabs-link-active-color);background-color:var(--tblr-nav-tabs-link-active-bg);border-color:var(--tblr-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--tblr-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--tblr-nav-pills-border-radius: var(--tblr-border-radius);--tblr-nav-pills-link-active-color: var(--tblr-primary);--tblr-nav-pills-link-active-bg: rgba(var(--tblr-secondary-rgb), .15)}.nav-pills .nav-link{border-radius:var(--tblr-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--tblr-nav-pills-link-active-color);background-color:var(--tblr-nav-pills-link-active-bg)}.nav-underline{--tblr-nav-underline-gap: 1rem;--tblr-nav-underline-border-width: .125rem;--tblr-nav-underline-link-active-color: var(--tblr-emphasis-color);gap:var(--tblr-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--tblr-nav-underline-border-width) solid transparent}.nav-underline .nav-link:hover,.nav-underline .nav-link:focus{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:600;color:var(--tblr-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill>.nav-link,.nav-fill .nav-item{flex:1 1 auto;text-align:center}.nav-justified>.nav-link,.nav-justified .nav-item{flex-grow:1;flex-basis:0;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--tblr-navbar-padding-x: 0;--tblr-navbar-padding-y: .25rem;--tblr-navbar-color: var(--tblr-secondary);--tblr-navbar-hover-color: var(--tblr-body-color);--tblr-navbar-disabled-color: var(--tblr-disabled-color);--tblr-navbar-active-color: var(--tblr-body-color);--tblr-navbar-brand-padding-y: .5rem;--tblr-navbar-brand-margin-end: 1rem;--tblr-navbar-brand-font-size: 1.25rem;--tblr-navbar-brand-color: var(--tblr-body-color);--tblr-navbar-brand-hover-color: var(--tblr-body-color);--tblr-navbar-nav-link-padding-x: .75rem;--tblr-navbar-toggler-padding-y: 0;--tblr-navbar-toggler-padding-x: 0;--tblr-navbar-toggler-font-size: 1rem;--tblr-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2831, 41, 55, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--tblr-navbar-toggler-border-color: rgba(var(--tblr-emphasis-color-rgb), .15);--tblr-navbar-toggler-border-radius: var(--tblr-border-radius);--tblr-navbar-toggler-focus-width: 0;--tblr-navbar-toggler-transition: box-shadow .15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--tblr-navbar-padding-y) var(--tblr-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-sm,.navbar>.container-md,.navbar>.container-lg,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--tblr-navbar-brand-padding-y);padding-bottom:var(--tblr-navbar-brand-padding-y);margin-right:var(--tblr-navbar-brand-margin-end);font-size:var(--tblr-navbar-brand-font-size);color:var(--tblr-navbar-brand-color);white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{color:var(--tblr-navbar-brand-hover-color);text-decoration:none}.navbar-nav{--tblr-nav-link-padding-x: 0;--tblr-nav-link-padding-y: .5rem;--tblr-nav-link-font-weight: ;--tblr-nav-link-color: var(--tblr-navbar-color);--tblr-nav-link-hover-color: var(--tblr-navbar-hover-color);--tblr-nav-link-disabled-color: var(--tblr-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--tblr-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--tblr-navbar-color)}.navbar-text a,.navbar-text a:hover,.navbar-text a:focus{color:var(--tblr-navbar-active-color)}.navbar-collapse{flex-grow:1;flex-basis:100%;align-items:center}.navbar-toggler{padding:var(--tblr-navbar-toggler-padding-y) var(--tblr-navbar-toggler-padding-x);font-size:var(--tblr-navbar-toggler-font-size);line-height:1;color:var(--tblr-navbar-color);background-color:transparent;border:var(--tblr-border-width) solid var(--tblr-navbar-toggler-border-color);border-radius:var(--tblr-navbar-toggler-border-radius);transition:var(--tblr-navbar-toggler-transition)}@media(prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--tblr-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--tblr-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--tblr-scroll-height, 75vh);overflow-y:auto}@media(min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark],body[data-bs-theme=dark] .navbar[data-bs-theme=light]{--tblr-navbar-color: rgba(255, 255, 255, .7);--tblr-navbar-hover-color: rgba(255, 255, 255, .75);--tblr-navbar-disabled-color: var(--tblr-disabled-color);--tblr-navbar-active-color: #ffffff;--tblr-navbar-brand-color: #ffffff;--tblr-navbar-brand-hover-color: #ffffff;--tblr-navbar-toggler-border-color: rgba(255, 255, 255, .1);--tblr-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.7%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon,body[data-bs-theme=dark] [data-bs-theme=light] .navbar-toggler-icon{--tblr-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.7%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--tblr-card-spacer-y: 1rem;--tblr-card-spacer-x: 1.25rem;--tblr-card-title-spacer-y: 1.25rem;--tblr-card-title-color: ;--tblr-card-subtitle-color: ;--tblr-card-border-width: var(--tblr-border-width);--tblr-card-border-color: var(--tblr-border-color-translucent);--tblr-card-border-radius: var(--tblr-border-radius-lg);--tblr-card-box-shadow: var(--tblr-shadow-card);--tblr-card-inner-border-radius: calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)));--tblr-card-cap-padding-y: 1rem;--tblr-card-cap-padding-x: 1.25rem;--tblr-card-cap-bg: var(--tblr-bg-surface-tertiary);--tblr-card-cap-color: inherit;--tblr-card-height: ;--tblr-card-color: inherit;--tblr-card-bg: var(--tblr-bg-surface);--tblr-card-img-overlay-padding: 1rem;--tblr-card-group-margin: 1.5rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--tblr-card-height);color:var(--tblr-body-color);word-wrap:break-word;background-color:var(--tblr-card-bg);background-clip:border-box;border:var(--tblr-card-border-width) solid var(--tblr-card-border-color);border-radius:var(--tblr-card-border-radius);box-shadow:var(--tblr-card-box-shadow)}.card>hr,.card>.hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--tblr-card-inner-border-radius);border-top-right-radius:var(--tblr-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--tblr-card-inner-border-radius);border-bottom-left-radius:var(--tblr-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--tblr-card-spacer-y) var(--tblr-card-spacer-x);color:var(--tblr-card-color)}.card-title{margin-bottom:var(--tblr-card-title-spacer-y);color:var(--tblr-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--tblr-card-title-spacer-y));margin-bottom:0;color:var(--tblr-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:var(--tblr-card-spacer-x)}.card-header{padding:var(--tblr-card-cap-padding-y) var(--tblr-card-cap-padding-x);margin-bottom:0;color:var(--tblr-card-cap-color);background-color:var(--tblr-card-cap-bg);border-bottom:var(--tblr-card-border-width) solid var(--tblr-card-border-color)}.card-header:first-child{border-radius:var(--tblr-card-inner-border-radius) var(--tblr-card-inner-border-radius) 0 0}.card-footer{padding:var(--tblr-card-cap-padding-y) var(--tblr-card-cap-padding-x);color:var(--tblr-card-cap-color);background-color:var(--tblr-card-cap-bg);border-top:var(--tblr-card-border-width) solid var(--tblr-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--tblr-card-inner-border-radius) var(--tblr-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--tblr-card-cap-padding-x));margin-bottom:calc(-1 * var(--tblr-card-cap-padding-y));margin-left:calc(-.5 * var(--tblr-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--tblr-card-bg);border-bottom-color:var(--tblr-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--tblr-card-cap-padding-x));margin-left:calc(-.5 * var(--tblr-card-cap-padding-x))}.card-img-overlay{position:absolute;inset:0;padding:var(--tblr-card-img-overlay-padding);border-radius:var(--tblr-card-inner-border-radius)}.card-img,.card-img-top,.card-img-bottom{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--tblr-card-inner-border-radius);border-top-right-radius:var(--tblr-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--tblr-card-inner-border-radius);border-bottom-left-radius:var(--tblr-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--tblr-card-group-margin)}@media(min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child)>.card-img-top,.card-group>.card:not(:last-child)>.card-header{border-top-right-radius:0}.card-group>.card:not(:last-child)>.card-img-bottom,.card-group>.card:not(:last-child)>.card-footer{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child)>.card-img-top,.card-group>.card:not(:first-child)>.card-header{border-top-left-radius:0}.card-group>.card:not(:first-child)>.card-img-bottom,.card-group>.card:not(:first-child)>.card-footer{border-bottom-left-radius:0}}.pagination{--tblr-pagination-padding-x: .25rem;--tblr-pagination-padding-y: calc(.25rem + 1px) ;--tblr-pagination-font-size: .875rem;--tblr-pagination-color: var(--tblr-body-color);--tblr-pagination-bg: transparent;--tblr-pagination-border-width: 1px;--tblr-pagination-border-color: transparent;--tblr-pagination-border-radius: var(--tblr-border-radius);--tblr-pagination-hover-color: var(--tblr-link-hover-color);--tblr-pagination-hover-bg: var(--tblr-active-bg);--tblr-pagination-hover-border-color: var(--tblr-pagination-border-color);--tblr-pagination-focus-color: var(--tblr-link-hover-color);--tblr-pagination-focus-bg: var(--tblr-secondary-bg);--tblr-pagination-focus-box-shadow: 0 0 0 .25rem rgba(var(--tblr-primary-rgb), .25);--tblr-pagination-active-color: #ffffff;--tblr-pagination-active-bg: var(--tblr-primary);--tblr-pagination-active-border-color: var(--tblr-primary);--tblr-pagination-disabled-color: var(--tblr-disabled-color);--tblr-pagination-disabled-bg: transparent;--tblr-pagination-disabled-border-color: var(--tblr-pagination-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--tblr-pagination-padding-y) var(--tblr-pagination-padding-x);font-size:var(--tblr-pagination-font-size);color:var(--tblr-pagination-color);background-color:var(--tblr-pagination-bg);border:var(--tblr-pagination-border-width) solid var(--tblr-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--tblr-pagination-hover-color);text-decoration:none;background-color:var(--tblr-pagination-hover-bg);border-color:var(--tblr-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--tblr-pagination-focus-color);background-color:var(--tblr-pagination-focus-bg);outline:0;box-shadow:var(--tblr-pagination-focus-box-shadow)}.page-link.active,.active>.page-link{z-index:3;color:var(--tblr-pagination-active-color);background-color:var(--tblr-pagination-active-bg);border-color:var(--tblr-pagination-active-border-color)}.page-link.disabled,.disabled>.page-link{color:var(--tblr-pagination-disabled-color);pointer-events:none;background-color:var(--tblr-pagination-disabled-bg);border-color:var(--tblr-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item:first-child .page-link{border-top-left-radius:var(--tblr-pagination-border-radius);border-bottom-left-radius:var(--tblr-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--tblr-pagination-border-radius);border-bottom-right-radius:var(--tblr-pagination-border-radius)}.pagination-lg{--tblr-pagination-padding-x: 1.5rem;--tblr-pagination-padding-y: .75rem;--tblr-pagination-font-size: 1.09375rem;--tblr-pagination-border-radius: var(--tblr-border-radius-lg)}.pagination-sm{--tblr-pagination-padding-x: .5rem;--tblr-pagination-padding-y: .25rem;--tblr-pagination-font-size: .765625rem;--tblr-pagination-border-radius: var(--tblr-border-radius-sm)}@keyframes progress-bar-stripes{0%{background-position-x:var(--tblr-progress-height)}}.progress,.progress-stacked{--tblr-progress-height: .5rem;--tblr-progress-font-size: .65625rem;--tblr-progress-bg: var(--tblr-border-color);--tblr-progress-border-radius: var(--tblr-border-radius);--tblr-progress-box-shadow: var(--tblr-box-shadow-inset);--tblr-progress-bar-color: #ffffff;--tblr-progress-bar-bg: var(--tblr-primary);--tblr-progress-bar-transition: width .6s ease;display:flex;height:var(--tblr-progress-height);overflow:hidden;font-size:var(--tblr-progress-font-size);background-color:var(--tblr-progress-bg);border-radius:var(--tblr-progress-border-radius);box-shadow:var(--tblr-progress-box-shadow)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--tblr-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--tblr-progress-bar-bg);transition:var(--tblr-progress-bar-transition)}@media(prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--tblr-progress-height) var(--tblr-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media(prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--tblr-list-group-color: var(--tblr-body-color);--tblr-list-group-bg: inherit;--tblr-list-group-border-color: var(--tblr-border-color);--tblr-list-group-border-width: var(--tblr-border-width);--tblr-list-group-border-radius: var(--tblr-border-radius);--tblr-list-group-item-padding-x: 1.25rem;--tblr-list-group-item-padding-y: 1rem;--tblr-list-group-action-color: inherit;--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: rgba(var(--tblr-secondary-rgb), .08);--tblr-list-group-action-active-color: var(--tblr-body-color);--tblr-list-group-action-active-bg: var(--tblr-secondary-bg);--tblr-list-group-disabled-color: var(--tblr-secondary-color);--tblr-list-group-disabled-bg: inherit;--tblr-list-group-active-color: inherit;--tblr-list-group-active-bg: var(--tblr-active-bg);--tblr-list-group-active-border-color: var(--tblr-border-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--tblr-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item:before{content:counters(section,".") ". ";counter-increment:section}.list-group-item{position:relative;display:block;padding:var(--tblr-list-group-item-padding-y) var(--tblr-list-group-item-padding-x);color:var(--tblr-list-group-color);background-color:var(--tblr-list-group-bg);border:var(--tblr-list-group-border-width) solid var(--tblr-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--tblr-list-group-disabled-color);pointer-events:none;background-color:var(--tblr-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--tblr-list-group-active-color);background-color:var(--tblr-list-group-active-bg);border-color:var(--tblr-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--tblr-list-group-border-width));border-top-width:var(--tblr-list-group-border-width)}.list-group-item-action{width:100%;color:var(--tblr-list-group-action-color);text-align:inherit}.list-group-item-action:not(.active):hover,.list-group-item-action:not(.active):focus{z-index:1;color:var(--tblr-list-group-action-hover-color);text-decoration:none;background-color:var(--tblr-list-group-action-hover-bg)}.list-group-item-action:not(.active):active{color:var(--tblr-list-group-action-active-color);background-color:var(--tblr-list-group-action-active-bg)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}@media(min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}}@media(min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}}@media(min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}}@media(min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}}@media(min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--tblr-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--tblr-list-group-color: var(--tblr-primary-text-emphasis);--tblr-list-group-bg: var(--tblr-primary-bg-subtle);--tblr-list-group-border-color: var(--tblr-primary-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-primary-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-primary-border-subtle);--tblr-list-group-active-color: var(--tblr-primary-bg-subtle);--tblr-list-group-active-bg: var(--tblr-primary-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-primary-text-emphasis)}.list-group-item-secondary{--tblr-list-group-color: var(--tblr-secondary-text-emphasis);--tblr-list-group-bg: var(--tblr-secondary-bg-subtle);--tblr-list-group-border-color: var(--tblr-secondary-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-secondary-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-secondary-border-subtle);--tblr-list-group-active-color: var(--tblr-secondary-bg-subtle);--tblr-list-group-active-bg: var(--tblr-secondary-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-secondary-text-emphasis)}.list-group-item-success{--tblr-list-group-color: var(--tblr-success-text-emphasis);--tblr-list-group-bg: var(--tblr-success-bg-subtle);--tblr-list-group-border-color: var(--tblr-success-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-success-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-success-border-subtle);--tblr-list-group-active-color: var(--tblr-success-bg-subtle);--tblr-list-group-active-bg: var(--tblr-success-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-success-text-emphasis)}.list-group-item-info{--tblr-list-group-color: var(--tblr-info-text-emphasis);--tblr-list-group-bg: var(--tblr-info-bg-subtle);--tblr-list-group-border-color: var(--tblr-info-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-info-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-info-border-subtle);--tblr-list-group-active-color: var(--tblr-info-bg-subtle);--tblr-list-group-active-bg: var(--tblr-info-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-info-text-emphasis)}.list-group-item-warning{--tblr-list-group-color: var(--tblr-warning-text-emphasis);--tblr-list-group-bg: var(--tblr-warning-bg-subtle);--tblr-list-group-border-color: var(--tblr-warning-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-warning-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-warning-border-subtle);--tblr-list-group-active-color: var(--tblr-warning-bg-subtle);--tblr-list-group-active-bg: var(--tblr-warning-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-warning-text-emphasis)}.list-group-item-danger{--tblr-list-group-color: var(--tblr-danger-text-emphasis);--tblr-list-group-bg: var(--tblr-danger-bg-subtle);--tblr-list-group-border-color: var(--tblr-danger-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-danger-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-danger-border-subtle);--tblr-list-group-active-color: var(--tblr-danger-bg-subtle);--tblr-list-group-active-bg: var(--tblr-danger-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-danger-text-emphasis)}.list-group-item-light{--tblr-list-group-color: var(--tblr-light-text-emphasis);--tblr-list-group-bg: var(--tblr-light-bg-subtle);--tblr-list-group-border-color: var(--tblr-light-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-light-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-light-border-subtle);--tblr-list-group-active-color: var(--tblr-light-bg-subtle);--tblr-list-group-active-bg: var(--tblr-light-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-light-text-emphasis)}.list-group-item-dark{--tblr-list-group-color: var(--tblr-dark-text-emphasis);--tblr-list-group-bg: var(--tblr-dark-bg-subtle);--tblr-list-group-border-color: var(--tblr-dark-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-dark-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-dark-border-subtle);--tblr-list-group-active-color: var(--tblr-dark-bg-subtle);--tblr-list-group-active-bg: var(--tblr-dark-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-dark-text-emphasis)}.list-group-item-muted{--tblr-list-group-color: var(--tblr-muted-text-emphasis);--tblr-list-group-bg: var(--tblr-muted-bg-subtle);--tblr-list-group-border-color: var(--tblr-muted-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-muted-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-muted-border-subtle);--tblr-list-group-active-color: var(--tblr-muted-bg-subtle);--tblr-list-group-active-bg: var(--tblr-muted-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-muted-text-emphasis)}.list-group-item-blue{--tblr-list-group-color: var(--tblr-blue-text-emphasis);--tblr-list-group-bg: var(--tblr-blue-bg-subtle);--tblr-list-group-border-color: var(--tblr-blue-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-blue-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-blue-border-subtle);--tblr-list-group-active-color: var(--tblr-blue-bg-subtle);--tblr-list-group-active-bg: var(--tblr-blue-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-blue-text-emphasis)}.list-group-item-azure{--tblr-list-group-color: var(--tblr-azure-text-emphasis);--tblr-list-group-bg: var(--tblr-azure-bg-subtle);--tblr-list-group-border-color: var(--tblr-azure-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-azure-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-azure-border-subtle);--tblr-list-group-active-color: var(--tblr-azure-bg-subtle);--tblr-list-group-active-bg: var(--tblr-azure-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-azure-text-emphasis)}.list-group-item-indigo{--tblr-list-group-color: var(--tblr-indigo-text-emphasis);--tblr-list-group-bg: var(--tblr-indigo-bg-subtle);--tblr-list-group-border-color: var(--tblr-indigo-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-indigo-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-indigo-border-subtle);--tblr-list-group-active-color: var(--tblr-indigo-bg-subtle);--tblr-list-group-active-bg: var(--tblr-indigo-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-indigo-text-emphasis)}.list-group-item-purple{--tblr-list-group-color: var(--tblr-purple-text-emphasis);--tblr-list-group-bg: var(--tblr-purple-bg-subtle);--tblr-list-group-border-color: var(--tblr-purple-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-purple-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-purple-border-subtle);--tblr-list-group-active-color: var(--tblr-purple-bg-subtle);--tblr-list-group-active-bg: var(--tblr-purple-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-purple-text-emphasis)}.list-group-item-pink{--tblr-list-group-color: var(--tblr-pink-text-emphasis);--tblr-list-group-bg: var(--tblr-pink-bg-subtle);--tblr-list-group-border-color: var(--tblr-pink-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-pink-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-pink-border-subtle);--tblr-list-group-active-color: var(--tblr-pink-bg-subtle);--tblr-list-group-active-bg: var(--tblr-pink-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-pink-text-emphasis)}.list-group-item-red{--tblr-list-group-color: var(--tblr-red-text-emphasis);--tblr-list-group-bg: var(--tblr-red-bg-subtle);--tblr-list-group-border-color: var(--tblr-red-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-red-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-red-border-subtle);--tblr-list-group-active-color: var(--tblr-red-bg-subtle);--tblr-list-group-active-bg: var(--tblr-red-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-red-text-emphasis)}.list-group-item-orange{--tblr-list-group-color: var(--tblr-orange-text-emphasis);--tblr-list-group-bg: var(--tblr-orange-bg-subtle);--tblr-list-group-border-color: var(--tblr-orange-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-orange-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-orange-border-subtle);--tblr-list-group-active-color: var(--tblr-orange-bg-subtle);--tblr-list-group-active-bg: var(--tblr-orange-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-orange-text-emphasis)}.list-group-item-yellow{--tblr-list-group-color: var(--tblr-yellow-text-emphasis);--tblr-list-group-bg: var(--tblr-yellow-bg-subtle);--tblr-list-group-border-color: var(--tblr-yellow-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-yellow-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-yellow-border-subtle);--tblr-list-group-active-color: var(--tblr-yellow-bg-subtle);--tblr-list-group-active-bg: var(--tblr-yellow-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-yellow-text-emphasis)}.list-group-item-lime{--tblr-list-group-color: var(--tblr-lime-text-emphasis);--tblr-list-group-bg: var(--tblr-lime-bg-subtle);--tblr-list-group-border-color: var(--tblr-lime-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-lime-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-lime-border-subtle);--tblr-list-group-active-color: var(--tblr-lime-bg-subtle);--tblr-list-group-active-bg: var(--tblr-lime-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-lime-text-emphasis)}.list-group-item-green{--tblr-list-group-color: var(--tblr-green-text-emphasis);--tblr-list-group-bg: var(--tblr-green-bg-subtle);--tblr-list-group-border-color: var(--tblr-green-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-green-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-green-border-subtle);--tblr-list-group-active-color: var(--tblr-green-bg-subtle);--tblr-list-group-active-bg: var(--tblr-green-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-green-text-emphasis)}.list-group-item-teal{--tblr-list-group-color: var(--tblr-teal-text-emphasis);--tblr-list-group-bg: var(--tblr-teal-bg-subtle);--tblr-list-group-border-color: var(--tblr-teal-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-teal-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-teal-border-subtle);--tblr-list-group-active-color: var(--tblr-teal-bg-subtle);--tblr-list-group-active-bg: var(--tblr-teal-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-teal-text-emphasis)}.list-group-item-cyan{--tblr-list-group-color: var(--tblr-cyan-text-emphasis);--tblr-list-group-bg: var(--tblr-cyan-bg-subtle);--tblr-list-group-border-color: var(--tblr-cyan-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-cyan-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-cyan-border-subtle);--tblr-list-group-active-color: var(--tblr-cyan-bg-subtle);--tblr-list-group-active-bg: var(--tblr-cyan-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-cyan-text-emphasis)}.toast{--tblr-toast-zindex: 1090;--tblr-toast-padding-x: .75rem;--tblr-toast-padding-y: .5rem;--tblr-toast-spacing: calc(var(--tblr-page-padding) * 2);--tblr-toast-max-width: 350px;--tblr-toast-font-size: .875rem;--tblr-toast-color: ;--tblr-toast-bg: var(--tblr-bg-surface);--tblr-toast-border-width: var(--tblr-border-width);--tblr-toast-border-color: var(--tblr-border-color);--tblr-toast-border-radius: var(--tblr-border-radius);--tblr-toast-box-shadow: var(--tblr-box-shadow);--tblr-toast-header-color: var(--tblr-gray-500);--tblr-toast-header-bg: rgba(var(--tblr-body-bg-rgb), .85);--tblr-toast-header-border-color: var(--tblr-border-color);width:var(--tblr-toast-max-width);max-width:100%;font-size:var(--tblr-toast-font-size);color:var(--tblr-toast-color);pointer-events:auto;background-color:var(--tblr-toast-bg);background-clip:padding-box;border:var(--tblr-toast-border-width) solid var(--tblr-toast-border-color);box-shadow:var(--tblr-toast-box-shadow);border-radius:var(--tblr-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--tblr-toast-zindex: 1090;position:absolute;z-index:var(--tblr-toast-zindex);width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--tblr-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--tblr-toast-padding-y) var(--tblr-toast-padding-x);color:var(--tblr-toast-header-color);background-color:var(--tblr-toast-header-bg);background-clip:padding-box;border-bottom:var(--tblr-toast-border-width) solid var(--tblr-toast-header-border-color);border-top-left-radius:calc(var(--tblr-toast-border-radius) - var(--tblr-toast-border-width));border-top-right-radius:calc(var(--tblr-toast-border-radius) - var(--tblr-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--tblr-toast-padding-x));margin-left:var(--tblr-toast-padding-x)}.toast-body{padding:var(--tblr-toast-padding-x);word-wrap:break-word}.modal{--tblr-modal-zindex: 1055;--tblr-modal-width: 540px;--tblr-modal-padding: 1.5rem;--tblr-modal-margin: .5rem;--tblr-modal-color: var(--tblr-body-color);--tblr-modal-bg: var(--tblr-bg-surface);--tblr-modal-border-color: transparent;--tblr-modal-border-width: var(--tblr-border-width);--tblr-modal-border-radius: var(--tblr-border-radius-lg);--tblr-modal-box-shadow: var(--tblr-box-shadow-sm);--tblr-modal-inner-border-radius: calc(var(--tblr-modal-border-radius) - 1px);--tblr-modal-header-padding-x: 1.5rem;--tblr-modal-header-padding-y: 1.5rem;--tblr-modal-header-padding: 1.5rem;--tblr-modal-header-border-color: var(--tblr-border-color);--tblr-modal-header-border-width: var(--tblr-border-width);--tblr-modal-title-line-height: 1.4285714286;--tblr-modal-footer-gap: .75rem;--tblr-modal-footer-bg: var(--tblr-bg-surface-tertiary);--tblr-modal-footer-border-color: var(--tblr-border-color);--tblr-modal-footer-border-width: var(--tblr-border-width);position:fixed;top:0;left:0;z-index:var(--tblr-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--tblr-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transform:translateY(-1rem);transition:transform .3s ease-out}@media(prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--tblr-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--tblr-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--tblr-modal-color);pointer-events:auto;background-color:var(--tblr-modal-bg);background-clip:padding-box;border:var(--tblr-modal-border-width) solid var(--tblr-modal-border-color);border-radius:var(--tblr-modal-border-radius);box-shadow:var(--tblr-modal-box-shadow);outline:0}.modal-backdrop{--tblr-backdrop-zindex: 1050;--tblr-backdrop-bg: var(--tblr-gray-800);--tblr-backdrop-opacity: .24;position:fixed;top:0;left:0;z-index:var(--tblr-backdrop-zindex);width:100vw;height:100vh;background-color:var(--tblr-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--tblr-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--tblr-modal-header-padding);border-bottom:var(--tblr-modal-header-border-width) solid var(--tblr-modal-header-border-color);border-top-left-radius:var(--tblr-modal-inner-border-radius);border-top-right-radius:var(--tblr-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--tblr-modal-header-padding-y) * .5) calc(var(--tblr-modal-header-padding-x) * .5);margin-top:calc(-.5 * var(--tblr-modal-header-padding-y));margin-right:calc(-.5 * var(--tblr-modal-header-padding-x));margin-bottom:calc(-.5 * var(--tblr-modal-header-padding-y));margin-left:auto}.modal-title{margin-bottom:0;line-height:var(--tblr-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--tblr-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--tblr-modal-padding) - var(--tblr-modal-footer-gap) * .5);background-color:var(--tblr-modal-footer-bg);border-top:var(--tblr-modal-footer-border-width) solid var(--tblr-modal-footer-border-color);border-bottom-right-radius:var(--tblr-modal-inner-border-radius);border-bottom-left-radius:var(--tblr-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--tblr-modal-footer-gap) * .5)}@media(min-width:576px){.modal{--tblr-modal-margin: 1.75rem;--tblr-modal-box-shadow: var(--tblr-box-shadow)}.modal-dialog{max-width:var(--tblr-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--tblr-modal-width: 380px}}@media(min-width:992px){.modal-lg,.modal-xl{--tblr-modal-width: 720px}}@media(min-width:1200px){.modal-xl{--tblr-modal-width: 1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header,.modal-fullscreen .modal-footer{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media(max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header,.modal-fullscreen-sm-down .modal-footer{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media(max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header,.modal-fullscreen-md-down .modal-footer{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media(max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header,.modal-fullscreen-lg-down .modal-footer{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media(max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header,.modal-fullscreen-xl-down .modal-footer{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media(max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header,.modal-fullscreen-xxl-down .modal-footer{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--tblr-tooltip-zindex: 1080;--tblr-tooltip-max-width: 200px;--tblr-tooltip-padding-x: var(--tblr-spacer-3);--tblr-tooltip-padding-y: var(--tblr-spacer-1);--tblr-tooltip-margin: ;--tblr-tooltip-font-size: .765625rem;--tblr-tooltip-color: var(--tblr-text-inverted);--tblr-tooltip-bg: var(--tblr-bg-surface-inverted);--tblr-tooltip-border-radius: var(--tblr-border-radius);--tblr-tooltip-opacity: .9;--tblr-tooltip-arrow-width: .8rem;--tblr-tooltip-arrow-height: .4rem;z-index:var(--tblr-tooltip-zindex);display:block;margin:var(--tblr-tooltip-margin);font-family:var(--tblr-font-sans-serif);font-style:normal;font-weight:400;line-height:1.4285714286;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--tblr-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--tblr-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--tblr-tooltip-arrow-width);height:var(--tblr-tooltip-arrow-height)}.tooltip .tooltip-arrow:before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow{bottom:calc(-1 * var(--tblr-tooltip-arrow-height))}.bs-tooltip-top .tooltip-arrow:before,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow:before{top:-1px;border-width:var(--tblr-tooltip-arrow-height) calc(var(--tblr-tooltip-arrow-width) * .5) 0;border-top-color:var(--tblr-tooltip-bg)}.bs-tooltip-end .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow{left:calc(-1 * var(--tblr-tooltip-arrow-height));width:var(--tblr-tooltip-arrow-height);height:var(--tblr-tooltip-arrow-width)}.bs-tooltip-end .tooltip-arrow:before,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow:before{right:-1px;border-width:calc(var(--tblr-tooltip-arrow-width) * .5) var(--tblr-tooltip-arrow-height) calc(var(--tblr-tooltip-arrow-width) * .5) 0;border-right-color:var(--tblr-tooltip-bg)}.bs-tooltip-bottom .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow{top:calc(-1 * var(--tblr-tooltip-arrow-height))}.bs-tooltip-bottom .tooltip-arrow:before,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow:before{bottom:-1px;border-width:0 calc(var(--tblr-tooltip-arrow-width) * .5) var(--tblr-tooltip-arrow-height);border-bottom-color:var(--tblr-tooltip-bg)}.bs-tooltip-start .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow{right:calc(-1 * var(--tblr-tooltip-arrow-height));width:var(--tblr-tooltip-arrow-height);height:var(--tblr-tooltip-arrow-width)}.bs-tooltip-start .tooltip-arrow:before,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow:before{left:-1px;border-width:calc(var(--tblr-tooltip-arrow-width) * .5) 0 calc(var(--tblr-tooltip-arrow-width) * .5) var(--tblr-tooltip-arrow-height);border-left-color:var(--tblr-tooltip-bg)}.tooltip-inner{max-width:var(--tblr-tooltip-max-width);padding:var(--tblr-tooltip-padding-y) var(--tblr-tooltip-padding-x);color:var(--tblr-tooltip-color);text-align:center;background-color:var(--tblr-tooltip-bg);border-radius:var(--tblr-tooltip-border-radius)}.popover{--tblr-popover-zindex: 1070;--tblr-popover-max-width: 276px;--tblr-popover-font-size: .765625rem;--tblr-popover-bg: var(--tblr-bg-surface);--tblr-popover-border-width: var(--tblr-border-width);--tblr-popover-border-color: var(--tblr-border-color);--tblr-popover-border-radius: var(--tblr-border-radius-lg);--tblr-popover-inner-border-radius: calc(var(--tblr-border-radius-lg) - var(--tblr-border-width));--tblr-popover-box-shadow: var(--tblr-shadow-lg);--tblr-popover-header-padding-x: 1rem;--tblr-popover-header-padding-y: .5rem;--tblr-popover-header-font-size: .875rem;--tblr-popover-header-color: inherit;--tblr-popover-header-bg: transparent;--tblr-popover-body-padding-x: .5rem;--tblr-popover-body-padding-y: .5rem;--tblr-popover-body-color: inherit;--tblr-popover-arrow-width: 1rem;--tblr-popover-arrow-height: .5rem;--tblr-popover-arrow-border: var(--tblr-popover-border-color);z-index:var(--tblr-popover-zindex);display:block;max-width:var(--tblr-popover-max-width);font-family:var(--tblr-font-sans-serif);font-style:normal;font-weight:400;line-height:1.4285714286;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--tblr-popover-font-size);word-wrap:break-word;background-color:var(--tblr-popover-bg);background-clip:padding-box;border:var(--tblr-popover-border-width) solid var(--tblr-popover-border-color);border-radius:var(--tblr-popover-border-radius);box-shadow:var(--tblr-popover-box-shadow)}.popover .popover-arrow{display:block;width:var(--tblr-popover-arrow-width);height:var(--tblr-popover-arrow-height)}.popover .popover-arrow:before,.popover .popover-arrow:after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-top>.popover-arrow,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow{bottom:calc(-1 * (var(--tblr-popover-arrow-height)) - var(--tblr-popover-border-width))}.bs-popover-top>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow:before,.bs-popover-top>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow:after{border-width:var(--tblr-popover-arrow-height) calc(var(--tblr-popover-arrow-width) * .5) 0}.bs-popover-top>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow:before{bottom:0;border-top-color:var(--tblr-popover-arrow-border)}.bs-popover-top>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow:after{bottom:var(--tblr-popover-border-width);border-top-color:var(--tblr-popover-bg)}.bs-popover-end>.popover-arrow,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow{left:calc(-1 * (var(--tblr-popover-arrow-height)) - var(--tblr-popover-border-width));width:var(--tblr-popover-arrow-height);height:var(--tblr-popover-arrow-width)}.bs-popover-end>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow:before,.bs-popover-end>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow:after{border-width:calc(var(--tblr-popover-arrow-width) * .5) var(--tblr-popover-arrow-height) calc(var(--tblr-popover-arrow-width) * .5) 0}.bs-popover-end>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow:before{left:0;border-right-color:var(--tblr-popover-arrow-border)}.bs-popover-end>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow:after{left:var(--tblr-popover-border-width);border-right-color:var(--tblr-popover-bg)}.bs-popover-bottom>.popover-arrow,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow{top:calc(-1 * (var(--tblr-popover-arrow-height)) - var(--tblr-popover-border-width))}.bs-popover-bottom>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow:before,.bs-popover-bottom>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow:after{border-width:0 calc(var(--tblr-popover-arrow-width) * .5) var(--tblr-popover-arrow-height)}.bs-popover-bottom>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow:before{top:0;border-bottom-color:var(--tblr-popover-arrow-border)}.bs-popover-bottom>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow:after{top:var(--tblr-popover-border-width);border-bottom-color:var(--tblr-popover-bg)}.bs-popover-bottom .popover-header:before,.bs-popover-auto[data-popper-placement^=bottom] .popover-header:before{position:absolute;top:0;left:50%;display:block;width:var(--tblr-popover-arrow-width);margin-left:calc(-.5 * var(--tblr-popover-arrow-width));content:"";border-bottom:var(--tblr-popover-border-width) solid var(--tblr-popover-header-bg)}.bs-popover-start>.popover-arrow,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow{right:calc(-1 * (var(--tblr-popover-arrow-height)) - var(--tblr-popover-border-width));width:var(--tblr-popover-arrow-height);height:var(--tblr-popover-arrow-width)}.bs-popover-start>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow:before,.bs-popover-start>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow:after{border-width:calc(var(--tblr-popover-arrow-width) * .5) 0 calc(var(--tblr-popover-arrow-width) * .5) var(--tblr-popover-arrow-height)}.bs-popover-start>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow:before{right:0;border-left-color:var(--tblr-popover-arrow-border)}.bs-popover-start>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow:after{right:var(--tblr-popover-border-width);border-left-color:var(--tblr-popover-bg)}.popover-header{padding:var(--tblr-popover-header-padding-y) var(--tblr-popover-header-padding-x);margin-bottom:0;font-size:var(--tblr-popover-header-font-size);color:var(--tblr-popover-header-color);background-color:var(--tblr-popover-header-bg);border-bottom:var(--tblr-popover-border-width) solid var(--tblr-popover-border-color);border-top-left-radius:var(--tblr-popover-inner-border-radius);border-top-right-radius:var(--tblr-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--tblr-popover-body-padding-y) var(--tblr-popover-body-padding-x);color:var(--tblr-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner:after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;transition:transform .6s ease-in-out}@media(prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block}.carousel-item-next:not(.carousel-item-start),.active.carousel-item-end{transform:translate(100%)}.carousel-item-prev:not(.carousel-item-end),.active.carousel-item-start{transform:translate(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end{z-index:1;opacity:1}.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{z-index:0;opacity:0;transition:opacity 0s .6s}@media(prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{transition:none}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:none;filter:var(--tblr-carousel-control-icon-filter);border:0;opacity:.5;transition:opacity .15s ease}@media(prefers-reduced-motion:reduce){.carousel-control-prev,.carousel-control-next{transition:none}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:1.5rem;height:1.5rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='15 18 9 12 15 6'%3e%3c/polyline%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='9 18 15 12 9 6'%3e%3c/polyline%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:var(--tblr-carousel-indicator-active-bg);background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media(prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:var(--tblr-carousel-caption-color);text-align:center}.carousel-dark{--tblr-carousel-indicator-active-bg: #000000;--tblr-carousel-caption-color: #000000;--tblr-carousel-control-icon-filter: invert(1) grayscale(100)}:root,[data-bs-theme=light]{--tblr-carousel-indicator-active-bg: #ffffff;--tblr-carousel-caption-color: #ffffff;--tblr-carousel-control-icon-filter: }[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{--tblr-carousel-indicator-active-bg: #000000;--tblr-carousel-caption-color: #000000;--tblr-carousel-control-icon-filter: invert(1) grayscale(100)}.spinner-grow,.spinner-border{display:inline-block;flex-shrink:0;width:var(--tblr-spinner-width);height:var(--tblr-spinner-height);vertical-align:var(--tblr-spinner-vertical-align);border-radius:50%;animation:var(--tblr-spinner-animation-speed) linear infinite var(--tblr-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--tblr-spinner-width: 1.5rem;--tblr-spinner-height: 1.5rem;--tblr-spinner-vertical-align: -.125em;--tblr-spinner-border-width: 2px;--tblr-spinner-animation-speed: .75s;--tblr-spinner-animation-name: spinner-border;border:var(--tblr-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--tblr-spinner-width: 1rem;--tblr-spinner-height: 1rem;--tblr-spinner-border-width: 1px}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--tblr-spinner-width: 1.5rem;--tblr-spinner-height: 1.5rem;--tblr-spinner-vertical-align: -.125em;--tblr-spinner-animation-speed: .75s;--tblr-spinner-animation-name: spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--tblr-spinner-width: 1rem;--tblr-spinner-height: 1rem}@media(prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--tblr-spinner-animation-speed: 1.5s}}.offcanvas,.offcanvas-xxl,.offcanvas-xl,.offcanvas-lg,.offcanvas-md,.offcanvas-sm{--tblr-offcanvas-zindex: 1045;--tblr-offcanvas-width: 400px;--tblr-offcanvas-height: 30vh;--tblr-offcanvas-padding-x: 1.5rem;--tblr-offcanvas-padding-y: 1.5rem;--tblr-offcanvas-color: var(--tblr-body-color);--tblr-offcanvas-bg: var(--tblr-bg-surface);--tblr-offcanvas-border-width: var(--tblr-border-width);--tblr-offcanvas-border-color: var(--tblr-border-color);--tblr-offcanvas-box-shadow: var(--tblr-box-shadow-sm);--tblr-offcanvas-transition: transform .3s ease-in-out;--tblr-offcanvas-title-line-height: 1.4285714286}@media(max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}}@media(max-width:575.98px)and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media(max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.showing,.offcanvas-sm.show:not(.hiding){transform:none}.offcanvas-sm.showing,.offcanvas-sm.hiding,.offcanvas-sm.show{visibility:visible}}@media(min-width:576px){.offcanvas-sm{--tblr-offcanvas-height: auto;--tblr-offcanvas-border-width: 0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}}@media(max-width:767.98px)and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media(max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.showing,.offcanvas-md.show:not(.hiding){transform:none}.offcanvas-md.showing,.offcanvas-md.hiding,.offcanvas-md.show{visibility:visible}}@media(min-width:768px){.offcanvas-md{--tblr-offcanvas-height: auto;--tblr-offcanvas-border-width: 0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}}@media(max-width:991.98px)and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media(max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.showing,.offcanvas-lg.show:not(.hiding){transform:none}.offcanvas-lg.showing,.offcanvas-lg.hiding,.offcanvas-lg.show{visibility:visible}}@media(min-width:992px){.offcanvas-lg{--tblr-offcanvas-height: auto;--tblr-offcanvas-border-width: 0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}}@media(max-width:1199.98px)and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media(max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.showing,.offcanvas-xl.show:not(.hiding){transform:none}.offcanvas-xl.showing,.offcanvas-xl.hiding,.offcanvas-xl.show{visibility:visible}}@media(min-width:1200px){.offcanvas-xl{--tblr-offcanvas-height: auto;--tblr-offcanvas-border-width: 0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}}@media(max-width:1399.98px)and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media(max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.showing,.offcanvas-xxl.show:not(.hiding){transform:none}.offcanvas-xxl.showing,.offcanvas-xxl.hiding,.offcanvas-xxl.show{visibility:visible}}@media(min-width:1400px){.offcanvas-xxl{--tblr-offcanvas-height: auto;--tblr-offcanvas-border-width: 0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}@media(prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas.showing,.offcanvas.show:not(.hiding){transform:none}.offcanvas.showing,.offcanvas.hiding,.offcanvas.show{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:var(--tblr-gray-800)}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.24}.offcanvas-header{display:flex;align-items:center;padding:var(--tblr-offcanvas-padding-y) var(--tblr-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--tblr-offcanvas-padding-y) * .5) calc(var(--tblr-offcanvas-padding-x) * .5);margin-top:calc(-.5 * var(--tblr-offcanvas-padding-y));margin-right:calc(-.5 * var(--tblr-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--tblr-offcanvas-padding-y));margin-left:auto}.offcanvas-title{margin-bottom:0;line-height:var(--tblr-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--tblr-offcanvas-padding-y) var(--tblr-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.2}.placeholder.btn:before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.1}}.placeholder-wave{mask-image:linear-gradient(130deg,#000 55%,#000000e6,#000 95%);mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{to{mask-position:-200% 0%}}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{object-fit:contain!important}.object-fit-cover{object-fit:cover!important}.object-fit-fill{object-fit:fill!important}.object-fit-scale{object-fit:scale-down!important}.object-fit-none{object-fit:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:var(--tblr-box-shadow)!important}.shadow-sm{box-shadow:var(--tblr-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--tblr-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--tblr-focus-ring-color: rgba(var(--tblr-primary-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-secondary{--tblr-focus-ring-color: rgba(var(--tblr-secondary-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-success{--tblr-focus-ring-color: rgba(var(--tblr-success-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-info{--tblr-focus-ring-color: rgba(var(--tblr-info-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-warning{--tblr-focus-ring-color: rgba(var(--tblr-warning-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-danger{--tblr-focus-ring-color: rgba(var(--tblr-danger-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-light{--tblr-focus-ring-color: rgba(var(--tblr-light-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-dark{--tblr-focus-ring-color: rgba(var(--tblr-dark-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-muted{--tblr-focus-ring-color: rgba(var(--tblr-muted-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-blue{--tblr-focus-ring-color: rgba(var(--tblr-blue-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-azure{--tblr-focus-ring-color: rgba(var(--tblr-azure-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-indigo{--tblr-focus-ring-color: rgba(var(--tblr-indigo-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-purple{--tblr-focus-ring-color: rgba(var(--tblr-purple-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-pink{--tblr-focus-ring-color: rgba(var(--tblr-pink-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-red{--tblr-focus-ring-color: rgba(var(--tblr-red-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-orange{--tblr-focus-ring-color: rgba(var(--tblr-orange-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-yellow{--tblr-focus-ring-color: rgba(var(--tblr-yellow-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-lime{--tblr-focus-ring-color: rgba(var(--tblr-lime-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-green{--tblr-focus-ring-color: rgba(var(--tblr-green-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-teal{--tblr-focus-ring-color: rgba(var(--tblr-teal-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-cyan{--tblr-focus-ring-color: rgba(var(--tblr-cyan-rgb), var(--tblr-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translate(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-wide{border:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-0{border:0!important}.border-top{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-top-wide{border-top:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-end-wide{border-right:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-bottom-wide{border-bottom:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-start-wide{border-left:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-start-0{border-left:0!important}.border-red{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-red-rgb),var(--tblr-border-opacity))!important}.border-green{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-green-rgb),var(--tblr-border-opacity))!important}.border-primary-subtle{border-color:var(--tblr-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--tblr-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--tblr-success-border-subtle)!important}.border-info-subtle{border-color:var(--tblr-info-border-subtle)!important}.border-warning-subtle{border-color:var(--tblr-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--tblr-danger-border-subtle)!important}.border-light-subtle{border-color:var(--tblr-light-border-subtle)!important}.border-dark-subtle{border-color:var(--tblr-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--tblr-border-opacity: .1}.border-opacity-25{--tblr-border-opacity: .25}.border-opacity-50{--tblr-border-opacity: .5}.border-opacity-75{--tblr-border-opacity: .75}.border-opacity-100{--tblr-border-opacity: 1}.w-25{width:25%!important}.w-33{width:33.33333%!important}.w-50{width:50%!important}.w-66{width:66.66666%!important}.w-75{width:75%!important}.w-100{width:100%!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-33{height:33.33333%!important}.h-50{height:50%!important}.h-66{height:66.66666%!important}.h-75{height:75%!important}.h-100{height:100%!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:2rem!important}.m-6{margin:2.5rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:2rem!important;margin-left:2rem!important}.mx-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:2rem!important}.mt-6{margin-top:2.5rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:2rem!important}.me-6{margin-right:2.5rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:2rem!important}.mb-6{margin-bottom:2.5rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:2rem!important}.ms-6{margin-left:2.5rem!important}.ms-auto{margin-left:auto!important}.m-n1{margin:-.25rem!important}.m-n2{margin:-.5rem!important}.m-n3{margin:-1rem!important}.m-n4{margin:-1.5rem!important}.m-n5{margin:-2rem!important}.m-n6{margin:-2.5rem!important}.mx-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-n1{margin-top:-.25rem!important}.mt-n2{margin-top:-.5rem!important}.mt-n3{margin-top:-1rem!important}.mt-n4{margin-top:-1.5rem!important}.mt-n5{margin-top:-2rem!important}.mt-n6{margin-top:-2.5rem!important}.me-n1{margin-right:-.25rem!important}.me-n2{margin-right:-.5rem!important}.me-n3{margin-right:-1rem!important}.me-n4{margin-right:-1.5rem!important}.me-n5{margin-right:-2rem!important}.me-n6{margin-right:-2.5rem!important}.mb-n1{margin-bottom:-.25rem!important}.mb-n2{margin-bottom:-.5rem!important}.mb-n3{margin-bottom:-1rem!important}.mb-n4{margin-bottom:-1.5rem!important}.mb-n5{margin-bottom:-2rem!important}.mb-n6{margin-bottom:-2.5rem!important}.ms-n1{margin-left:-.25rem!important}.ms-n2{margin-left:-.5rem!important}.ms-n3{margin-left:-1rem!important}.ms-n4{margin-left:-1.5rem!important}.ms-n5{margin-left:-2rem!important}.ms-n6{margin-left:-2.5rem!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:2rem!important}.p-6{padding:2.5rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:2rem!important;padding-left:2rem!important}.px-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:2rem!important}.pt-6{padding-top:2.5rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:2rem!important}.pe-6{padding-right:2.5rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:2rem!important}.pb-6{padding-bottom:2.5rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:2rem!important}.ps-6{padding-left:2.5rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:2rem!important}.gap-6{gap:2.5rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:2rem!important}.row-gap-6{row-gap:2.5rem!important}.column-gap-0{column-gap:0!important}.column-gap-1{column-gap:.25rem!important}.column-gap-2{column-gap:.5rem!important}.column-gap-3{column-gap:1rem!important}.column-gap-4{column-gap:1.5rem!important}.column-gap-5{column-gap:2rem!important}.column-gap-6{column-gap:2.5rem!important}.font-monospace{font-family:var(--tblr-font-monospace)!important}.fs-1{font-size:1.5rem!important}.fs-2{font-size:1.25rem!important}.fs-3{font-size:1rem!important}.fs-4{font-size:.875rem!important}.fs-5{font-size:.75rem!important}.fs-6{font-size:.625rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold,.fw-bold{font-weight:600!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.1428571429!important}.lh-base{line-height:1.4285714286!important}.lh-lg{line-height:1.7142857143!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--tblr-text-opacity: 1;color:rgba(var(--tblr-primary-rgb),var(--tblr-text-opacity))!important}.text-secondary{--tblr-text-opacity: 1;color:rgba(var(--tblr-secondary-rgb),var(--tblr-text-opacity))!important}.text-success{--tblr-text-opacity: 1;color:rgba(var(--tblr-success-rgb),var(--tblr-text-opacity))!important}.text-info{--tblr-text-opacity: 1;color:rgba(var(--tblr-info-rgb),var(--tblr-text-opacity))!important}.text-warning{--tblr-text-opacity: 1;color:rgba(var(--tblr-warning-rgb),var(--tblr-text-opacity))!important}.text-danger{--tblr-text-opacity: 1;color:rgba(var(--tblr-danger-rgb),var(--tblr-text-opacity))!important}.text-light{--tblr-text-opacity: 1;color:rgba(var(--tblr-light-rgb),var(--tblr-text-opacity))!important}.text-dark{--tblr-text-opacity: 1;color:rgba(var(--tblr-dark-rgb),var(--tblr-text-opacity))!important}.text-muted{--tblr-text-opacity: 1;color:var(--tblr-secondary-color)!important}.text-blue{--tblr-text-opacity: 1;color:rgba(var(--tblr-blue-rgb),var(--tblr-text-opacity))!important}.text-azure{--tblr-text-opacity: 1;color:rgba(var(--tblr-azure-rgb),var(--tblr-text-opacity))!important}.text-indigo{--tblr-text-opacity: 1;color:rgba(var(--tblr-indigo-rgb),var(--tblr-text-opacity))!important}.text-purple{--tblr-text-opacity: 1;color:rgba(var(--tblr-purple-rgb),var(--tblr-text-opacity))!important}.text-pink{--tblr-text-opacity: 1;color:rgba(var(--tblr-pink-rgb),var(--tblr-text-opacity))!important}.text-red{--tblr-text-opacity: 1;color:rgba(var(--tblr-red-rgb),var(--tblr-text-opacity))!important}.text-orange{--tblr-text-opacity: 1;color:rgba(var(--tblr-orange-rgb),var(--tblr-text-opacity))!important}.text-yellow{--tblr-text-opacity: 1;color:rgba(var(--tblr-yellow-rgb),var(--tblr-text-opacity))!important}.text-lime{--tblr-text-opacity: 1;color:rgba(var(--tblr-lime-rgb),var(--tblr-text-opacity))!important}.text-green{--tblr-text-opacity: 1;color:rgba(var(--tblr-green-rgb),var(--tblr-text-opacity))!important}.text-teal{--tblr-text-opacity: 1;color:rgba(var(--tblr-teal-rgb),var(--tblr-text-opacity))!important}.text-cyan{--tblr-text-opacity: 1;color:rgba(var(--tblr-cyan-rgb),var(--tblr-text-opacity))!important}.text-black{--tblr-text-opacity: 1;color:rgba(var(--tblr-black-rgb),var(--tblr-text-opacity))!important}.text-white{--tblr-text-opacity: 1;color:rgba(var(--tblr-white-rgb),var(--tblr-text-opacity))!important}.text-body{--tblr-text-opacity: 1;color:rgba(var(--tblr-body-color-rgb),var(--tblr-text-opacity))!important}.text-black-50{--tblr-text-opacity: 1;color:#00000080!important}.text-white-50{--tblr-text-opacity: 1;color:#ffffff80!important}.text-body-secondary{--tblr-text-opacity: 1;color:var(--tblr-secondary-color)!important}.text-body-tertiary{--tblr-text-opacity: 1;color:var(--tblr-tertiary-color)!important}.text-body-emphasis{--tblr-text-opacity: 1;color:var(--tblr-emphasis-color)!important}.text-reset{--tblr-text-opacity: 1;color:inherit!important}.text-opacity-25{--tblr-text-opacity: .25}.text-opacity-50{--tblr-text-opacity: .5}.text-opacity-75{--tblr-text-opacity: .75}.text-opacity-100{--tblr-text-opacity: 1}.text-primary-emphasis{color:var(--tblr-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--tblr-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--tblr-success-text-emphasis)!important}.text-info-emphasis{color:var(--tblr-info-text-emphasis)!important}.text-warning-emphasis{color:var(--tblr-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--tblr-danger-text-emphasis)!important}.text-light-emphasis{color:var(--tblr-light-text-emphasis)!important}.text-dark-emphasis{color:var(--tblr-dark-text-emphasis)!important}.link-opacity-10,.link-opacity-10-hover:hover{--tblr-link-opacity: .1}.link-opacity-25,.link-opacity-25-hover:hover{--tblr-link-opacity: .25}.link-opacity-50,.link-opacity-50-hover:hover{--tblr-link-opacity: .5}.link-opacity-75,.link-opacity-75-hover:hover{--tblr-link-opacity: .75}.link-opacity-100,.link-opacity-100-hover:hover{--tblr-link-opacity: 1}.link-offset-1,.link-offset-1-hover:hover{text-underline-offset:.125em!important}.link-offset-2,.link-offset-2-hover:hover{text-underline-offset:.25em!important}.link-offset-3,.link-offset-3-hover:hover{text-underline-offset:.375em!important}.link-underline-primary{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-primary-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-secondary{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-secondary-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-success{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-success-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-info{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-info-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-warning{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-warning-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-danger{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-danger-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-light{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-light-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-dark{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-dark-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-muted{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-muted-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-blue{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-blue-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-azure{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-azure-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-indigo{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-indigo-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-purple{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-purple-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-pink{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-pink-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-red{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-red-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-orange{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-orange-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-yellow{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-yellow-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-lime{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-lime-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-green{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-green-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-teal{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-teal-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-cyan{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-cyan-rgb),var(--tblr-link-underline-opacity))!important}.link-underline{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-link-color-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-underline-opacity-0,.link-underline-opacity-0-hover:hover{--tblr-link-underline-opacity: 0}.link-underline-opacity-10,.link-underline-opacity-10-hover:hover{--tblr-link-underline-opacity: .1}.link-underline-opacity-25,.link-underline-opacity-25-hover:hover{--tblr-link-underline-opacity: .25}.link-underline-opacity-50,.link-underline-opacity-50-hover:hover{--tblr-link-underline-opacity: .5}.link-underline-opacity-75,.link-underline-opacity-75-hover:hover{--tblr-link-underline-opacity: .75}.link-underline-opacity-100,.link-underline-opacity-100-hover:hover{--tblr-link-underline-opacity: 1}.bg-primary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-primary-rgb),var(--tblr-bg-opacity))!important}.bg-secondary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-secondary-rgb),var(--tblr-bg-opacity))!important}.bg-success{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-success-rgb),var(--tblr-bg-opacity))!important}.bg-info{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-info-rgb),var(--tblr-bg-opacity))!important}.bg-warning{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-warning-rgb),var(--tblr-bg-opacity))!important}.bg-danger{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-danger-rgb),var(--tblr-bg-opacity))!important}.bg-light{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-light-rgb),var(--tblr-bg-opacity))!important}.bg-dark{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-dark-rgb),var(--tblr-bg-opacity))!important}.bg-muted{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-muted-rgb),var(--tblr-bg-opacity))!important}.bg-blue{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-blue-rgb),var(--tblr-bg-opacity))!important}.bg-azure{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-azure-rgb),var(--tblr-bg-opacity))!important}.bg-indigo{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-indigo-rgb),var(--tblr-bg-opacity))!important}.bg-purple{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-purple-rgb),var(--tblr-bg-opacity))!important}.bg-pink{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-pink-rgb),var(--tblr-bg-opacity))!important}.bg-red{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-red-rgb),var(--tblr-bg-opacity))!important}.bg-orange{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-orange-rgb),var(--tblr-bg-opacity))!important}.bg-yellow{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-yellow-rgb),var(--tblr-bg-opacity))!important}.bg-lime{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-lime-rgb),var(--tblr-bg-opacity))!important}.bg-green{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-green-rgb),var(--tblr-bg-opacity))!important}.bg-teal{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-teal-rgb),var(--tblr-bg-opacity))!important}.bg-cyan{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-cyan-rgb),var(--tblr-bg-opacity))!important}.bg-black{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-black-rgb),var(--tblr-bg-opacity))!important}.bg-white{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-white-rgb),var(--tblr-bg-opacity))!important}.bg-body{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-body-bg-rgb),var(--tblr-bg-opacity))!important}.bg-transparent{--tblr-bg-opacity: 1;background-color:transparent!important}.bg-body-secondary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-secondary-bg-rgb),var(--tblr-bg-opacity))!important}.bg-body-tertiary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-tertiary-bg-rgb),var(--tblr-bg-opacity))!important}.bg-opacity-10{--tblr-bg-opacity: .1}.bg-opacity-25{--tblr-bg-opacity: .25}.bg-opacity-50{--tblr-bg-opacity: .5}.bg-opacity-75{--tblr-bg-opacity: .75}.bg-opacity-100{--tblr-bg-opacity: 1}.bg-primary-subtle{background-color:var(--tblr-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--tblr-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--tblr-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--tblr-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--tblr-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--tblr-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--tblr-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--tblr-dark-bg-subtle)!important}.bg-gradient{background-image:var(--tblr-gradient)!important}.user-select-all{user-select:all!important}.user-select-auto{user-select:auto!important}.user-select-none{user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--tblr-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--tblr-border-radius-sm)!important}.rounded-2{border-radius:var(--tblr-border-radius)!important}.rounded-3{border-radius:var(--tblr-border-radius-lg)!important}.rounded-4{border-radius:var(--tblr-border-radius-xl)!important}.rounded-5{border-radius:var(--tblr-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--tblr-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--tblr-border-radius)!important;border-top-right-radius:var(--tblr-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--tblr-border-radius-sm)!important;border-top-right-radius:var(--tblr-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--tblr-border-radius)!important;border-top-right-radius:var(--tblr-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--tblr-border-radius-lg)!important;border-top-right-radius:var(--tblr-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--tblr-border-radius-xl)!important;border-top-right-radius:var(--tblr-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--tblr-border-radius-xxl)!important;border-top-right-radius:var(--tblr-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--tblr-border-radius-pill)!important;border-top-right-radius:var(--tblr-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--tblr-border-radius)!important;border-bottom-right-radius:var(--tblr-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--tblr-border-radius-sm)!important;border-bottom-right-radius:var(--tblr-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--tblr-border-radius)!important;border-bottom-right-radius:var(--tblr-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--tblr-border-radius-lg)!important;border-bottom-right-radius:var(--tblr-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--tblr-border-radius-xl)!important;border-bottom-right-radius:var(--tblr-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--tblr-border-radius-xxl)!important;border-bottom-right-radius:var(--tblr-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--tblr-border-radius-pill)!important;border-bottom-right-radius:var(--tblr-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--tblr-border-radius)!important;border-bottom-left-radius:var(--tblr-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--tblr-border-radius-sm)!important;border-bottom-left-radius:var(--tblr-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--tblr-border-radius)!important;border-bottom-left-radius:var(--tblr-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--tblr-border-radius-lg)!important;border-bottom-left-radius:var(--tblr-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--tblr-border-radius-xl)!important;border-bottom-left-radius:var(--tblr-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--tblr-border-radius-xxl)!important;border-bottom-left-radius:var(--tblr-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--tblr-border-radius-pill)!important;border-bottom-left-radius:var(--tblr-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--tblr-border-radius)!important;border-top-left-radius:var(--tblr-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--tblr-border-radius-sm)!important;border-top-left-radius:var(--tblr-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--tblr-border-radius)!important;border-top-left-radius:var(--tblr-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--tblr-border-radius-lg)!important;border-top-left-radius:var(--tblr-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--tblr-border-radius-xl)!important;border-top-left-radius:var(--tblr-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--tblr-border-radius-xxl)!important;border-top-left-radius:var(--tblr-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--tblr-border-radius-pill)!important;border-top-left-radius:var(--tblr-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}.object-contain{object-fit:contain!important}.object-cover{object-fit:cover!important}.object-fill{object-fit:fill!important}.object-scale-down{object-fit:scale-down!important}.object-none{object-fit:none!important}.cursor-auto{cursor:auto!important}.cursor-pointer{cursor:pointer!important}.cursor-move{cursor:move!important}.cursor-not-allowed{cursor:not-allowed!important}.cursor-zoom-in{cursor:zoom-in!important}.cursor-zoom-out{cursor:zoom-out!important}.cursor-default{cursor:default!important}.cursor-none{cursor:none!important}.cursor-help{cursor:help!important}.cursor-progress{cursor:progress!important}.cursor-wait{cursor:wait!important}.cursor-text{cursor:text!important}.cursor-v-text{cursor:vertical-text!important}.cursor-grab{cursor:grab!important}.cursor-grabbing{cursor:grabbing!important}.cursor-crosshair{cursor:crosshair!important}.border-x{border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important;border-right:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-x-wide{border-left:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important;border-right:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-x-0{border-left:0!important;border-right:0!important}.border-y{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important;border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-y-wide{border-top:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important;border-bottom:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-y-0{border-top:0!important;border-bottom:0!important}.columns-2{columns:2!important}.columns-3{columns:3!important}.columns-4{columns:4!important}.bg-pattern-transparent{background:url('data:image/svg+xml;charset=UTF-8,') repeat center/16px 16px!important}.bg-gradient{background:linear-gradient(var(--tblr-gradient-direction, to right),var(--tblr-gradient-stops, var(--tblr-gradient-from, transparent), var(--tblr-gradient-to, transparent))) no-repeat!important}.bg-gradient-to-t{--tblr-gradient-direction: to top !important}.bg-gradient-to-te{--tblr-gradient-direction: to top right !important}.bg-gradient-to-e{--tblr-gradient-direction: to right !important}.bg-gradient-to-be{--tblr-gradient-direction: to bottom right !important}.bg-gradient-to-b{--tblr-gradient-direction: to bottom !important}.bg-gradient-to-bs{--tblr-gradient-direction: to bottom left !important}.bg-gradient-to-s{--tblr-gradient-direction: to left !important}.bg-gradient-to-ts{--tblr-gradient-direction: to top left !important}.table-auto{table-layout:auto!important}.table-fixed{table-layout:fixed!important}@media(min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{object-fit:contain!important}.object-fit-sm-cover{object-fit:cover!important}.object-fit-sm-fill{object-fit:fill!important}.object-fit-sm-scale{object-fit:scale-down!important}.object-fit-sm-none{object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:2rem!important}.m-sm-6{margin:2.5rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:2rem!important;margin-left:2rem!important}.mx-sm-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-sm-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:2rem!important}.mt-sm-6{margin-top:2.5rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:2rem!important}.me-sm-6{margin-right:2.5rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:2rem!important}.mb-sm-6{margin-bottom:2.5rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:2rem!important}.ms-sm-6{margin-left:2.5rem!important}.ms-sm-auto{margin-left:auto!important}.m-sm-n1{margin:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.m-sm-n3{margin:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.m-sm-n5{margin:-2rem!important}.m-sm-n6{margin:-2.5rem!important}.mx-sm-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-sm-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-sm-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-sm-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-sm-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-sm-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-sm-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-sm-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-sm-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-sm-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-sm-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-sm-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-sm-n1{margin-top:-.25rem!important}.mt-sm-n2{margin-top:-.5rem!important}.mt-sm-n3{margin-top:-1rem!important}.mt-sm-n4{margin-top:-1.5rem!important}.mt-sm-n5{margin-top:-2rem!important}.mt-sm-n6{margin-top:-2.5rem!important}.me-sm-n1{margin-right:-.25rem!important}.me-sm-n2{margin-right:-.5rem!important}.me-sm-n3{margin-right:-1rem!important}.me-sm-n4{margin-right:-1.5rem!important}.me-sm-n5{margin-right:-2rem!important}.me-sm-n6{margin-right:-2.5rem!important}.mb-sm-n1{margin-bottom:-.25rem!important}.mb-sm-n2{margin-bottom:-.5rem!important}.mb-sm-n3{margin-bottom:-1rem!important}.mb-sm-n4{margin-bottom:-1.5rem!important}.mb-sm-n5{margin-bottom:-2rem!important}.mb-sm-n6{margin-bottom:-2.5rem!important}.ms-sm-n1{margin-left:-.25rem!important}.ms-sm-n2{margin-left:-.5rem!important}.ms-sm-n3{margin-left:-1rem!important}.ms-sm-n4{margin-left:-1.5rem!important}.ms-sm-n5{margin-left:-2rem!important}.ms-sm-n6{margin-left:-2.5rem!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:2rem!important}.p-sm-6{padding:2.5rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:2rem!important;padding-left:2rem!important}.px-sm-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-sm-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:2rem!important}.pt-sm-6{padding-top:2.5rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:2rem!important}.pe-sm-6{padding-right:2.5rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:2rem!important}.pb-sm-6{padding-bottom:2.5rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:2rem!important}.ps-sm-6{padding-left:2.5rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:2rem!important}.gap-sm-6{gap:2.5rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:2rem!important}.row-gap-sm-6{row-gap:2.5rem!important}.column-gap-sm-0{column-gap:0!important}.column-gap-sm-1{column-gap:.25rem!important}.column-gap-sm-2{column-gap:.5rem!important}.column-gap-sm-3{column-gap:1rem!important}.column-gap-sm-4{column-gap:1.5rem!important}.column-gap-sm-5{column-gap:2rem!important}.column-gap-sm-6{column-gap:2.5rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}.columns-sm-2{columns:2!important}.columns-sm-3{columns:3!important}.columns-sm-4{columns:4!important}}@media(min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{object-fit:contain!important}.object-fit-md-cover{object-fit:cover!important}.object-fit-md-fill{object-fit:fill!important}.object-fit-md-scale{object-fit:scale-down!important}.object-fit-md-none{object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:2rem!important}.m-md-6{margin:2.5rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:2rem!important;margin-left:2rem!important}.mx-md-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-md-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:2rem!important}.mt-md-6{margin-top:2.5rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:2rem!important}.me-md-6{margin-right:2.5rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:2rem!important}.mb-md-6{margin-bottom:2.5rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:2rem!important}.ms-md-6{margin-left:2.5rem!important}.ms-md-auto{margin-left:auto!important}.m-md-n1{margin:-.25rem!important}.m-md-n2{margin:-.5rem!important}.m-md-n3{margin:-1rem!important}.m-md-n4{margin:-1.5rem!important}.m-md-n5{margin:-2rem!important}.m-md-n6{margin:-2.5rem!important}.mx-md-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-md-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-md-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-md-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-md-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-md-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-md-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-md-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-md-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-md-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-md-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-md-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-md-n1{margin-top:-.25rem!important}.mt-md-n2{margin-top:-.5rem!important}.mt-md-n3{margin-top:-1rem!important}.mt-md-n4{margin-top:-1.5rem!important}.mt-md-n5{margin-top:-2rem!important}.mt-md-n6{margin-top:-2.5rem!important}.me-md-n1{margin-right:-.25rem!important}.me-md-n2{margin-right:-.5rem!important}.me-md-n3{margin-right:-1rem!important}.me-md-n4{margin-right:-1.5rem!important}.me-md-n5{margin-right:-2rem!important}.me-md-n6{margin-right:-2.5rem!important}.mb-md-n1{margin-bottom:-.25rem!important}.mb-md-n2{margin-bottom:-.5rem!important}.mb-md-n3{margin-bottom:-1rem!important}.mb-md-n4{margin-bottom:-1.5rem!important}.mb-md-n5{margin-bottom:-2rem!important}.mb-md-n6{margin-bottom:-2.5rem!important}.ms-md-n1{margin-left:-.25rem!important}.ms-md-n2{margin-left:-.5rem!important}.ms-md-n3{margin-left:-1rem!important}.ms-md-n4{margin-left:-1.5rem!important}.ms-md-n5{margin-left:-2rem!important}.ms-md-n6{margin-left:-2.5rem!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:2rem!important}.p-md-6{padding:2.5rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:2rem!important;padding-left:2rem!important}.px-md-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-md-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:2rem!important}.pt-md-6{padding-top:2.5rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:2rem!important}.pe-md-6{padding-right:2.5rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:2rem!important}.pb-md-6{padding-bottom:2.5rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:2rem!important}.ps-md-6{padding-left:2.5rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:2rem!important}.gap-md-6{gap:2.5rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:2rem!important}.row-gap-md-6{row-gap:2.5rem!important}.column-gap-md-0{column-gap:0!important}.column-gap-md-1{column-gap:.25rem!important}.column-gap-md-2{column-gap:.5rem!important}.column-gap-md-3{column-gap:1rem!important}.column-gap-md-4{column-gap:1.5rem!important}.column-gap-md-5{column-gap:2rem!important}.column-gap-md-6{column-gap:2.5rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}.columns-md-2{columns:2!important}.columns-md-3{columns:3!important}.columns-md-4{columns:4!important}}@media(min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{object-fit:contain!important}.object-fit-lg-cover{object-fit:cover!important}.object-fit-lg-fill{object-fit:fill!important}.object-fit-lg-scale{object-fit:scale-down!important}.object-fit-lg-none{object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:2rem!important}.m-lg-6{margin:2.5rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:2rem!important;margin-left:2rem!important}.mx-lg-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-lg-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:2rem!important}.mt-lg-6{margin-top:2.5rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:2rem!important}.me-lg-6{margin-right:2.5rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:2rem!important}.mb-lg-6{margin-bottom:2.5rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:2rem!important}.ms-lg-6{margin-left:2.5rem!important}.ms-lg-auto{margin-left:auto!important}.m-lg-n1{margin:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.m-lg-n3{margin:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.m-lg-n5{margin:-2rem!important}.m-lg-n6{margin:-2.5rem!important}.mx-lg-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-lg-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-lg-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-lg-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-lg-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-lg-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-lg-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-lg-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-lg-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-lg-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-lg-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-lg-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-lg-n1{margin-top:-.25rem!important}.mt-lg-n2{margin-top:-.5rem!important}.mt-lg-n3{margin-top:-1rem!important}.mt-lg-n4{margin-top:-1.5rem!important}.mt-lg-n5{margin-top:-2rem!important}.mt-lg-n6{margin-top:-2.5rem!important}.me-lg-n1{margin-right:-.25rem!important}.me-lg-n2{margin-right:-.5rem!important}.me-lg-n3{margin-right:-1rem!important}.me-lg-n4{margin-right:-1.5rem!important}.me-lg-n5{margin-right:-2rem!important}.me-lg-n6{margin-right:-2.5rem!important}.mb-lg-n1{margin-bottom:-.25rem!important}.mb-lg-n2{margin-bottom:-.5rem!important}.mb-lg-n3{margin-bottom:-1rem!important}.mb-lg-n4{margin-bottom:-1.5rem!important}.mb-lg-n5{margin-bottom:-2rem!important}.mb-lg-n6{margin-bottom:-2.5rem!important}.ms-lg-n1{margin-left:-.25rem!important}.ms-lg-n2{margin-left:-.5rem!important}.ms-lg-n3{margin-left:-1rem!important}.ms-lg-n4{margin-left:-1.5rem!important}.ms-lg-n5{margin-left:-2rem!important}.ms-lg-n6{margin-left:-2.5rem!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:2rem!important}.p-lg-6{padding:2.5rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:2rem!important;padding-left:2rem!important}.px-lg-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-lg-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:2rem!important}.pt-lg-6{padding-top:2.5rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:2rem!important}.pe-lg-6{padding-right:2.5rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:2rem!important}.pb-lg-6{padding-bottom:2.5rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:2rem!important}.ps-lg-6{padding-left:2.5rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:2rem!important}.gap-lg-6{gap:2.5rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:2rem!important}.row-gap-lg-6{row-gap:2.5rem!important}.column-gap-lg-0{column-gap:0!important}.column-gap-lg-1{column-gap:.25rem!important}.column-gap-lg-2{column-gap:.5rem!important}.column-gap-lg-3{column-gap:1rem!important}.column-gap-lg-4{column-gap:1.5rem!important}.column-gap-lg-5{column-gap:2rem!important}.column-gap-lg-6{column-gap:2.5rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}.columns-lg-2{columns:2!important}.columns-lg-3{columns:3!important}.columns-lg-4{columns:4!important}}@media(min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{object-fit:contain!important}.object-fit-xl-cover{object-fit:cover!important}.object-fit-xl-fill{object-fit:fill!important}.object-fit-xl-scale{object-fit:scale-down!important}.object-fit-xl-none{object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:2rem!important}.m-xl-6{margin:2.5rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:2rem!important;margin-left:2rem!important}.mx-xl-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-xl-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:2rem!important}.mt-xl-6{margin-top:2.5rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:2rem!important}.me-xl-6{margin-right:2.5rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:2rem!important}.mb-xl-6{margin-bottom:2.5rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:2rem!important}.ms-xl-6{margin-left:2.5rem!important}.ms-xl-auto{margin-left:auto!important}.m-xl-n1{margin:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.m-xl-n3{margin:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.m-xl-n5{margin:-2rem!important}.m-xl-n6{margin:-2.5rem!important}.mx-xl-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-xl-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-xl-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-xl-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-xl-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-xl-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-xl-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-xl-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-xl-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-xl-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-xl-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-xl-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-xl-n1{margin-top:-.25rem!important}.mt-xl-n2{margin-top:-.5rem!important}.mt-xl-n3{margin-top:-1rem!important}.mt-xl-n4{margin-top:-1.5rem!important}.mt-xl-n5{margin-top:-2rem!important}.mt-xl-n6{margin-top:-2.5rem!important}.me-xl-n1{margin-right:-.25rem!important}.me-xl-n2{margin-right:-.5rem!important}.me-xl-n3{margin-right:-1rem!important}.me-xl-n4{margin-right:-1.5rem!important}.me-xl-n5{margin-right:-2rem!important}.me-xl-n6{margin-right:-2.5rem!important}.mb-xl-n1{margin-bottom:-.25rem!important}.mb-xl-n2{margin-bottom:-.5rem!important}.mb-xl-n3{margin-bottom:-1rem!important}.mb-xl-n4{margin-bottom:-1.5rem!important}.mb-xl-n5{margin-bottom:-2rem!important}.mb-xl-n6{margin-bottom:-2.5rem!important}.ms-xl-n1{margin-left:-.25rem!important}.ms-xl-n2{margin-left:-.5rem!important}.ms-xl-n3{margin-left:-1rem!important}.ms-xl-n4{margin-left:-1.5rem!important}.ms-xl-n5{margin-left:-2rem!important}.ms-xl-n6{margin-left:-2.5rem!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:2rem!important}.p-xl-6{padding:2.5rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:2rem!important;padding-left:2rem!important}.px-xl-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-xl-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:2rem!important}.pt-xl-6{padding-top:2.5rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:2rem!important}.pe-xl-6{padding-right:2.5rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:2rem!important}.pb-xl-6{padding-bottom:2.5rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:2rem!important}.ps-xl-6{padding-left:2.5rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:2rem!important}.gap-xl-6{gap:2.5rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:2rem!important}.row-gap-xl-6{row-gap:2.5rem!important}.column-gap-xl-0{column-gap:0!important}.column-gap-xl-1{column-gap:.25rem!important}.column-gap-xl-2{column-gap:.5rem!important}.column-gap-xl-3{column-gap:1rem!important}.column-gap-xl-4{column-gap:1.5rem!important}.column-gap-xl-5{column-gap:2rem!important}.column-gap-xl-6{column-gap:2.5rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}.columns-xl-2{columns:2!important}.columns-xl-3{columns:3!important}.columns-xl-4{columns:4!important}}@media(min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{object-fit:contain!important}.object-fit-xxl-cover{object-fit:cover!important}.object-fit-xxl-fill{object-fit:fill!important}.object-fit-xxl-scale{object-fit:scale-down!important}.object-fit-xxl-none{object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:2rem!important}.m-xxl-6{margin:2.5rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:2rem!important;margin-left:2rem!important}.mx-xxl-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-xxl-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:2rem!important}.mt-xxl-6{margin-top:2.5rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:2rem!important}.me-xxl-6{margin-right:2.5rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:2rem!important}.mb-xxl-6{margin-bottom:2.5rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:2rem!important}.ms-xxl-6{margin-left:2.5rem!important}.ms-xxl-auto{margin-left:auto!important}.m-xxl-n1{margin:-.25rem!important}.m-xxl-n2{margin:-.5rem!important}.m-xxl-n3{margin:-1rem!important}.m-xxl-n4{margin:-1.5rem!important}.m-xxl-n5{margin:-2rem!important}.m-xxl-n6{margin:-2.5rem!important}.mx-xxl-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-xxl-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-xxl-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-xxl-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-xxl-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-xxl-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-xxl-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-xxl-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-xxl-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-xxl-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-xxl-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-xxl-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-xxl-n1{margin-top:-.25rem!important}.mt-xxl-n2{margin-top:-.5rem!important}.mt-xxl-n3{margin-top:-1rem!important}.mt-xxl-n4{margin-top:-1.5rem!important}.mt-xxl-n5{margin-top:-2rem!important}.mt-xxl-n6{margin-top:-2.5rem!important}.me-xxl-n1{margin-right:-.25rem!important}.me-xxl-n2{margin-right:-.5rem!important}.me-xxl-n3{margin-right:-1rem!important}.me-xxl-n4{margin-right:-1.5rem!important}.me-xxl-n5{margin-right:-2rem!important}.me-xxl-n6{margin-right:-2.5rem!important}.mb-xxl-n1{margin-bottom:-.25rem!important}.mb-xxl-n2{margin-bottom:-.5rem!important}.mb-xxl-n3{margin-bottom:-1rem!important}.mb-xxl-n4{margin-bottom:-1.5rem!important}.mb-xxl-n5{margin-bottom:-2rem!important}.mb-xxl-n6{margin-bottom:-2.5rem!important}.ms-xxl-n1{margin-left:-.25rem!important}.ms-xxl-n2{margin-left:-.5rem!important}.ms-xxl-n3{margin-left:-1rem!important}.ms-xxl-n4{margin-left:-1.5rem!important}.ms-xxl-n5{margin-left:-2rem!important}.ms-xxl-n6{margin-left:-2.5rem!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:2rem!important}.p-xxl-6{padding:2.5rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:2rem!important;padding-left:2rem!important}.px-xxl-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-xxl-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:2rem!important}.pt-xxl-6{padding-top:2.5rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:2rem!important}.pe-xxl-6{padding-right:2.5rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:2rem!important}.pb-xxl-6{padding-bottom:2.5rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:2rem!important}.ps-xxl-6{padding-left:2.5rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:2rem!important}.gap-xxl-6{gap:2.5rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:2rem!important}.row-gap-xxl-6{row-gap:2.5rem!important}.column-gap-xxl-0{column-gap:0!important}.column-gap-xxl-1{column-gap:.25rem!important}.column-gap-xxl-2{column-gap:.5rem!important}.column-gap-xxl-3{column-gap:1rem!important}.column-gap-xxl-4{column-gap:1.5rem!important}.column-gap-xxl-5{column-gap:2rem!important}.column-gap-xxl-6{column-gap:2.5rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}.columns-xxl-2{columns:2!important}.columns-xxl-3{columns:3!important}.columns-xxl-4{columns:4!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}}:root,:host{--tblr-font-monospace: Roboto Mono;--tblr-font-sans-serif: Inter, system-ui, sans-serif;--tblr-font-serif: Georgia, Times New Roman, times, serif;--tblr-font-comic: Comic Sans MS, Comic Sans, Chalkboard SE, Comic Neue, sans-serif, cursive;--tblr-gray-50: #f9fafb;--tblr-gray-100: #f3f4f6;--tblr-gray-200: #e5e7eb;--tblr-gray-300: #d1d5db;--tblr-gray-400: #9ca3af;--tblr-gray-500: #6b7280;--tblr-gray-600: #4b5563;--tblr-gray-700: #374151;--tblr-gray-800: #1f2937;--tblr-gray-900: #111827;--tblr-gray-950: #030712;--tblr-white: #ffffff;--tblr-black: #000000;--tblr-brand: #00857D;--tblr-primary: #00857D;--tblr-primary-rgb: 0, 133, 125;--tblr-primary-fg: var(--tblr-light);--tblr-primary-darken: rgb(0, 119.7, 112.5);--tblr-primary-darken: color-mix(in oklab, var(--tblr-primary), transparent 20%);--tblr-primary-lt: rgb(229.5, 242.8, 242);--tblr-primary-lt: color-mix(in oklab, var(--tblr-primary) 10%, transparent);--tblr-primary-200: color-mix(in oklab, var(--tblr-primary) 20%, transparent);--tblr-primary-lt-rgb: 230, 243, 242;--tblr-secondary: #6b7280;--tblr-secondary-rgb: 107, 114, 128;--tblr-secondary-fg: var(--tblr-light);--tblr-secondary-darken: rgb(96.3, 102.6, 115.2);--tblr-secondary-darken: color-mix(in oklab, var(--tblr-secondary), transparent 20%);--tblr-secondary-lt: rgb(240.2, 240.9, 242.3);--tblr-secondary-lt: color-mix(in oklab, var(--tblr-secondary) 10%, transparent);--tblr-secondary-200: color-mix(in oklab, var(--tblr-secondary) 20%, transparent);--tblr-secondary-lt-rgb: 240, 241, 242;--tblr-success: #2fb344;--tblr-success-rgb: 47, 179, 68;--tblr-success-fg: var(--tblr-light);--tblr-success-darken: rgb(42.3, 161.1, 61.2);--tblr-success-darken: color-mix(in oklab, var(--tblr-success), transparent 20%);--tblr-success-lt: rgb(234.2, 247.4, 236.3);--tblr-success-lt: color-mix(in oklab, var(--tblr-success) 10%, transparent);--tblr-success-200: color-mix(in oklab, var(--tblr-success) 20%, transparent);--tblr-success-lt-rgb: 234, 247, 236;--tblr-info: #4299e1;--tblr-info-rgb: 66, 153, 225;--tblr-info-fg: var(--tblr-light);--tblr-info-darken: rgb(59.4, 137.7, 202.5);--tblr-info-darken: color-mix(in oklab, var(--tblr-info), transparent 20%);--tblr-info-lt: rgb(236.1, 244.8, 252);--tblr-info-lt: color-mix(in oklab, var(--tblr-info) 10%, transparent);--tblr-info-200: color-mix(in oklab, var(--tblr-info) 20%, transparent);--tblr-info-lt-rgb: 236, 245, 252;--tblr-warning: #f59f00;--tblr-warning-rgb: 245, 159, 0;--tblr-warning-fg: var(--tblr-light);--tblr-warning-darken: rgb(220.5, 143.1, 0);--tblr-warning-darken: color-mix(in oklab, var(--tblr-warning), transparent 20%);--tblr-warning-lt: rgb(254, 245.4, 229.5);--tblr-warning-lt: color-mix(in oklab, var(--tblr-warning) 10%, transparent);--tblr-warning-200: color-mix(in oklab, var(--tblr-warning) 20%, transparent);--tblr-warning-lt-rgb: 254, 245, 230;--tblr-danger: #d63939;--tblr-danger-rgb: 214, 57, 57;--tblr-danger-fg: var(--tblr-light);--tblr-danger-darken: rgb(192.6, 51.3, 51.3);--tblr-danger-darken: color-mix(in oklab, var(--tblr-danger), transparent 20%);--tblr-danger-lt: rgb(250.9, 235.2, 235.2);--tblr-danger-lt: color-mix(in oklab, var(--tblr-danger) 10%, transparent);--tblr-danger-200: color-mix(in oklab, var(--tblr-danger) 20%, transparent);--tblr-danger-lt-rgb: 251, 235, 235;--tblr-light: #f9fafb;--tblr-light-rgb: 249, 250, 251;--tblr-light-fg: var(--tblr-dark);--tblr-light-darken: rgb(224.1, 225, 225.9);--tblr-light-darken: color-mix(in oklab, var(--tblr-light), transparent 20%);--tblr-light-lt: rgb(254.4, 254.5, 254.6);--tblr-light-lt: color-mix(in oklab, var(--tblr-light) 10%, transparent);--tblr-light-200: color-mix(in oklab, var(--tblr-light) 20%, transparent);--tblr-light-lt-rgb: 254, 255, 255;--tblr-dark: #1f2937;--tblr-dark-rgb: 31, 41, 55;--tblr-dark-fg: var(--tblr-light);--tblr-dark-darken: rgb(27.9, 36.9, 49.5);--tblr-dark-darken: color-mix(in oklab, var(--tblr-dark), transparent 20%);--tblr-dark-lt: rgb(232.6, 233.6, 235);--tblr-dark-lt: color-mix(in oklab, var(--tblr-dark) 10%, transparent);--tblr-dark-200: color-mix(in oklab, var(--tblr-dark) 20%, transparent);--tblr-dark-lt-rgb: 233, 234, 235;--tblr-muted: #6b7280;--tblr-muted-rgb: 107, 114, 128;--tblr-muted-fg: var(--tblr-light);--tblr-muted-darken: rgb(96.3, 102.6, 115.2);--tblr-muted-darken: color-mix(in oklab, var(--tblr-muted), transparent 20%);--tblr-muted-lt: rgb(240.2, 240.9, 242.3);--tblr-muted-lt: color-mix(in oklab, var(--tblr-muted) 10%, transparent);--tblr-muted-200: color-mix(in oklab, var(--tblr-muted) 20%, transparent);--tblr-muted-lt-rgb: 240, 241, 242;--tblr-blue: #066fd1;--tblr-blue-rgb: 6, 111, 209;--tblr-blue-fg: var(--tblr-light);--tblr-blue-darken: rgb(5.4, 99.9, 188.1);--tblr-blue-darken: color-mix(in oklab, var(--tblr-blue), transparent 20%);--tblr-blue-lt: rgb(230.1, 240.6, 250.4);--tblr-blue-lt: color-mix(in oklab, var(--tblr-blue) 10%, transparent);--tblr-blue-200: color-mix(in oklab, var(--tblr-blue) 20%, transparent);--tblr-blue-lt-rgb: 230, 241, 250;--tblr-azure: #4299e1;--tblr-azure-rgb: 66, 153, 225;--tblr-azure-fg: var(--tblr-light);--tblr-azure-darken: rgb(59.4, 137.7, 202.5);--tblr-azure-darken: color-mix(in oklab, var(--tblr-azure), transparent 20%);--tblr-azure-lt: rgb(236.1, 244.8, 252);--tblr-azure-lt: color-mix(in oklab, var(--tblr-azure) 10%, transparent);--tblr-azure-200: color-mix(in oklab, var(--tblr-azure) 20%, transparent);--tblr-azure-lt-rgb: 236, 245, 252;--tblr-indigo: #4263eb;--tblr-indigo-rgb: 66, 99, 235;--tblr-indigo-fg: var(--tblr-light);--tblr-indigo-darken: rgb(59.4, 89.1, 211.5);--tblr-indigo-darken: color-mix(in oklab, var(--tblr-indigo), transparent 20%);--tblr-indigo-lt: rgb(236.1, 239.4, 253);--tblr-indigo-lt: color-mix(in oklab, var(--tblr-indigo) 10%, transparent);--tblr-indigo-200: color-mix(in oklab, var(--tblr-indigo) 20%, transparent);--tblr-indigo-lt-rgb: 236, 239, 253;--tblr-purple: #ae3ec9;--tblr-purple-rgb: 174, 62, 201;--tblr-purple-fg: var(--tblr-light);--tblr-purple-darken: rgb(156.6, 55.8, 180.9);--tblr-purple-darken: color-mix(in oklab, var(--tblr-purple), transparent 20%);--tblr-purple-lt: rgb(246.9, 235.7, 249.6);--tblr-purple-lt: color-mix(in oklab, var(--tblr-purple) 10%, transparent);--tblr-purple-200: color-mix(in oklab, var(--tblr-purple) 20%, transparent);--tblr-purple-lt-rgb: 247, 236, 250;--tblr-pink: #d6336c;--tblr-pink-rgb: 214, 51, 108;--tblr-pink-fg: var(--tblr-light);--tblr-pink-darken: rgb(192.6, 45.9, 97.2);--tblr-pink-darken: color-mix(in oklab, var(--tblr-pink), transparent 20%);--tblr-pink-lt: rgb(250.9, 234.6, 240.3);--tblr-pink-lt: color-mix(in oklab, var(--tblr-pink) 10%, transparent);--tblr-pink-200: color-mix(in oklab, var(--tblr-pink) 20%, transparent);--tblr-pink-lt-rgb: 251, 235, 240;--tblr-red: #d63939;--tblr-red-rgb: 214, 57, 57;--tblr-red-fg: var(--tblr-light);--tblr-red-darken: rgb(192.6, 51.3, 51.3);--tblr-red-darken: color-mix(in oklab, var(--tblr-red), transparent 20%);--tblr-red-lt: rgb(250.9, 235.2, 235.2);--tblr-red-lt: color-mix(in oklab, var(--tblr-red) 10%, transparent);--tblr-red-200: color-mix(in oklab, var(--tblr-red) 20%, transparent);--tblr-red-lt-rgb: 251, 235, 235;--tblr-orange: #f76707;--tblr-orange-rgb: 247, 103, 7;--tblr-orange-fg: var(--tblr-light);--tblr-orange-darken: rgb(222.3, 92.7, 6.3);--tblr-orange-darken: color-mix(in oklab, var(--tblr-orange), transparent 20%);--tblr-orange-lt: rgb(254.2, 239.8, 230.2);--tblr-orange-lt: color-mix(in oklab, var(--tblr-orange) 10%, transparent);--tblr-orange-200: color-mix(in oklab, var(--tblr-orange) 20%, transparent);--tblr-orange-lt-rgb: 254, 240, 230;--tblr-yellow: #f59f00;--tblr-yellow-rgb: 245, 159, 0;--tblr-yellow-fg: var(--tblr-light);--tblr-yellow-darken: rgb(220.5, 143.1, 0);--tblr-yellow-darken: color-mix(in oklab, var(--tblr-yellow), transparent 20%);--tblr-yellow-lt: rgb(254, 245.4, 229.5);--tblr-yellow-lt: color-mix(in oklab, var(--tblr-yellow) 10%, transparent);--tblr-yellow-200: color-mix(in oklab, var(--tblr-yellow) 20%, transparent);--tblr-yellow-lt-rgb: 254, 245, 230;--tblr-lime: #74b816;--tblr-lime-rgb: 116, 184, 22;--tblr-lime-fg: var(--tblr-light);--tblr-lime-darken: rgb(104.4, 165.6, 19.8);--tblr-lime-darken: color-mix(in oklab, var(--tblr-lime), transparent 20%);--tblr-lime-lt: rgb(241.1, 247.9, 231.7);--tblr-lime-lt: color-mix(in oklab, var(--tblr-lime) 10%, transparent);--tblr-lime-200: color-mix(in oklab, var(--tblr-lime) 20%, transparent);--tblr-lime-lt-rgb: 241, 248, 232;--tblr-green: #2fb344;--tblr-green-rgb: 47, 179, 68;--tblr-green-fg: var(--tblr-light);--tblr-green-darken: rgb(42.3, 161.1, 61.2);--tblr-green-darken: color-mix(in oklab, var(--tblr-green), transparent 20%);--tblr-green-lt: rgb(234.2, 247.4, 236.3);--tblr-green-lt: color-mix(in oklab, var(--tblr-green) 10%, transparent);--tblr-green-200: color-mix(in oklab, var(--tblr-green) 20%, transparent);--tblr-green-lt-rgb: 234, 247, 236;--tblr-teal: #0ca678;--tblr-teal-rgb: 12, 166, 120;--tblr-teal-fg: var(--tblr-light);--tblr-teal-darken: rgb(10.8, 149.4, 108);--tblr-teal-darken: color-mix(in oklab, var(--tblr-teal), transparent 20%);--tblr-teal-lt: rgb(230.7, 246.1, 241.5);--tblr-teal-lt: color-mix(in oklab, var(--tblr-teal) 10%, transparent);--tblr-teal-200: color-mix(in oklab, var(--tblr-teal) 20%, transparent);--tblr-teal-lt-rgb: 231, 246, 242;--tblr-cyan: #17a2b8;--tblr-cyan-rgb: 23, 162, 184;--tblr-cyan-fg: var(--tblr-light);--tblr-cyan-darken: rgb(20.7, 145.8, 165.6);--tblr-cyan-darken: color-mix(in oklab, var(--tblr-cyan), transparent 20%);--tblr-cyan-lt: rgb(231.8, 245.7, 247.9);--tblr-cyan-lt: color-mix(in oklab, var(--tblr-cyan) 10%, transparent);--tblr-cyan-200: color-mix(in oklab, var(--tblr-cyan) 20%, transparent);--tblr-cyan-lt-rgb: 232, 246, 248;--tblr-x: #000000;--tblr-x-rgb: 0, 0, 0;--tblr-x-fg: var(--tblr-light);--tblr-x-darken: black;--tblr-x-darken: color-mix(in oklab, var(--tblr-x), transparent 20%);--tblr-x-lt: rgb(229.5, 229.5, 229.5);--tblr-x-lt: color-mix(in oklab, var(--tblr-x) 10%, transparent);--tblr-x-200: color-mix(in oklab, var(--tblr-x) 20%, transparent);--tblr-x-lt-rgb: 230, 230, 230;--tblr-facebook: #1877f2;--tblr-facebook-rgb: 24, 119, 242;--tblr-facebook-fg: var(--tblr-light);--tblr-facebook-darken: rgb(21.6, 107.1, 217.8);--tblr-facebook-darken: color-mix(in oklab, var(--tblr-facebook), transparent 20%);--tblr-facebook-lt: rgb(231.9, 241.4, 253.7);--tblr-facebook-lt: color-mix(in oklab, var(--tblr-facebook) 10%, transparent);--tblr-facebook-200: color-mix(in oklab, var(--tblr-facebook) 20%, transparent);--tblr-facebook-lt-rgb: 232, 241, 254;--tblr-twitter: #1da1f2;--tblr-twitter-rgb: 29, 161, 242;--tblr-twitter-fg: var(--tblr-light);--tblr-twitter-darken: rgb(26.1, 144.9, 217.8);--tblr-twitter-darken: color-mix(in oklab, var(--tblr-twitter), transparent 20%);--tblr-twitter-lt: rgb(232.4, 245.6, 253.7);--tblr-twitter-lt: color-mix(in oklab, var(--tblr-twitter) 10%, transparent);--tblr-twitter-200: color-mix(in oklab, var(--tblr-twitter) 20%, transparent);--tblr-twitter-lt-rgb: 232, 246, 254;--tblr-linkedin: #0a66c2;--tblr-linkedin-rgb: 10, 102, 194;--tblr-linkedin-fg: var(--tblr-light);--tblr-linkedin-darken: rgb(9, 91.8, 174.6);--tblr-linkedin-darken: color-mix(in oklab, var(--tblr-linkedin), transparent 20%);--tblr-linkedin-lt: rgb(230.5, 239.7, 248.9);--tblr-linkedin-lt: color-mix(in oklab, var(--tblr-linkedin) 10%, transparent);--tblr-linkedin-200: color-mix(in oklab, var(--tblr-linkedin) 20%, transparent);--tblr-linkedin-lt-rgb: 231, 240, 249;--tblr-google: #dc4e41;--tblr-google-rgb: 220, 78, 65;--tblr-google-fg: var(--tblr-light);--tblr-google-darken: rgb(198, 70.2, 58.5);--tblr-google-darken: color-mix(in oklab, var(--tblr-google), transparent 20%);--tblr-google-lt: rgb(251.5, 237.3, 236);--tblr-google-lt: color-mix(in oklab, var(--tblr-google) 10%, transparent);--tblr-google-200: color-mix(in oklab, var(--tblr-google) 20%, transparent);--tblr-google-lt-rgb: 252, 237, 236;--tblr-youtube: #ff0000;--tblr-youtube-rgb: 255, 0, 0;--tblr-youtube-fg: var(--tblr-light);--tblr-youtube-darken: rgb(229.5, 0, 0);--tblr-youtube-darken: color-mix(in oklab, var(--tblr-youtube), transparent 20%);--tblr-youtube-lt: rgb(255, 229.5, 229.5);--tblr-youtube-lt: color-mix(in oklab, var(--tblr-youtube) 10%, transparent);--tblr-youtube-200: color-mix(in oklab, var(--tblr-youtube) 20%, transparent);--tblr-youtube-lt-rgb: 255, 230, 230;--tblr-vimeo: #1ab7ea;--tblr-vimeo-rgb: 26, 183, 234;--tblr-vimeo-fg: var(--tblr-light);--tblr-vimeo-darken: rgb(23.4, 164.7, 210.6);--tblr-vimeo-darken: color-mix(in oklab, var(--tblr-vimeo), transparent 20%);--tblr-vimeo-lt: rgb(232.1, 247.8, 252.9);--tblr-vimeo-lt: color-mix(in oklab, var(--tblr-vimeo) 10%, transparent);--tblr-vimeo-200: color-mix(in oklab, var(--tblr-vimeo) 20%, transparent);--tblr-vimeo-lt-rgb: 232, 248, 253;--tblr-dribbble: #ea4c89;--tblr-dribbble-rgb: 234, 76, 137;--tblr-dribbble-fg: var(--tblr-light);--tblr-dribbble-darken: rgb(210.6, 68.4, 123.3);--tblr-dribbble-darken: color-mix(in oklab, var(--tblr-dribbble), transparent 20%);--tblr-dribbble-lt: rgb(252.9, 237.1, 243.2);--tblr-dribbble-lt: color-mix(in oklab, var(--tblr-dribbble) 10%, transparent);--tblr-dribbble-200: color-mix(in oklab, var(--tblr-dribbble) 20%, transparent);--tblr-dribbble-lt-rgb: 253, 237, 243;--tblr-github: #181717;--tblr-github-rgb: 24, 23, 23;--tblr-github-fg: var(--tblr-light);--tblr-github-darken: rgb(21.6, 20.7, 20.7);--tblr-github-darken: color-mix(in oklab, var(--tblr-github), transparent 20%);--tblr-github-lt: rgb(231.9, 231.8, 231.8);--tblr-github-lt: color-mix(in oklab, var(--tblr-github) 10%, transparent);--tblr-github-200: color-mix(in oklab, var(--tblr-github) 20%, transparent);--tblr-github-lt-rgb: 232, 232, 232;--tblr-instagram: #e4405f;--tblr-instagram-rgb: 228, 64, 95;--tblr-instagram-fg: var(--tblr-light);--tblr-instagram-darken: rgb(205.2, 57.6, 85.5);--tblr-instagram-darken: color-mix(in oklab, var(--tblr-instagram), transparent 20%);--tblr-instagram-lt: rgb(252.3, 235.9, 239);--tblr-instagram-lt: color-mix(in oklab, var(--tblr-instagram) 10%, transparent);--tblr-instagram-200: color-mix(in oklab, var(--tblr-instagram) 20%, transparent);--tblr-instagram-lt-rgb: 252, 236, 239;--tblr-pinterest: #bd081c;--tblr-pinterest-rgb: 189, 8, 28;--tblr-pinterest-fg: var(--tblr-light);--tblr-pinterest-darken: rgb(170.1, 7.2, 25.2);--tblr-pinterest-darken: color-mix(in oklab, var(--tblr-pinterest), transparent 20%);--tblr-pinterest-lt: rgb(248.4, 230.3, 232.3);--tblr-pinterest-lt: color-mix(in oklab, var(--tblr-pinterest) 10%, transparent);--tblr-pinterest-200: color-mix(in oklab, var(--tblr-pinterest) 20%, transparent);--tblr-pinterest-lt-rgb: 248, 230, 232;--tblr-vk: #6383a8;--tblr-vk-rgb: 99, 131, 168;--tblr-vk-fg: var(--tblr-light);--tblr-vk-darken: rgb(89.1, 117.9, 151.2);--tblr-vk-darken: color-mix(in oklab, var(--tblr-vk), transparent 20%);--tblr-vk-lt: rgb(239.4, 242.6, 246.3);--tblr-vk-lt: color-mix(in oklab, var(--tblr-vk) 10%, transparent);--tblr-vk-200: color-mix(in oklab, var(--tblr-vk) 20%, transparent);--tblr-vk-lt-rgb: 239, 243, 246;--tblr-rss: #ffa500;--tblr-rss-rgb: 255, 165, 0;--tblr-rss-fg: var(--tblr-light);--tblr-rss-darken: rgb(229.5, 148.5, 0);--tblr-rss-darken: color-mix(in oklab, var(--tblr-rss), transparent 20%);--tblr-rss-lt: rgb(255, 246, 229.5);--tblr-rss-lt: color-mix(in oklab, var(--tblr-rss) 10%, transparent);--tblr-rss-200: color-mix(in oklab, var(--tblr-rss) 20%, transparent);--tblr-rss-lt-rgb: 255, 246, 230;--tblr-flickr: #0063dc;--tblr-flickr-rgb: 0, 99, 220;--tblr-flickr-fg: var(--tblr-light);--tblr-flickr-darken: rgb(0, 89.1, 198);--tblr-flickr-darken: color-mix(in oklab, var(--tblr-flickr), transparent 20%);--tblr-flickr-lt: rgb(229.5, 239.4, 251.5);--tblr-flickr-lt: color-mix(in oklab, var(--tblr-flickr) 10%, transparent);--tblr-flickr-200: color-mix(in oklab, var(--tblr-flickr) 20%, transparent);--tblr-flickr-lt-rgb: 230, 239, 252;--tblr-bitbucket: #0052cc;--tblr-bitbucket-rgb: 0, 82, 204;--tblr-bitbucket-fg: var(--tblr-light);--tblr-bitbucket-darken: rgb(0, 73.8, 183.6);--tblr-bitbucket-darken: color-mix(in oklab, var(--tblr-bitbucket), transparent 20%);--tblr-bitbucket-lt: rgb(229.5, 237.7, 249.9);--tblr-bitbucket-lt: color-mix(in oklab, var(--tblr-bitbucket) 10%, transparent);--tblr-bitbucket-200: color-mix(in oklab, var(--tblr-bitbucket) 20%, transparent);--tblr-bitbucket-lt-rgb: 230, 238, 250;--tblr-tabler: #066fd1;--tblr-tabler-rgb: 6, 111, 209;--tblr-tabler-fg: var(--tblr-light);--tblr-tabler-darken: rgb(5.4, 99.9, 188.1);--tblr-tabler-darken: color-mix(in oklab, var(--tblr-tabler), transparent 20%);--tblr-tabler-lt: rgb(230.1, 240.6, 250.4);--tblr-tabler-lt: color-mix(in oklab, var(--tblr-tabler) 10%, transparent);--tblr-tabler-200: color-mix(in oklab, var(--tblr-tabler) 20%, transparent);--tblr-tabler-lt-rgb: 230, 241, 250;--tblr-gray-50-fg: var(--tblr-body-color);--tblr-gray-100-fg: var(--tblr-body-color);--tblr-gray-200-fg: var(--tblr-body-color);--tblr-gray-300-fg: var(--tblr-body-color);--tblr-gray-400-fg: var(--tblr-white);--tblr-gray-500-fg: var(--tblr-white);--tblr-gray-600-fg: var(--tblr-white);--tblr-gray-700-fg: var(--tblr-white);--tblr-gray-800-fg: var(--tblr-white);--tblr-gray-900-fg: var(--tblr-white);--tblr-gray-950-fg: var(--tblr-white);--tblr-spacer-0: 0;--tblr-spacer-1: .25rem;--tblr-spacer-2: .5rem;--tblr-spacer-3: 1rem;--tblr-spacer-4: 1.5rem;--tblr-spacer-5: 2rem;--tblr-spacer-6: 2.5rem;--tblr-font-weight-light: 300;--tblr-font-weight-normal: 400;--tblr-font-weight-medium: 500;--tblr-font-weight-bold: 600;--tblr-font-weight-black: 700;--tblr-font-weight-headings: var(--tblr-font-weight-bold);--tblr-font-size-h1: 1.5rem;--tblr-font-size-h2: 1.25rem;--tblr-font-size-h3: 1rem;--tblr-font-size-h4: .875rem;--tblr-font-size-h5: .75rem;--tblr-font-size-h6: .625rem;--tblr-line-height-h1: 2rem;--tblr-line-height-h2: 1.75rem;--tblr-line-height-h3: 1.5rem;--tblr-line-height-h4: 1.25rem;--tblr-line-height-h5: 1rem;--tblr-line-height-h6: 1rem;--tblr-shadow: rgba(var(--tblr-body-color-rgb), .04) 0 2px 4px 0;--tblr-shadow-border: inset 0 0 0 1px var(--tblr-border-color-translucent);--tblr-shadow-transparent: 0 0 0 0 transparent;--tblr-shadow-input: 0 1px 1px rgba(var(--tblr-body-color-rgb), .06);--tblr-shadow-card: 0 0 4px rgba(var(--tblr-body-color-rgb), .04);--tblr-shadow-card-hover: rgba(var(--tblr-body-color-rgb), .16) 0 2px 16px 0;--tblr-shadow-dropdown: 0 16px 24px 2px rgba(0, 0, 0, .07), 0 6px 30px 5px rgba(0, 0, 0, .06), 0 8px 10px -5px rgba(0, 0, 0, .1);--tblr-border-radius-scale: 1;--tblr-border-radius-0: calc(0 * var(--tblr-border-radius-scale, 1));--tblr-border-radius-sm: calc(4px * var(--tblr-border-radius-scale, 1));--tblr-border-radius-md: calc(6px * var(--tblr-border-radius-scale, 1));--tblr-border-radius-lg: calc(8px * var(--tblr-border-radius-scale, 1));--tblr-border-radius-pill: calc(100rem * var(--tblr-border-radius-scale, 1));--tblr-border-radius: var(--tblr-border-radius-md);--tblr-backdrop-opacity: 24%;--tblr-backdrop-bg: var(--tblr-bg-surface-dark);--tblr-backdrop-bg-dark: color-mix(in srgb, var(--tblr-color-dark), transparent var(--tblr-backdrop-opacity));--tblr-backdrop-bg-light: color-mix(in srgb, var(--tblr-color-light), transparent var(--tblr-backdrop-opacity));--tblr-backdrop-blur: 4px;--tblr-backdrop-filter: blur(var(--tblr-backdrop-blur))}:root,:host{font-size:16px;height:100%}@media(min-width:992px){:root,:host{margin-left:calc(100vw - 100%);margin-right:0}}:root,:host,[data-bs-theme=light]{color-scheme:light;--tblr-spacer: var(--tblr-spacer-2);--tblr-bg-surface: var(--tblr-bg-surface-primary);--tblr-bg-surface-primary: var(--tblr-white);--tblr-bg-surface-secondary: var(--tblr-gray-50);--tblr-bg-surface-tertiary: var(--tblr-gray-50);--tblr-bg-surface-dark: var(--tblr-gray-900);--tblr-bg-surface-inverted: var(--tblr-gray-900);--tblr-bg-forms: var(--tblr-bg-surface);--tblr-text-inverted: var(--tblr-gray-100);--tblr-body-color: var(--tblr-gray-700);--tblr-body-bg: var(--tblr-bg-surface-secondary);--tblr-link-color: var(--tblr-primary);--tblr-link-hover-color: color-mix(in srgb, var(--tblr-primary), #000 20%);--tblr-secondary: var(--tblr-gray-500);--tblr-tertiary: var(--tblr-gray-400);--tblr-border-color: #e5e7eb;--tblr-border-color-translucent: rgba(4, 32, 69, .1);--tblr-border-dark-color: #9ca3af;--tblr-border-dark-color-translucent: rgba(4, 32, 69, .27);--tblr-border-active-color: rgb(169.16, 173.22, 181.34);--tblr-icon-color: var(--tblr-gray-400);--tblr-active-bg: rgba(var(--tblr-primary-rgb), .04);--tblr-disabled-bg: var(--tblr-bg-surface-secondary);--tblr-disabled-color: color-mix(in srgb, var(--tblr-body-color) 40%, transparent);--tblr-code-color: light-dark(var(--tblr-gray-600), var(--tblr-gray-400));--tblr-code-bg: light-dark(var(--tblr-gray-100), var(--tblr-gray-900));--tblr-dark-mode-border-color: rgb(45.7069767442, 60.4511627907, 81.0930232558);--tblr-dark-mode-border-color-translucent: rgba(72, 110, 149, .14);--tblr-dark-mode-border-active-color: rgb(53.0604651163, 70.176744186, 94.1395348837);--tblr-dark-mode-border-dark-color: rgb(38.3534883721, 50.7255813953, 68.0465116279);--tblr-page-padding: var(--tblr-spacer-3);--tblr-page-padding-y: var(--tblr-spacer-4)}@media(max-width:991.98px){:root,:host,[data-bs-theme=light]{--tblr-page-padding: var(--tblr-spacer-2)}}@keyframes pulse{0%{transform:scale(1)}14%{transform:scale(1.25)}28%{transform:scale(1)}42%{transform:scale(1.25)}70%{transform:scale(1)}}@keyframes tada{0%{transform:scaleZ(1)}10%,5%{transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-5deg)}15%,25%,35%,45%{transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,5deg)}20%,30%,40%{transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-5deg)}50%{transform:scaleZ(1)}}@keyframes rotate-360{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes blink{0%{opacity:0}50%{opacity:1}to{opacity:0}}@keyframes shake{0%{transform:scaleX(1)}20%{transform:scale3d(.9,.9,.9) rotate(-5deg)}50%,70%,90%{transform:scale3d(1.25,1.25,1.25) rotate(5deg)}60%,80%{transform:scale3d(1.25,1.25,1.25) rotate(-5deg)}to{transform:scaleX(1)}}body{letter-spacing:0;touch-action:manipulation;text-rendering:optimizeLegibility;font-feature-settings:"liga" 0,"cv03","cv04","cv11";position:relative;min-height:100%;height:100%;padding:0;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media print{body{background:transparent}}*{scrollbar-color:color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent) transparent}*::-webkit-scrollbar{width:1rem;height:1rem;transition:background .3s}@media(prefers-reduced-motion:reduce){*::-webkit-scrollbar{transition:none}}*::-webkit-scrollbar-thumb{border-radius:1rem;border:5px solid transparent;box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent)}*::-webkit-scrollbar-track{background:transparent}*:hover::-webkit-scrollbar-thumb{box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 40%,transparent)}*::-webkit-scrollbar-corner{background:transparent}.layout-fluid .container,.layout-fluid [class^=container-],.layout-fluid [class*=" container-"]{max-width:100%}.layout-boxed{--tblr-theme-boxed-border-radius: 0;--tblr-theme-boxed-width: 1320px}@media(min-width:768px){.layout-boxed{background:#1f2937 linear-gradient(to right,rgba(255,255,255,.1),transparent) fixed;padding:1rem;--tblr-theme-boxed-border-radius: 6px}}.layout-boxed .page{margin:0 auto;max-width:var(--tblr-theme-boxed-width);border-radius:var(--tblr-theme-boxed-border-radius);color:var(--tblr-body-color)}@media(min-width:768px){.layout-boxed .page{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);background:var(--tblr-body-bg)}}.layout-boxed .page>.navbar:first-child{border-top-left-radius:var(--tblr-theme-boxed-border-radius);border-top-right-radius:var(--tblr-theme-boxed-border-radius)}.navbar{--tblr-navbar-bg: var(--tblr-bg-surface);--tblr-navbar-border-width: var(--tblr-border-width);--tblr-navbar-active-border-color: #00857D;--tblr-navbar-active-bg: rgba(0, 0, 0, .2);--tblr-navbar-border-color: var(--tblr-border-color);--tblr-navbar-hover-color: var(--tblr-body-color);align-items:stretch;min-height:3.5rem;box-shadow:inset 0 calc(-1 * var(--tblr-navbar-border-width)) 0 0 var(--tblr-navbar-border-color);background:var(--tblr-navbar-bg);color:var(--tblr-navbar-color)}.navbar-collapse .navbar{flex-grow:1}.navbar.collapsing{min-height:0}.navbar .dropdown-menu{position:absolute;z-index:1030}.navbar .navbar-nav{min-height:3rem}.navbar .navbar-nav .nav-link{position:relative;min-width:2.5rem;min-height:2.5rem;justify-content:center;border-radius:var(--tblr-border-radius)}.navbar .navbar-nav .nav-link .badge{position:absolute;top:.375rem;right:.375rem;transform:translate(50%,-50%)}@media(max-width:575.98px){.navbar-expand-sm .navbar-collapse{flex-direction:column}.navbar-expand-sm .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand-sm .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand-sm .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand-sm .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand-sm .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand-sm .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand-sm .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:576px){.navbar-expand-sm .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand-sm .nav-item.active{position:relative}.navbar-expand-sm .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand-sm .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand-sm.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-sm.navbar-vertical.navbar-right,.navbar-expand-sm.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-sm.navbar-vertical~.navbar,.navbar-expand-sm.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand-sm.navbar-vertical.navbar-right~.navbar,.navbar-expand-sm.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand-sm.navbar-vertical.navbar-end~.navbar,.navbar-expand-sm.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}}@media(max-width:767.98px){.navbar-expand-md .navbar-collapse{flex-direction:column}.navbar-expand-md .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand-md .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand-md .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand-md .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand-md .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand-md .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand-md .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:768px){.navbar-expand-md .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand-md .nav-item.active{position:relative}.navbar-expand-md .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand-md .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand-md.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-md.navbar-vertical.navbar-right,.navbar-expand-md.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-md.navbar-vertical~.navbar,.navbar-expand-md.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand-md.navbar-vertical.navbar-right~.navbar,.navbar-expand-md.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand-md.navbar-vertical.navbar-end~.navbar,.navbar-expand-md.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}}@media(max-width:991.98px){.navbar-expand-lg .navbar-collapse{flex-direction:column}.navbar-expand-lg .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand-lg .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand-lg .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand-lg .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand-lg .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand-lg .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand-lg .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:992px){.navbar-expand-lg .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand-lg .nav-item.active{position:relative}.navbar-expand-lg .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand-lg .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand-lg.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-lg.navbar-vertical.navbar-right,.navbar-expand-lg.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-lg.navbar-vertical~.navbar,.navbar-expand-lg.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand-lg.navbar-vertical.navbar-right~.navbar,.navbar-expand-lg.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand-lg.navbar-vertical.navbar-end~.navbar,.navbar-expand-lg.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}}@media(max-width:1199.98px){.navbar-expand-xl .navbar-collapse{flex-direction:column}.navbar-expand-xl .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand-xl .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand-xl .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand-xl .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand-xl .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand-xl .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand-xl .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:1200px){.navbar-expand-xl .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand-xl .nav-item.active{position:relative}.navbar-expand-xl .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand-xl .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand-xl.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-xl.navbar-vertical.navbar-right,.navbar-expand-xl.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-xl.navbar-vertical~.navbar,.navbar-expand-xl.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand-xl.navbar-vertical.navbar-right~.navbar,.navbar-expand-xl.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand-xl.navbar-vertical.navbar-end~.navbar,.navbar-expand-xl.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}}@media(max-width:1399.98px){.navbar-expand-xxl .navbar-collapse{flex-direction:column}.navbar-expand-xxl .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand-xxl .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand-xxl .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand-xxl .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand-xxl .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand-xxl .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand-xxl .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:1400px){.navbar-expand-xxl .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand-xxl .nav-item.active{position:relative}.navbar-expand-xxl .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand-xxl .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand-xxl.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-xxl.navbar-vertical.navbar-right,.navbar-expand-xxl.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-xxl.navbar-vertical~.navbar,.navbar-expand-xxl.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand-xxl.navbar-vertical.navbar-right~.navbar,.navbar-expand-xxl.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand-xxl.navbar-vertical.navbar-end~.navbar,.navbar-expand-xxl.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}}.navbar-expand .navbar-collapse{flex-direction:column}.navbar-expand .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}.navbar-expand .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand .nav-item.active{position:relative}.navbar-expand .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand.navbar-vertical.navbar-right,.navbar-expand.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand.navbar-vertical~.navbar,.navbar-expand.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand.navbar-vertical.navbar-right~.navbar,.navbar-expand.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand.navbar-vertical.navbar-end~.navbar,.navbar-expand.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}.navbar-brand{display:inline-flex;align-items:center;font-weight:var(--tblr-font-weight-bold);margin:0;line-height:1;gap:.5rem}.navbar-brand-image{height:2rem;width:auto}.navbar-toggler{border:0;width:2rem;height:2rem;position:relative;display:flex;align-items:center;justify-content:center}.navbar-toggler-icon{height:2px;width:1.25em;background:currentColor;border-radius:10px;transition:top .2s .2s,bottom .2s .2s,transform .2s,opacity 0s .2s}@media(prefers-reduced-motion:reduce){.navbar-toggler-icon{transition:none}}.navbar-toggler-icon{position:relative}.navbar-toggler-icon:before,.navbar-toggler-icon:after{content:"";display:block;height:inherit;width:inherit;border-radius:inherit;background:inherit;position:absolute;left:0;transition:inherit}@media(prefers-reduced-motion:reduce){.navbar-toggler-icon:before,.navbar-toggler-icon:after{transition:none}}.navbar-toggler-icon:before{top:-.45em}.navbar-toggler-icon:after{bottom:-.45em}.navbar-toggler[aria-expanded=true] .navbar-toggler-icon{transform:rotate(45deg);transition:top .3s,bottom .3s,transform .3s .3s,opacity 0s .3s}@media(prefers-reduced-motion:reduce){.navbar-toggler[aria-expanded=true] .navbar-toggler-icon{transition:none}}.navbar-toggler[aria-expanded=true] .navbar-toggler-icon:before{top:0;transform:rotate(-90deg)}.navbar-toggler[aria-expanded=true] .navbar-toggler-icon:after{bottom:0;opacity:0}.navbar-transparent{--tblr-navbar-border-color: transparent !important;background:transparent!important}.navbar-nav{--tblr-nav-link-hover-bg: color-mix(in srgb, var(--tblr-nav-link-color) 4%, transparent);margin:0;padding:0;align-items:stretch}.navbar-nav .nav-item{display:flex;flex-direction:column;justify-content:center}.navbar-side{margin:0;display:flex;flex-direction:row;align-items:center;justify-content:space-around}@media(min-width:576px){.navbar-vertical.navbar-expand-sm{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s;overflow-y:scroll;padding:0}}@media(min-width:576px)and (prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand-sm{transition:none}}@media(min-width:576px){.navbar-vertical.navbar-expand-sm.navbar-right,.navbar-vertical.navbar-expand-sm.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand-sm .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand-sm .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand-sm .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand-sm .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand-sm>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand-sm~.page{padding-left:18rem}.navbar-vertical.navbar-expand-sm~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand-sm.navbar-right~.page,.navbar-vertical.navbar-expand-sm.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand-sm .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand-sm .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand-sm .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand-sm .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand-sm .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:768px){.navbar-vertical.navbar-expand-md{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s;overflow-y:scroll;padding:0}}@media(min-width:768px)and (prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand-md{transition:none}}@media(min-width:768px){.navbar-vertical.navbar-expand-md.navbar-right,.navbar-vertical.navbar-expand-md.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand-md .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand-md .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand-md .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand-md .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand-md>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand-md~.page{padding-left:18rem}.navbar-vertical.navbar-expand-md~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand-md.navbar-right~.page,.navbar-vertical.navbar-expand-md.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand-md .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand-md .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand-md .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand-md .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand-md .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:992px){.navbar-vertical.navbar-expand-lg{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s;overflow-y:scroll;padding:0}}@media(min-width:992px)and (prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand-lg{transition:none}}@media(min-width:992px){.navbar-vertical.navbar-expand-lg.navbar-right,.navbar-vertical.navbar-expand-lg.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand-lg .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand-lg .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand-lg .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand-lg .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand-lg>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand-lg~.page{padding-left:18rem}.navbar-vertical.navbar-expand-lg~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand-lg.navbar-right~.page,.navbar-vertical.navbar-expand-lg.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand-lg .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand-lg .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand-lg .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand-lg .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand-lg .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:1200px){.navbar-vertical.navbar-expand-xl{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s;overflow-y:scroll;padding:0}}@media(min-width:1200px)and (prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand-xl{transition:none}}@media(min-width:1200px){.navbar-vertical.navbar-expand-xl.navbar-right,.navbar-vertical.navbar-expand-xl.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand-xl .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand-xl .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand-xl .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand-xl .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand-xl>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand-xl~.page{padding-left:18rem}.navbar-vertical.navbar-expand-xl~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand-xl.navbar-right~.page,.navbar-vertical.navbar-expand-xl.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand-xl .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand-xl .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand-xl .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand-xl .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand-xl .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:1400px){.navbar-vertical.navbar-expand-xxl{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s;overflow-y:scroll;padding:0}}@media(min-width:1400px)and (prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand-xxl{transition:none}}@media(min-width:1400px){.navbar-vertical.navbar-expand-xxl.navbar-right,.navbar-vertical.navbar-expand-xxl.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand-xxl .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand-xxl .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand-xxl .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand-xxl .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand-xxl>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand-xxl~.page{padding-left:18rem}.navbar-vertical.navbar-expand-xxl~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand-xxl.navbar-right~.page,.navbar-vertical.navbar-expand-xxl.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand-xxl .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand-xxl .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand-xxl .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand-xxl .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand-xxl .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}.navbar-vertical.navbar-expand{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s}@media(prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand{transition:none}}.navbar-vertical.navbar-expand{overflow-y:scroll;padding:0}.navbar-vertical.navbar-expand.navbar-right,.navbar-vertical.navbar-expand.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand~.page{padding-left:18rem}.navbar-vertical.navbar-expand~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand.navbar-right~.page,.navbar-vertical.navbar-expand.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}.navbar-overlap:after{content:"";height:9rem;position:absolute;top:100%;left:0;right:0;background:inherit;z-index:-1;box-shadow:inherit}.page{display:flex;flex-direction:column;position:relative;min-height:100%}.page-center{justify-content:center}.page-wrapper{flex:1;display:flex;flex-direction:column}@media print{.page-wrapper{margin:0!important}}.page-wrapper-full .page-body:first-child{margin:0;border-top:0}.page-body{margin-top:var(--tblr-page-padding-y);margin-bottom:var(--tblr-page-padding-y);display:flex;flex-direction:column;flex:1}.page-body-card{background:var(--tblr-bg-surface);border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);padding:var(--tblr-page-padding) 0;margin-bottom:0;flex:1}.page-body~.page-body-card{margin-top:0}.page-cover{background:no-repeat center/cover;min-height:9rem}@media(min-width:768px){.page-cover{min-height:12rem}}@media(min-width:992px){.page-cover{min-height:15rem}}.page-cover-overlay{position:relative}.page-cover-overlay:after{content:"";position:absolute;inset:0;background-image:linear-gradient(180deg,#0000,#0009)}.page-header{display:flex;flex-wrap:wrap;min-height:2.25rem;flex-direction:column;justify-content:center;max-width:100%}.page-wrapper .page-header{margin:var(--tblr-page-padding-y) 0 0}.page-header-border{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);padding:var(--tblr-page-padding-y) 0;margin:0!important;background-color:var(--tblr-bg-surface)}.page-pretitle{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary)}.page-title{margin:0;font-size:var(--tblr-font-size-h2);line-height:var(--tblr-line-height-h2);font-weight:var(--tblr-font-weight-headings);color:inherit;display:flex;align-items:center}.page-title svg{width:1.5rem;height:1.5rem;margin-right:.25rem}.page-title-lg{font-size:1.5rem;line-height:2rem}.page-subtitle{margin-top:.25rem;color:var(--tblr-secondary)}.page-cover{--tblr-page-cover-blur: 20px;--tblr-page-cover-padding: 1rem;min-height:6rem;padding:var(--tblr-page-cover-padding) 0;position:relative;overflow:hidden}.page-cover-img{position:absolute;top:calc(-2 * var(--tblr-page-cover-blur, 0));left:calc(-2 * var(--tblr-page-cover-blur, 0));right:calc(-2 * var(--tblr-page-cover-blur, 0));bottom:calc(-2 * var(--tblr-page-cover-blur, 0));pointer-events:none;filter:blur(var(--tblr-page-cover-blur));object-fit:cover;background-size:cover;background-position:center;z-index:-1}.page-tabs{margin-top:.5rem;position:relative}.page-header-tabs .nav-bordered{border:0}.page-header-tabs+.page-body-card{margin-top:0}.footer{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);background-color:var(--tblr-bg-surface);padding:2rem 0;color:var(--tblr-gray-500);margin-top:auto}.footer-transparent{background-color:transparent;border-top:0}:root:not(.theme-dark):not([data-bs-theme=dark]) .hide-theme-light{display:none!important}:root:not(.theme-dark):not([data-bs-theme=dark]) .img-dark{display:none!important}:root.theme-dark .hide-theme-dark,:root[data-bs-theme=dark] .hide-theme-dark,body[data-bs-theme=dark] [data-bs-theme=light]:root .hide-theme-dark{display:none!important}:root.theme-dark .img-light,:root[data-bs-theme=dark] .img-light,body[data-bs-theme=dark] [data-bs-theme=light]:root .img-light{display:none!important}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{color-scheme:dark;--tblr-body-color: var(--tblr-gray-200);--tblr-secondary: var(--tblr-gray-400);--tblr-body-bg: var(--tblr-gray-900);--tblr-emphasis-color: #ffffff;--tblr-emphasis-color-rgb: 255, 255, 255;--tblr-bg-forms: var(--tblr-gray-900);--tblr-bg-surface: var(--tblr-gray-800);--tblr-bg-surface-inverted: var(--tblr-gray-100);--tblr-bg-surface-secondary: var(--tblr-gray-900);--tblr-bg-surface-tertiary: var(--tblr-gray-800);--tblr-text-inverted: var(--tblr-gray-800);--tblr-link-color: var(--tblr-primary);--tblr-link-hover-color: color-mix(in srgb, var(--tblr-primary), black 20%);--tblr-active-bg: rgb(34.676744186, 45.8627906977, 61.523255814);--tblr-disabled-color: color-mix(in srgb, var(--tblr-body-color) 40%, transparent);--tblr-border-color: var(--tblr-gray-700);--tblr-border-color-translucent: var( --tblr-dark-mode-border-color-translucent );--tblr-border-dark-color: var(--tblr-dark-mode-border-dark-color);--tblr-border-active-color: var( --tblr-dark-mode-border-active-color );--tblr-btn-color: rgb(27.323255814, 36.1372093023, 48.476744186)}[data-bs-theme=dark] .navbar-brand-autodark .navbar-brand-image{filter:brightness(0) invert(1)}.accordion{--tblr-accordion-color: var(--tblr-body-color);--tblr-accordion-border-color: var(--tblr-border-color);--tblr-accordion-border-radius: var(--tblr-border-radius);--tblr-accordion-inner-border-radius: calc(var(--tblr-border-radius) - (var(--tblr-border-width)));--tblr-accordion-padding-x: 1.25rem;--tblr-accordion-gap: 0;--tblr-accordion-active-color: inherit;--tblr-accordion-btn-color: var(--tblr-accordion-color);--tblr-accordion-btn-bg: transparent;--tblr-accordion-btn-toggle-width: 1.25rem;--tblr-accordion-btn-padding-x: var(--tblr-accordion-padding-x);--tblr-accordion-btn-padding-y: 1rem;--tblr-accordion-btn-font-weight: var(--tblr-font-weight-medium);--tblr-accordion-body-padding-x: var(--tblr-accordion-padding-x);--tblr-accordion-body-padding-y: 1rem;display:flex;flex-direction:column;gap:var(--tblr-accordion-gap)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--tblr-accordion-btn-padding-y) var(--tblr-accordion-padding-x);color:inherit;text-align:inherit;background-color:transparent;border:0;font-size:inherit;font-weight:var(--tblr-accordion-btn-font-weight);gap:.75rem}.accordion-button:not(.collapsed){border-bottom-color:transparent;box-shadow:none;color:var(--tblr-accordion-active-color)}.accordion-header{margin:0;position:relative;display:flex;gap:1rem;align-items:center;width:100%;color:var(--tblr-accordion-btn-color);text-align:left;background-color:transparent;border:0;overflow-anchor:none;transition:transform .3s}.accordion-header:hover{z-index:2}.accordion-header:focus{z-index:3;outline:0;box-shadow:var(--tblr-accordion-btn-focus-box-shadow)}.accordion-header:focus:not(:focus-visible){outline:none;box-shadow:none}.accordion-button-icon{color:var(--tblr-secondary)}.accordion-button-toggle{display:flex;line-height:1;transition:.3s transform;margin-left:auto;margin-right:0;color:var(--tblr-secondary);width:var(--tblr-accordion-btn-toggle-width);height:var(--tblr-accordion-btn-toggle-width)}.accordion-button:not(.collapsed) .accordion-button-toggle{transform:rotate(-180deg);color:var(--tblr-accordion-active-color)}.accordion-button-toggle path{transition:.3s opacity}.accordion-button:not(.collapsed) .accordion-button-toggle-plus path:first-child{opacity:0}.accordion-item{color:var(--tblr-accordion-color);border:var(--tblr-border-width) solid var(--tblr-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--tblr-accordion-border-radius);border-top-right-radius:var(--tblr-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header{border-top-left-radius:var(--tblr-accordion-inner-border-radius);border-top-right-radius:var(--tblr-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--tblr-accordion-border-radius);border-bottom-left-radius:var(--tblr-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header.collapsed{border-bottom-right-radius:var(--tblr-accordion-inner-border-radius);border-bottom-left-radius:var(--tblr-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--tblr-accordion-border-radius);border-bottom-left-radius:var(--tblr-accordion-border-radius)}.accordion-body{color:var(--tblr-secondary);padding:0 var(--tblr-accordion-body-padding-x) var(--tblr-accordion-body-padding-y)}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush>.accordion-item:first-child{border-top:0}.accordion-flush>.accordion-item:last-child{border-bottom:0}.accordion-flush>.accordion-item>.accordion-collapse,.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}.accordion-tabs{--tblr-accordion-gap: .75rem}.accordion-tabs>.accordion-item{border:var(--tblr-border-width) solid var(--tblr-accordion-border-color);border-radius:var(--tblr-accordion-border-radius)}.accordion-inverted .accordion-button-toggle{order:-1;margin-left:0}.alert{--tblr-alert-color: var(--tblr-body-color);--tblr-alert-bg: color-mix(in srgb, var(--tblr-alert-color) 10%, transparent);--tblr-alert-padding-x: 1rem;--tblr-alert-padding-y: .75rem;--tblr-alert-margin-bottom: 1rem;--tblr-alert-border-color: color-mix(in srgb, var(--tblr-alert-color) 20%, transparent);--tblr-alert-border: var(--tblr-border-width) solid var(--tblr-alert-border-color);--tblr-alert-border-radius: var(--tblr-border-radius);--tblr-alert-link-color: inherit;--tblr-alert-heading-font-weight: var(--tblr-font-weight-medium);position:relative;padding:var(--tblr-alert-padding-y) var(--tblr-alert-padding-x);margin-bottom:var(--tblr-alert-margin-bottom);background-color:color-mix(in srgb,var(--tblr-alert-bg),var(--tblr-bg-surface));border-radius:var(--tblr-alert-border-radius);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-alert-border-color);display:flex;flex-direction:row;gap:1rem}.alert-heading{color:inherit;margin-bottom:.25rem;font-weight:var(--tblr-alert-heading-font-weight)}.alert-description{color:var(--tblr-secondary)}.alert-icon{color:var(--tblr-alert-color);width:1.25rem!important;height:1.25rem!important}.alert-action{color:var(--tblr-alert-color);text-decoration:underline}.alert-action:hover{text-decoration:none}.alert-list{margin:0}.alert-link{font-weight:var(--tblr-font-weight-bold);color:var(--tblr-alert-link-color)}.alert-link,.alert-link:hover{color:var(--tblr-alert-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:calc(var(--tblr-alert-padding-x) / 2 - 1px);right:calc(var(--tblr-alert-padding-y) / 2 - 1px);z-index:1;padding:calc(var(--tblr-alert-padding-y) * 1.25) var(--tblr-alert-padding-x)}.alert-important{border-color:var(--tblr-alert-color);background-color:var(--tblr-alert-color);color:var(--tblr-white)}.alert-important .alert-description,.alert-important .alert-icon{color:inherit}.alert-minor{background:transparent;border-color:var(--tblr-border-color)}.alert-primary{--tblr-alert-color: var(--tblr-primary)}.alert-secondary{--tblr-alert-color: var(--tblr-secondary)}.alert-success{--tblr-alert-color: var(--tblr-success)}.alert-info{--tblr-alert-color: var(--tblr-info)}.alert-warning{--tblr-alert-color: var(--tblr-warning)}.alert-danger{--tblr-alert-color: var(--tblr-danger)}.alert-light{--tblr-alert-color: var(--tblr-light)}.alert-dark{--tblr-alert-color: var(--tblr-dark)}.alert-muted{--tblr-alert-color: var(--tblr-muted)}.alert-blue{--tblr-alert-color: var(--tblr-blue)}.alert-azure{--tblr-alert-color: var(--tblr-azure)}.alert-indigo{--tblr-alert-color: var(--tblr-indigo)}.alert-purple{--tblr-alert-color: var(--tblr-purple)}.alert-pink{--tblr-alert-color: var(--tblr-pink)}.alert-red{--tblr-alert-color: var(--tblr-red)}.alert-orange{--tblr-alert-color: var(--tblr-orange)}.alert-yellow{--tblr-alert-color: var(--tblr-yellow)}.alert-lime{--tblr-alert-color: var(--tblr-lime)}.alert-green{--tblr-alert-color: var(--tblr-green)}.alert-teal{--tblr-alert-color: var(--tblr-teal)}.alert-cyan{--tblr-alert-color: var(--tblr-cyan)}.avatar{--tblr-avatar-size: var(--tblr-avatar-list-size, 2.5rem);--tblr-avatar-status-size: .75rem;--tblr-avatar-bg: var(--tblr-bg-surface-secondary);--tblr-avatar-box-shadow-color: var(--tblr-border-color-translucent);--tblr-avatar-box-shadow: inset 0 0 0 1px var(--tblr-avatar-box-shadow-color);--tblr-avatar-font-size: 1rem;--tblr-avatar-icon-size: 1.5rem;--tblr-avatar-brand-size: 1.25rem;position:relative;width:var(--tblr-avatar-size);height:var(--tblr-avatar-size);font-size:var(--tblr-avatar-font-size);font-weight:var(--tblr-font-weight-medium);line-height:1;display:inline-flex;align-items:center;justify-content:center;color:var(--tblr-secondary);text-align:center;text-transform:uppercase;vertical-align:bottom;user-select:none;background:var(--tblr-avatar-bg) no-repeat center/cover;border-radius:var(--tblr-border-radius);box-shadow:var(--tblr-avatar-box-shadow);transition:color .3s,background-color .3s,box-shadow .3s}.avatar .icon{width:var(--tblr-avatar-icon-size);height:var(--tblr-avatar-icon-size)}.avatar .badge{position:absolute;right:0;bottom:0;border-radius:100rem;box-shadow:0 0 0 calc(var(--tblr-avatar-status-size) / 4) var(--tblr-bg-surface)}a.avatar{cursor:pointer}a.avatar:hover{color:var(--tblr-primary);--tblr-avatar-box-shadow-color: var(--tblr-primary)}.avatar-rounded{border-radius:100rem}.avatar-xxs{--tblr-avatar-size: 1rem;--tblr-avatar-status-size: .25rem;--tblr-avatar-font-size: .5rem;--tblr-avatar-icon-size: .5rem;--tblr-avatar-brand-size: .5rem}.avatar-xxs .badge:empty{width:.25rem;height:.25rem}.avatar-xs{--tblr-avatar-size: 1.25rem;--tblr-avatar-status-size: .375rem;--tblr-avatar-font-size: .625rem;--tblr-avatar-icon-size: .75rem;--tblr-avatar-brand-size: .75rem}.avatar-xs .badge:empty{width:.375rem;height:.375rem}.avatar-sm{--tblr-avatar-size: 2rem;--tblr-avatar-status-size: .5rem;--tblr-avatar-font-size: .75rem;--tblr-avatar-icon-size: 1.5rem;--tblr-avatar-brand-size: 1rem}.avatar-sm .badge:empty{width:.5rem;height:.5rem}.avatar-md{--tblr-avatar-size: 2.5rem;--tblr-avatar-status-size: .75rem;--tblr-avatar-font-size: .875rem;--tblr-avatar-icon-size: 1.5rem;--tblr-avatar-brand-size: 1.25rem}.avatar-md .badge:empty{width:.75rem;height:.75rem}.avatar-lg{--tblr-avatar-size: 3rem;--tblr-avatar-status-size: .75rem;--tblr-avatar-font-size: 1.25rem;--tblr-avatar-icon-size: 2rem;--tblr-avatar-brand-size: 1.25rem}.avatar-lg .badge:empty{width:.75rem;height:.75rem}.avatar-xl{--tblr-avatar-size: 5rem;--tblr-avatar-status-size: 1rem;--tblr-avatar-font-size: 2rem;--tblr-avatar-icon-size: 3rem;--tblr-avatar-brand-size: 1.25rem}.avatar-xl .badge:empty{width:1rem;height:1rem}.avatar-2xl{--tblr-avatar-size: 7rem;--tblr-avatar-status-size: 1rem;--tblr-avatar-font-size: 3rem;--tblr-avatar-icon-size: 5rem;--tblr-avatar-brand-size: 2rem}.avatar-2xl .badge:empty{width:1rem;height:1rem}.avatar-list{--tblr-avatar-list-size: 2.5rem;--tblr-list-gap: .5rem;display:flex;flex-wrap:wrap;gap:var(--tblr-list-gap)}.avatar-list a.avatar:hover{z-index:1}.avatar-list-stacked{display:block;--tblr-list-gap: 0}.avatar-list-stacked .avatar{margin-right:calc(-.5 * var(--tblr-avatar-size))!important;box-shadow:var(--tblr-avatar-box-shadow),0 0 0 2px var(--tblr-card-bg, var(--tblr-bg-surface))}.avatar-list-xxs{--tblr-avatar-list-size: 1rem}.avatar-list-xs{--tblr-avatar-list-size: 1.25rem}.avatar-list-sm{--tblr-avatar-list-size: 2rem}.avatar-list-md{--tblr-avatar-list-size: 2.5rem}.avatar-list-lg{--tblr-avatar-list-size: 3rem}.avatar-list-xl{--tblr-avatar-list-size: 5rem}.avatar-list-2xl{--tblr-avatar-list-size: 7rem}.avatar-upload{border:var(--tblr-border-width) dashed var(--tblr-border-color);background:var(--tblr-bg-forms);box-shadow:none;flex-direction:column;transition:color .3s,background-color .3s}@media(prefers-reduced-motion:reduce){.avatar-upload{transition:none}}.avatar-upload svg{width:1.5rem;height:1.5rem;stroke-width:1}.avatar-upload:hover{border-color:var(--tblr-primary);color:var(--tblr-primary);text-decoration:none}.avatar-upload-text{font-size:.625rem;line-height:1;margin-top:.25rem}.avatar-cover{margin-top:calc(-.5 * var(--tblr-avatar-size));box-shadow:0 0 0 .25rem var(--tblr-card-bg, var(--tblr-body-bg))}.avatar-brand{width:var(--tblr-avatar-brand-size);height:var(--tblr-avatar-brand-size);position:absolute;right:-2px;bottom:-2px;z-index:1000;background:var(--tblr-bg-surface);border-radius:var(--tblr-border-radius);border:1px solid var(--tblr-border-color)}.badge{--tblr-badge-padding-x: .5em;--tblr-badge-padding-y: .25em;--tblr-badge-font-size: .85714285em;--tblr-badge-font-weight: var(--tblr-font-weight-medium);--tblr-badge-color: var(--tblr-secondary);--tblr-badge-border-radius: var(--tblr-border-radius);--tblr-badge-icon-size: 1em;--tblr-badge-line-height: 1;display:inline-flex;padding:var(--tblr-badge-padding-y) var(--tblr-badge-padding-x);font-weight:var(--tblr-badge-font-weight);font-size:var(--tblr-badge-font-size);color:var(--tblr-badge-color);text-align:center;white-space:nowrap;justify-content:center;align-items:center;gap:.25rem;background:var(--tblr-bg-surface-secondary);overflow:hidden;user-select:none;border:var(--tblr-border-width) var(--tblr-border-style) transparent;border-radius:var(--tblr-badge-border-radius);min-width:calc(1em + var(--tblr-badge-padding-y) * 2 + 2px);letter-spacing:.04em;vertical-align:bottom;line-height:var(--tblr-badge-line-height)}a.badge{background:var(--tblr-bg-surface-secondary)}.badge .icon{width:1em;height:1em;font-size:var(--tblr-badge-icon-size);stroke-width:2}.badge:empty,.badge-dot{display:inline-block;width:10px;height:10px;min-width:0;min-height:auto;padding:0;border-radius:100rem;vertical-align:baseline}.badge-outline{background-color:transparent;border:var(--tblr-border-width) var(--tblr-border-style) currentColor}.badge-pill{border-radius:100rem}.badges-list{--tblr-list-gap: .5rem;display:flex;flex-wrap:wrap;gap:var(--tblr-list-gap)}.badge-notification{position:absolute!important;top:0!important;right:0!important;transform:translate(50%,-50%);z-index:1}.badge-blink{animation:blink 2s infinite}.badge-sm{--tblr-badge-font-size: .71428571em;--tblr-badge-icon-size: 1em;--tblr-badge-padding-y: 2px;--tblr-badge-padding-x: .25rem}.badge-lg{--tblr-badge-font-size: 1em;--tblr-badge-icon-size: 1em;--tblr-badge-padding-y: .25rem;--tblr-badge-padding-x: .5rem}.badge-icononly{--tblr-badge-padding-x: 0}.breadcrumb{--tblr-breadcrumb-padding-x: 0;--tblr-breadcrumb-padding-y: 0;--tblr-breadcrumb-margin-bottom: 1rem;--tblr-breadcrumb-font-size: ;--tblr-breadcrumb-bg: ;--tblr-breadcrumb-border-radius: ;--tblr-breadcrumb-divider-color: var(--tblr-gray-500);--tblr-breadcrumb-item-padding-x: .5rem;--tblr-breadcrumb-item-active-color: inherit;--tblr-breadcrumb-item-active-font-weight: var(--tblr-font-weight-bold);--tblr-breadcrumb-item-disabled-color: var(--tblr-disabled-color);--tblr-breadcrumb-link-color: var(--tblr-link-color);display:flex;flex-wrap:wrap;font-size:var(--tblr-breadcrumb-font-size);list-style:none;background-color:var(--tblr-breadcrumb-bg);border-radius:var(--tblr-breadcrumb-border-radius);padding:0;margin:0;background:transparent}.breadcrumb a{color:var(--tblr-breadcrumb-link-color)}.breadcrumb a:hover{text-decoration:underline}.breadcrumb-muted{--tblr-breadcrumb-link-color: var(--tblr-secondary)}.breadcrumb-item.active{color:var(--tblr-breadcrumb-item-active-color);font-weight:var(--tblr-breadcrumb-item-active-font-weight)}.breadcrumb-item.active a{color:inherit;pointer-events:none}.breadcrumb-item.disabled{color:var(--tblr-breadcrumb-item-disabled-color)}.breadcrumb-item.disabled:before{color:inherit}.breadcrumb-item.disabled a{color:inherit;pointer-events:none}.breadcrumb-item+.breadcrumb-item{padding-left:var(--tblr-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item:before{float:left;padding-right:var(--tblr-breadcrumb-item-padding-x);color:var(--tblr-breadcrumb-divider-color);content:var(--tblr-breadcrumb-divider, "/")}.breadcrumb-dots{--tblr-breadcrumb-divider: "\b7"}.breadcrumb-arrows{--tblr-breadcrumb-divider: "\203a"}.breadcrumb-bullets{--tblr-breadcrumb-divider: "\2022"}.btn{--tblr-btn-icon-size: 1.25rem;--tblr-btn-icon-color: inherit;--tblr-btn-bg: var(--tblr-bg-surface);--tblr-btn-color: var(--tblr-body-color);--tblr-btn-border-color: var(--tblr-border-color);--tblr-btn-hover-bg: var(--tblr-btn-bg);--tblr-btn-hover-border-color: var(--tblr-border-active-color);--tblr-btn-active-color: var(--tblr-primary);--tblr-btn-active-bg: rgba(var(--tblr-primary-rgb), .04);--tblr-btn-active-border-color: var(--tblr-primary);display:inline-flex;align-items:center;justify-content:center;white-space:nowrap;box-shadow:var(--tblr-btn-box-shadow);position:relative;min-width:calc(var(--tblr-btn-line-height) * 1 + var(--tblr-btn-padding-y) * 2 + var(--tblr-btn-border-width) * 2);min-height:calc(var(--tblr-btn-line-height) * 1 + var(--tblr-btn-padding-y) * 2 + var(--tblr-btn-border-width) * 2)}.btn .icon{width:var(--tblr-btn-icon-size);height:var(--tblr-btn-icon-size);min-width:var(--tblr-btn-icon-size);font-size:var(--tblr-btn-icon-size);margin:0 calc(var(--tblr-btn-padding-x) / 2) 0 calc(var(--tblr-btn-padding-x) / -4);vertical-align:bottom;color:var(--tblr-btn-icon-color)}.btn .avatar{width:var(--tblr-btn-icon-size);height:var(--tblr-btn-icon-size);margin:0 calc(var(--tblr-btn-padding-x) / 2) 0 calc(var(--tblr-btn-padding-x) / -4)}.btn .icon-right,.btn .icon-end{margin:0 calc(var(--tblr-btn-padding-x) / -4) 0 calc(var(--tblr-btn-padding-x) / 2)}.btn .badge{top:auto}.btn-check+.btn:hover{color:var(--tblr-btn-hover-color);background-color:var(--tblr-btn-hover-bg);border-color:var(--tblr-btn-hover-border-color)}.btn-link{color:#009f95;background-color:transparent;border-color:transparent;box-shadow:none}.btn-link .icon{color:inherit}.btn-link:hover{color:#006a64;border-color:transparent}.btn-primary{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-primary-fg, #ffffff);--tblr-btn-bg: var(--tblr-primary);--tblr-btn-hover-color: var(--tblr-primary-fg);--tblr-btn-hover-bg: var(--tblr-primary-darken);--tblr-btn-active-color: var(--tblr-primary-fg);--tblr-btn-active-bg: var(--tblr-primary-darken);--tblr-btn-disabled-bg: var(--tblr-primary);--tblr-btn-disabled-color: var(--tblr-primary-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-primary,.btn-outline.btn-primary{--tblr-btn-color: var(--tblr-primary);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-primary);--tblr-btn-hover-color: var(--tblr-primary-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-primary);--tblr-btn-active-color: var(--tblr-primary-fg);--tblr-btn-active-bg: var(--tblr-primary);--tblr-btn-active-border-color: var(--tblr-primary);--tblr-btn-disabled-color: var(--tblr-primary);--tblr-btn-disabled-border-color: var(--tblr-primary)}.btn-ghost-primary,.btn-ghost.btn-primary{--tblr-btn-color: var(--tblr-primary);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-primary-fg);--tblr-btn-hover-bg: var(--tblr-primary);--tblr-btn-hover-border-color: var(--tblr-primary);--tblr-btn-active-color: var(--tblr-primary-fg);--tblr-btn-active-bg: var(--tblr-primary);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-primary);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-secondary,.btn-grey,.btn-gray{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-secondary-fg, #ffffff);--tblr-btn-bg: var(--tblr-secondary);--tblr-btn-hover-color: var(--tblr-secondary-fg);--tblr-btn-hover-bg: var(--tblr-secondary-darken);--tblr-btn-active-color: var(--tblr-secondary-fg);--tblr-btn-active-bg: var(--tblr-secondary-darken);--tblr-btn-disabled-bg: var(--tblr-secondary);--tblr-btn-disabled-color: var(--tblr-secondary-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-secondary,.btn-outline.btn-secondary,.btn-outline.btn-grey,.btn-outline.btn-gray{--tblr-btn-color: var(--tblr-secondary);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-secondary);--tblr-btn-hover-color: var(--tblr-secondary-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-secondary);--tblr-btn-active-color: var(--tblr-secondary-fg);--tblr-btn-active-bg: var(--tblr-secondary);--tblr-btn-active-border-color: var(--tblr-secondary);--tblr-btn-disabled-color: var(--tblr-secondary);--tblr-btn-disabled-border-color: var(--tblr-secondary)}.btn-ghost-secondary,.btn-ghost.btn-secondary,.btn-ghost.btn-grey,.btn-ghost.btn-gray{--tblr-btn-color: var(--tblr-secondary);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-secondary-fg);--tblr-btn-hover-bg: var(--tblr-secondary);--tblr-btn-hover-border-color: var(--tblr-secondary);--tblr-btn-active-color: var(--tblr-secondary-fg);--tblr-btn-active-bg: var(--tblr-secondary);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-secondary);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-success{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-success-fg, #ffffff);--tblr-btn-bg: var(--tblr-success);--tblr-btn-hover-color: var(--tblr-success-fg);--tblr-btn-hover-bg: var(--tblr-success-darken);--tblr-btn-active-color: var(--tblr-success-fg);--tblr-btn-active-bg: var(--tblr-success-darken);--tblr-btn-disabled-bg: var(--tblr-success);--tblr-btn-disabled-color: var(--tblr-success-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-success,.btn-outline.btn-success{--tblr-btn-color: var(--tblr-success);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-success);--tblr-btn-hover-color: var(--tblr-success-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-success);--tblr-btn-active-color: var(--tblr-success-fg);--tblr-btn-active-bg: var(--tblr-success);--tblr-btn-active-border-color: var(--tblr-success);--tblr-btn-disabled-color: var(--tblr-success);--tblr-btn-disabled-border-color: var(--tblr-success)}.btn-ghost-success,.btn-ghost.btn-success{--tblr-btn-color: var(--tblr-success);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-success-fg);--tblr-btn-hover-bg: var(--tblr-success);--tblr-btn-hover-border-color: var(--tblr-success);--tblr-btn-active-color: var(--tblr-success-fg);--tblr-btn-active-bg: var(--tblr-success);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-success);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-info{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-info-fg, #ffffff);--tblr-btn-bg: var(--tblr-info);--tblr-btn-hover-color: var(--tblr-info-fg);--tblr-btn-hover-bg: var(--tblr-info-darken);--tblr-btn-active-color: var(--tblr-info-fg);--tblr-btn-active-bg: var(--tblr-info-darken);--tblr-btn-disabled-bg: var(--tblr-info);--tblr-btn-disabled-color: var(--tblr-info-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-info,.btn-outline.btn-info{--tblr-btn-color: var(--tblr-info);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-info);--tblr-btn-hover-color: var(--tblr-info-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-info);--tblr-btn-active-color: var(--tblr-info-fg);--tblr-btn-active-bg: var(--tblr-info);--tblr-btn-active-border-color: var(--tblr-info);--tblr-btn-disabled-color: var(--tblr-info);--tblr-btn-disabled-border-color: var(--tblr-info)}.btn-ghost-info,.btn-ghost.btn-info{--tblr-btn-color: var(--tblr-info);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-info-fg);--tblr-btn-hover-bg: var(--tblr-info);--tblr-btn-hover-border-color: var(--tblr-info);--tblr-btn-active-color: var(--tblr-info-fg);--tblr-btn-active-bg: var(--tblr-info);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-info);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-warning{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-warning-fg, #ffffff);--tblr-btn-bg: var(--tblr-warning);--tblr-btn-hover-color: var(--tblr-warning-fg);--tblr-btn-hover-bg: var(--tblr-warning-darken);--tblr-btn-active-color: var(--tblr-warning-fg);--tblr-btn-active-bg: var(--tblr-warning-darken);--tblr-btn-disabled-bg: var(--tblr-warning);--tblr-btn-disabled-color: var(--tblr-warning-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-warning,.btn-outline.btn-warning{--tblr-btn-color: var(--tblr-warning);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-warning);--tblr-btn-hover-color: var(--tblr-warning-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-warning);--tblr-btn-active-color: var(--tblr-warning-fg);--tblr-btn-active-bg: var(--tblr-warning);--tblr-btn-active-border-color: var(--tblr-warning);--tblr-btn-disabled-color: var(--tblr-warning);--tblr-btn-disabled-border-color: var(--tblr-warning)}.btn-ghost-warning,.btn-ghost.btn-warning{--tblr-btn-color: var(--tblr-warning);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-warning-fg);--tblr-btn-hover-bg: var(--tblr-warning);--tblr-btn-hover-border-color: var(--tblr-warning);--tblr-btn-active-color: var(--tblr-warning-fg);--tblr-btn-active-bg: var(--tblr-warning);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-warning);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-danger{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-danger-fg, #ffffff);--tblr-btn-bg: var(--tblr-danger);--tblr-btn-hover-color: var(--tblr-danger-fg);--tblr-btn-hover-bg: var(--tblr-danger-darken);--tblr-btn-active-color: var(--tblr-danger-fg);--tblr-btn-active-bg: var(--tblr-danger-darken);--tblr-btn-disabled-bg: var(--tblr-danger);--tblr-btn-disabled-color: var(--tblr-danger-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-danger,.btn-outline.btn-danger{--tblr-btn-color: var(--tblr-danger);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-danger);--tblr-btn-hover-color: var(--tblr-danger-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-danger);--tblr-btn-active-color: var(--tblr-danger-fg);--tblr-btn-active-bg: var(--tblr-danger);--tblr-btn-active-border-color: var(--tblr-danger);--tblr-btn-disabled-color: var(--tblr-danger);--tblr-btn-disabled-border-color: var(--tblr-danger)}.btn-ghost-danger,.btn-ghost.btn-danger{--tblr-btn-color: var(--tblr-danger);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-danger-fg);--tblr-btn-hover-bg: var(--tblr-danger);--tblr-btn-hover-border-color: var(--tblr-danger);--tblr-btn-active-color: var(--tblr-danger-fg);--tblr-btn-active-bg: var(--tblr-danger);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-danger);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-light,.btn-white{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-light-fg, #ffffff);--tblr-btn-bg: var(--tblr-light);--tblr-btn-hover-color: var(--tblr-light-fg);--tblr-btn-hover-bg: var(--tblr-light-darken);--tblr-btn-active-color: var(--tblr-light-fg);--tblr-btn-active-bg: var(--tblr-light-darken);--tblr-btn-disabled-bg: var(--tblr-light);--tblr-btn-disabled-color: var(--tblr-light-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-light,.btn-outline.btn-light,.btn-outline.btn-white{--tblr-btn-color: var(--tblr-light);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-light);--tblr-btn-hover-color: var(--tblr-light-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-light);--tblr-btn-active-color: var(--tblr-light-fg);--tblr-btn-active-bg: var(--tblr-light);--tblr-btn-active-border-color: var(--tblr-light);--tblr-btn-disabled-color: var(--tblr-light);--tblr-btn-disabled-border-color: var(--tblr-light)}.btn-ghost-light,.btn-ghost.btn-light,.btn-ghost.btn-white{--tblr-btn-color: var(--tblr-light);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-light-fg);--tblr-btn-hover-bg: var(--tblr-light);--tblr-btn-hover-border-color: var(--tblr-light);--tblr-btn-active-color: var(--tblr-light-fg);--tblr-btn-active-bg: var(--tblr-light);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-light);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-dark,.btn-black{--tblr-btn-border-color: var(--tblr-dark-mode-border-color);--tblr-btn-hover-border-color: var(--tblr-dark-mode-border-active-color);--tblr-btn-active-border-color: var(--tblr-dark-mode-border-active-color);--tblr-btn-color: var(--tblr-dark-fg, #ffffff);--tblr-btn-bg: var(--tblr-dark);--tblr-btn-hover-color: var(--tblr-dark-fg);--tblr-btn-hover-bg: var(--tblr-dark-darken);--tblr-btn-active-color: var(--tblr-dark-fg);--tblr-btn-active-bg: var(--tblr-dark-darken);--tblr-btn-disabled-bg: var(--tblr-dark);--tblr-btn-disabled-color: var(--tblr-dark-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-dark,.btn-outline.btn-dark,.btn-outline.btn-black{--tblr-btn-color: var(--tblr-dark);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-dark);--tblr-btn-hover-color: var(--tblr-dark-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-dark);--tblr-btn-active-color: var(--tblr-dark-fg);--tblr-btn-active-bg: var(--tblr-dark);--tblr-btn-active-border-color: var(--tblr-dark);--tblr-btn-disabled-color: var(--tblr-dark);--tblr-btn-disabled-border-color: var(--tblr-dark)}.btn-ghost-dark,.btn-ghost.btn-dark,.btn-ghost.btn-black{--tblr-btn-color: var(--tblr-dark);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-dark-fg);--tblr-btn-hover-bg: var(--tblr-dark);--tblr-btn-hover-border-color: var(--tblr-dark);--tblr-btn-active-color: var(--tblr-dark-fg);--tblr-btn-active-bg: var(--tblr-dark);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-dark);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-muted{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-muted-fg, #ffffff);--tblr-btn-bg: var(--tblr-muted);--tblr-btn-hover-color: var(--tblr-muted-fg);--tblr-btn-hover-bg: var(--tblr-muted-darken);--tblr-btn-active-color: var(--tblr-muted-fg);--tblr-btn-active-bg: var(--tblr-muted-darken);--tblr-btn-disabled-bg: var(--tblr-muted);--tblr-btn-disabled-color: var(--tblr-muted-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-muted,.btn-outline.btn-muted{--tblr-btn-color: var(--tblr-muted);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-muted);--tblr-btn-hover-color: var(--tblr-muted-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-muted);--tblr-btn-active-color: var(--tblr-muted-fg);--tblr-btn-active-bg: var(--tblr-muted);--tblr-btn-active-border-color: var(--tblr-muted);--tblr-btn-disabled-color: var(--tblr-muted);--tblr-btn-disabled-border-color: var(--tblr-muted)}.btn-ghost-muted,.btn-ghost.btn-muted{--tblr-btn-color: var(--tblr-muted);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-muted-fg);--tblr-btn-hover-bg: var(--tblr-muted);--tblr-btn-hover-border-color: var(--tblr-muted);--tblr-btn-active-color: var(--tblr-muted-fg);--tblr-btn-active-bg: var(--tblr-muted);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-muted);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-blue{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-blue-fg, #ffffff);--tblr-btn-bg: var(--tblr-blue);--tblr-btn-hover-color: var(--tblr-blue-fg);--tblr-btn-hover-bg: var(--tblr-blue-darken);--tblr-btn-active-color: var(--tblr-blue-fg);--tblr-btn-active-bg: var(--tblr-blue-darken);--tblr-btn-disabled-bg: var(--tblr-blue);--tblr-btn-disabled-color: var(--tblr-blue-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-blue,.btn-outline.btn-blue{--tblr-btn-color: var(--tblr-blue);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-blue);--tblr-btn-hover-color: var(--tblr-blue-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-blue);--tblr-btn-active-color: var(--tblr-blue-fg);--tblr-btn-active-bg: var(--tblr-blue);--tblr-btn-active-border-color: var(--tblr-blue);--tblr-btn-disabled-color: var(--tblr-blue);--tblr-btn-disabled-border-color: var(--tblr-blue)}.btn-ghost-blue,.btn-ghost.btn-blue{--tblr-btn-color: var(--tblr-blue);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-blue-fg);--tblr-btn-hover-bg: var(--tblr-blue);--tblr-btn-hover-border-color: var(--tblr-blue);--tblr-btn-active-color: var(--tblr-blue-fg);--tblr-btn-active-bg: var(--tblr-blue);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-blue);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-azure{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-azure-fg, #ffffff);--tblr-btn-bg: var(--tblr-azure);--tblr-btn-hover-color: var(--tblr-azure-fg);--tblr-btn-hover-bg: var(--tblr-azure-darken);--tblr-btn-active-color: var(--tblr-azure-fg);--tblr-btn-active-bg: var(--tblr-azure-darken);--tblr-btn-disabled-bg: var(--tblr-azure);--tblr-btn-disabled-color: var(--tblr-azure-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-azure,.btn-outline.btn-azure{--tblr-btn-color: var(--tblr-azure);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-azure);--tblr-btn-hover-color: var(--tblr-azure-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-azure);--tblr-btn-active-color: var(--tblr-azure-fg);--tblr-btn-active-bg: var(--tblr-azure);--tblr-btn-active-border-color: var(--tblr-azure);--tblr-btn-disabled-color: var(--tblr-azure);--tblr-btn-disabled-border-color: var(--tblr-azure)}.btn-ghost-azure,.btn-ghost.btn-azure{--tblr-btn-color: var(--tblr-azure);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-azure-fg);--tblr-btn-hover-bg: var(--tblr-azure);--tblr-btn-hover-border-color: var(--tblr-azure);--tblr-btn-active-color: var(--tblr-azure-fg);--tblr-btn-active-bg: var(--tblr-azure);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-azure);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-indigo{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-indigo-fg, #ffffff);--tblr-btn-bg: var(--tblr-indigo);--tblr-btn-hover-color: var(--tblr-indigo-fg);--tblr-btn-hover-bg: var(--tblr-indigo-darken);--tblr-btn-active-color: var(--tblr-indigo-fg);--tblr-btn-active-bg: var(--tblr-indigo-darken);--tblr-btn-disabled-bg: var(--tblr-indigo);--tblr-btn-disabled-color: var(--tblr-indigo-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-indigo,.btn-outline.btn-indigo{--tblr-btn-color: var(--tblr-indigo);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-indigo);--tblr-btn-hover-color: var(--tblr-indigo-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-indigo);--tblr-btn-active-color: var(--tblr-indigo-fg);--tblr-btn-active-bg: var(--tblr-indigo);--tblr-btn-active-border-color: var(--tblr-indigo);--tblr-btn-disabled-color: var(--tblr-indigo);--tblr-btn-disabled-border-color: var(--tblr-indigo)}.btn-ghost-indigo,.btn-ghost.btn-indigo{--tblr-btn-color: var(--tblr-indigo);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-indigo-fg);--tblr-btn-hover-bg: var(--tblr-indigo);--tblr-btn-hover-border-color: var(--tblr-indigo);--tblr-btn-active-color: var(--tblr-indigo-fg);--tblr-btn-active-bg: var(--tblr-indigo);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-indigo);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-purple{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-purple-fg, #ffffff);--tblr-btn-bg: var(--tblr-purple);--tblr-btn-hover-color: var(--tblr-purple-fg);--tblr-btn-hover-bg: var(--tblr-purple-darken);--tblr-btn-active-color: var(--tblr-purple-fg);--tblr-btn-active-bg: var(--tblr-purple-darken);--tblr-btn-disabled-bg: var(--tblr-purple);--tblr-btn-disabled-color: var(--tblr-purple-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-purple,.btn-outline.btn-purple{--tblr-btn-color: var(--tblr-purple);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-purple);--tblr-btn-hover-color: var(--tblr-purple-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-purple);--tblr-btn-active-color: var(--tblr-purple-fg);--tblr-btn-active-bg: var(--tblr-purple);--tblr-btn-active-border-color: var(--tblr-purple);--tblr-btn-disabled-color: var(--tblr-purple);--tblr-btn-disabled-border-color: var(--tblr-purple)}.btn-ghost-purple,.btn-ghost.btn-purple{--tblr-btn-color: var(--tblr-purple);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-purple-fg);--tblr-btn-hover-bg: var(--tblr-purple);--tblr-btn-hover-border-color: var(--tblr-purple);--tblr-btn-active-color: var(--tblr-purple-fg);--tblr-btn-active-bg: var(--tblr-purple);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-purple);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-pink{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-pink-fg, #ffffff);--tblr-btn-bg: var(--tblr-pink);--tblr-btn-hover-color: var(--tblr-pink-fg);--tblr-btn-hover-bg: var(--tblr-pink-darken);--tblr-btn-active-color: var(--tblr-pink-fg);--tblr-btn-active-bg: var(--tblr-pink-darken);--tblr-btn-disabled-bg: var(--tblr-pink);--tblr-btn-disabled-color: var(--tblr-pink-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-pink,.btn-outline.btn-pink{--tblr-btn-color: var(--tblr-pink);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-pink);--tblr-btn-hover-color: var(--tblr-pink-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-pink);--tblr-btn-active-color: var(--tblr-pink-fg);--tblr-btn-active-bg: var(--tblr-pink);--tblr-btn-active-border-color: var(--tblr-pink);--tblr-btn-disabled-color: var(--tblr-pink);--tblr-btn-disabled-border-color: var(--tblr-pink)}.btn-ghost-pink,.btn-ghost.btn-pink{--tblr-btn-color: var(--tblr-pink);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-pink-fg);--tblr-btn-hover-bg: var(--tblr-pink);--tblr-btn-hover-border-color: var(--tblr-pink);--tblr-btn-active-color: var(--tblr-pink-fg);--tblr-btn-active-bg: var(--tblr-pink);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-pink);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-red{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-red-fg, #ffffff);--tblr-btn-bg: var(--tblr-red);--tblr-btn-hover-color: var(--tblr-red-fg);--tblr-btn-hover-bg: var(--tblr-red-darken);--tblr-btn-active-color: var(--tblr-red-fg);--tblr-btn-active-bg: var(--tblr-red-darken);--tblr-btn-disabled-bg: var(--tblr-red);--tblr-btn-disabled-color: var(--tblr-red-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-red,.btn-outline.btn-red{--tblr-btn-color: var(--tblr-red);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-red);--tblr-btn-hover-color: var(--tblr-red-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-red);--tblr-btn-active-color: var(--tblr-red-fg);--tblr-btn-active-bg: var(--tblr-red);--tblr-btn-active-border-color: var(--tblr-red);--tblr-btn-disabled-color: var(--tblr-red);--tblr-btn-disabled-border-color: var(--tblr-red)}.btn-ghost-red,.btn-ghost.btn-red{--tblr-btn-color: var(--tblr-red);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-red-fg);--tblr-btn-hover-bg: var(--tblr-red);--tblr-btn-hover-border-color: var(--tblr-red);--tblr-btn-active-color: var(--tblr-red-fg);--tblr-btn-active-bg: var(--tblr-red);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-red);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-orange{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-orange-fg, #ffffff);--tblr-btn-bg: var(--tblr-orange);--tblr-btn-hover-color: var(--tblr-orange-fg);--tblr-btn-hover-bg: var(--tblr-orange-darken);--tblr-btn-active-color: var(--tblr-orange-fg);--tblr-btn-active-bg: var(--tblr-orange-darken);--tblr-btn-disabled-bg: var(--tblr-orange);--tblr-btn-disabled-color: var(--tblr-orange-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-orange,.btn-outline.btn-orange{--tblr-btn-color: var(--tblr-orange);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-orange);--tblr-btn-hover-color: var(--tblr-orange-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-orange);--tblr-btn-active-color: var(--tblr-orange-fg);--tblr-btn-active-bg: var(--tblr-orange);--tblr-btn-active-border-color: var(--tblr-orange);--tblr-btn-disabled-color: var(--tblr-orange);--tblr-btn-disabled-border-color: var(--tblr-orange)}.btn-ghost-orange,.btn-ghost.btn-orange{--tblr-btn-color: var(--tblr-orange);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-orange-fg);--tblr-btn-hover-bg: var(--tblr-orange);--tblr-btn-hover-border-color: var(--tblr-orange);--tblr-btn-active-color: var(--tblr-orange-fg);--tblr-btn-active-bg: var(--tblr-orange);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-orange);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-yellow{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-yellow-fg, #ffffff);--tblr-btn-bg: var(--tblr-yellow);--tblr-btn-hover-color: var(--tblr-yellow-fg);--tblr-btn-hover-bg: var(--tblr-yellow-darken);--tblr-btn-active-color: var(--tblr-yellow-fg);--tblr-btn-active-bg: var(--tblr-yellow-darken);--tblr-btn-disabled-bg: var(--tblr-yellow);--tblr-btn-disabled-color: var(--tblr-yellow-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-yellow,.btn-outline.btn-yellow{--tblr-btn-color: var(--tblr-yellow);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-yellow);--tblr-btn-hover-color: var(--tblr-yellow-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-yellow);--tblr-btn-active-color: var(--tblr-yellow-fg);--tblr-btn-active-bg: var(--tblr-yellow);--tblr-btn-active-border-color: var(--tblr-yellow);--tblr-btn-disabled-color: var(--tblr-yellow);--tblr-btn-disabled-border-color: var(--tblr-yellow)}.btn-ghost-yellow,.btn-ghost.btn-yellow{--tblr-btn-color: var(--tblr-yellow);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-yellow-fg);--tblr-btn-hover-bg: var(--tblr-yellow);--tblr-btn-hover-border-color: var(--tblr-yellow);--tblr-btn-active-color: var(--tblr-yellow-fg);--tblr-btn-active-bg: var(--tblr-yellow);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-yellow);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-lime{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-lime-fg, #ffffff);--tblr-btn-bg: var(--tblr-lime);--tblr-btn-hover-color: var(--tblr-lime-fg);--tblr-btn-hover-bg: var(--tblr-lime-darken);--tblr-btn-active-color: var(--tblr-lime-fg);--tblr-btn-active-bg: var(--tblr-lime-darken);--tblr-btn-disabled-bg: var(--tblr-lime);--tblr-btn-disabled-color: var(--tblr-lime-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-lime,.btn-outline.btn-lime{--tblr-btn-color: var(--tblr-lime);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-lime);--tblr-btn-hover-color: var(--tblr-lime-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-lime);--tblr-btn-active-color: var(--tblr-lime-fg);--tblr-btn-active-bg: var(--tblr-lime);--tblr-btn-active-border-color: var(--tblr-lime);--tblr-btn-disabled-color: var(--tblr-lime);--tblr-btn-disabled-border-color: var(--tblr-lime)}.btn-ghost-lime,.btn-ghost.btn-lime{--tblr-btn-color: var(--tblr-lime);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-lime-fg);--tblr-btn-hover-bg: var(--tblr-lime);--tblr-btn-hover-border-color: var(--tblr-lime);--tblr-btn-active-color: var(--tblr-lime-fg);--tblr-btn-active-bg: var(--tblr-lime);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-lime);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-green{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-green-fg, #ffffff);--tblr-btn-bg: var(--tblr-green);--tblr-btn-hover-color: var(--tblr-green-fg);--tblr-btn-hover-bg: var(--tblr-green-darken);--tblr-btn-active-color: var(--tblr-green-fg);--tblr-btn-active-bg: var(--tblr-green-darken);--tblr-btn-disabled-bg: var(--tblr-green);--tblr-btn-disabled-color: var(--tblr-green-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-green,.btn-outline.btn-green{--tblr-btn-color: var(--tblr-green);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-green);--tblr-btn-hover-color: var(--tblr-green-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-green);--tblr-btn-active-color: var(--tblr-green-fg);--tblr-btn-active-bg: var(--tblr-green);--tblr-btn-active-border-color: var(--tblr-green);--tblr-btn-disabled-color: var(--tblr-green);--tblr-btn-disabled-border-color: var(--tblr-green)}.btn-ghost-green,.btn-ghost.btn-green{--tblr-btn-color: var(--tblr-green);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-green-fg);--tblr-btn-hover-bg: var(--tblr-green);--tblr-btn-hover-border-color: var(--tblr-green);--tblr-btn-active-color: var(--tblr-green-fg);--tblr-btn-active-bg: var(--tblr-green);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-green);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-teal{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-teal-fg, #ffffff);--tblr-btn-bg: var(--tblr-teal);--tblr-btn-hover-color: var(--tblr-teal-fg);--tblr-btn-hover-bg: var(--tblr-teal-darken);--tblr-btn-active-color: var(--tblr-teal-fg);--tblr-btn-active-bg: var(--tblr-teal-darken);--tblr-btn-disabled-bg: var(--tblr-teal);--tblr-btn-disabled-color: var(--tblr-teal-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-teal,.btn-outline.btn-teal{--tblr-btn-color: var(--tblr-teal);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-teal);--tblr-btn-hover-color: var(--tblr-teal-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-teal);--tblr-btn-active-color: var(--tblr-teal-fg);--tblr-btn-active-bg: var(--tblr-teal);--tblr-btn-active-border-color: var(--tblr-teal);--tblr-btn-disabled-color: var(--tblr-teal);--tblr-btn-disabled-border-color: var(--tblr-teal)}.btn-ghost-teal,.btn-ghost.btn-teal{--tblr-btn-color: var(--tblr-teal);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-teal-fg);--tblr-btn-hover-bg: var(--tblr-teal);--tblr-btn-hover-border-color: var(--tblr-teal);--tblr-btn-active-color: var(--tblr-teal-fg);--tblr-btn-active-bg: var(--tblr-teal);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-teal);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-cyan{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-cyan-fg, #ffffff);--tblr-btn-bg: var(--tblr-cyan);--tblr-btn-hover-color: var(--tblr-cyan-fg);--tblr-btn-hover-bg: var(--tblr-cyan-darken);--tblr-btn-active-color: var(--tblr-cyan-fg);--tblr-btn-active-bg: var(--tblr-cyan-darken);--tblr-btn-disabled-bg: var(--tblr-cyan);--tblr-btn-disabled-color: var(--tblr-cyan-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-cyan,.btn-outline.btn-cyan{--tblr-btn-color: var(--tblr-cyan);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-cyan);--tblr-btn-hover-color: var(--tblr-cyan-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-cyan);--tblr-btn-active-color: var(--tblr-cyan-fg);--tblr-btn-active-bg: var(--tblr-cyan);--tblr-btn-active-border-color: var(--tblr-cyan);--tblr-btn-disabled-color: var(--tblr-cyan);--tblr-btn-disabled-border-color: var(--tblr-cyan)}.btn-ghost-cyan,.btn-ghost.btn-cyan{--tblr-btn-color: var(--tblr-cyan);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-cyan-fg);--tblr-btn-hover-bg: var(--tblr-cyan);--tblr-btn-hover-border-color: var(--tblr-cyan);--tblr-btn-active-color: var(--tblr-cyan-fg);--tblr-btn-active-bg: var(--tblr-cyan);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-cyan);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-x{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-x-fg, #ffffff);--tblr-btn-bg: var(--tblr-x);--tblr-btn-hover-color: var(--tblr-x-fg);--tblr-btn-hover-bg: var(--tblr-x-darken);--tblr-btn-active-color: var(--tblr-x-fg);--tblr-btn-active-bg: var(--tblr-x-darken);--tblr-btn-disabled-bg: var(--tblr-x);--tblr-btn-disabled-color: var(--tblr-x-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-x,.btn-outline.btn-x{--tblr-btn-color: var(--tblr-x);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-x);--tblr-btn-hover-color: var(--tblr-x-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-x);--tblr-btn-active-color: var(--tblr-x-fg);--tblr-btn-active-bg: var(--tblr-x);--tblr-btn-active-border-color: var(--tblr-x);--tblr-btn-disabled-color: var(--tblr-x);--tblr-btn-disabled-border-color: var(--tblr-x)}.btn-ghost-x,.btn-ghost.btn-x{--tblr-btn-color: var(--tblr-x);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-x-fg);--tblr-btn-hover-bg: var(--tblr-x);--tblr-btn-hover-border-color: var(--tblr-x);--tblr-btn-active-color: var(--tblr-x-fg);--tblr-btn-active-bg: var(--tblr-x);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-x);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-facebook{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-facebook-fg, #ffffff);--tblr-btn-bg: var(--tblr-facebook);--tblr-btn-hover-color: var(--tblr-facebook-fg);--tblr-btn-hover-bg: var(--tblr-facebook-darken);--tblr-btn-active-color: var(--tblr-facebook-fg);--tblr-btn-active-bg: var(--tblr-facebook-darken);--tblr-btn-disabled-bg: var(--tblr-facebook);--tblr-btn-disabled-color: var(--tblr-facebook-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-facebook,.btn-outline.btn-facebook{--tblr-btn-color: var(--tblr-facebook);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-facebook);--tblr-btn-hover-color: var(--tblr-facebook-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-facebook);--tblr-btn-active-color: var(--tblr-facebook-fg);--tblr-btn-active-bg: var(--tblr-facebook);--tblr-btn-active-border-color: var(--tblr-facebook);--tblr-btn-disabled-color: var(--tblr-facebook);--tblr-btn-disabled-border-color: var(--tblr-facebook)}.btn-ghost-facebook,.btn-ghost.btn-facebook{--tblr-btn-color: var(--tblr-facebook);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-facebook-fg);--tblr-btn-hover-bg: var(--tblr-facebook);--tblr-btn-hover-border-color: var(--tblr-facebook);--tblr-btn-active-color: var(--tblr-facebook-fg);--tblr-btn-active-bg: var(--tblr-facebook);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-facebook);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-twitter{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-twitter-fg, #ffffff);--tblr-btn-bg: var(--tblr-twitter);--tblr-btn-hover-color: var(--tblr-twitter-fg);--tblr-btn-hover-bg: var(--tblr-twitter-darken);--tblr-btn-active-color: var(--tblr-twitter-fg);--tblr-btn-active-bg: var(--tblr-twitter-darken);--tblr-btn-disabled-bg: var(--tblr-twitter);--tblr-btn-disabled-color: var(--tblr-twitter-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-twitter,.btn-outline.btn-twitter{--tblr-btn-color: var(--tblr-twitter);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-twitter);--tblr-btn-hover-color: var(--tblr-twitter-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-twitter);--tblr-btn-active-color: var(--tblr-twitter-fg);--tblr-btn-active-bg: var(--tblr-twitter);--tblr-btn-active-border-color: var(--tblr-twitter);--tblr-btn-disabled-color: var(--tblr-twitter);--tblr-btn-disabled-border-color: var(--tblr-twitter)}.btn-ghost-twitter,.btn-ghost.btn-twitter{--tblr-btn-color: var(--tblr-twitter);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-twitter-fg);--tblr-btn-hover-bg: var(--tblr-twitter);--tblr-btn-hover-border-color: var(--tblr-twitter);--tblr-btn-active-color: var(--tblr-twitter-fg);--tblr-btn-active-bg: var(--tblr-twitter);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-twitter);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-linkedin{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-linkedin-fg, #ffffff);--tblr-btn-bg: var(--tblr-linkedin);--tblr-btn-hover-color: var(--tblr-linkedin-fg);--tblr-btn-hover-bg: var(--tblr-linkedin-darken);--tblr-btn-active-color: var(--tblr-linkedin-fg);--tblr-btn-active-bg: var(--tblr-linkedin-darken);--tblr-btn-disabled-bg: var(--tblr-linkedin);--tblr-btn-disabled-color: var(--tblr-linkedin-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-linkedin,.btn-outline.btn-linkedin{--tblr-btn-color: var(--tblr-linkedin);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-linkedin);--tblr-btn-hover-color: var(--tblr-linkedin-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-linkedin);--tblr-btn-active-color: var(--tblr-linkedin-fg);--tblr-btn-active-bg: var(--tblr-linkedin);--tblr-btn-active-border-color: var(--tblr-linkedin);--tblr-btn-disabled-color: var(--tblr-linkedin);--tblr-btn-disabled-border-color: var(--tblr-linkedin)}.btn-ghost-linkedin,.btn-ghost.btn-linkedin{--tblr-btn-color: var(--tblr-linkedin);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-linkedin-fg);--tblr-btn-hover-bg: var(--tblr-linkedin);--tblr-btn-hover-border-color: var(--tblr-linkedin);--tblr-btn-active-color: var(--tblr-linkedin-fg);--tblr-btn-active-bg: var(--tblr-linkedin);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-linkedin);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-google{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-google-fg, #ffffff);--tblr-btn-bg: var(--tblr-google);--tblr-btn-hover-color: var(--tblr-google-fg);--tblr-btn-hover-bg: var(--tblr-google-darken);--tblr-btn-active-color: var(--tblr-google-fg);--tblr-btn-active-bg: var(--tblr-google-darken);--tblr-btn-disabled-bg: var(--tblr-google);--tblr-btn-disabled-color: var(--tblr-google-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-google,.btn-outline.btn-google{--tblr-btn-color: var(--tblr-google);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-google);--tblr-btn-hover-color: var(--tblr-google-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-google);--tblr-btn-active-color: var(--tblr-google-fg);--tblr-btn-active-bg: var(--tblr-google);--tblr-btn-active-border-color: var(--tblr-google);--tblr-btn-disabled-color: var(--tblr-google);--tblr-btn-disabled-border-color: var(--tblr-google)}.btn-ghost-google,.btn-ghost.btn-google{--tblr-btn-color: var(--tblr-google);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-google-fg);--tblr-btn-hover-bg: var(--tblr-google);--tblr-btn-hover-border-color: var(--tblr-google);--tblr-btn-active-color: var(--tblr-google-fg);--tblr-btn-active-bg: var(--tblr-google);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-google);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-youtube{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-youtube-fg, #ffffff);--tblr-btn-bg: var(--tblr-youtube);--tblr-btn-hover-color: var(--tblr-youtube-fg);--tblr-btn-hover-bg: var(--tblr-youtube-darken);--tblr-btn-active-color: var(--tblr-youtube-fg);--tblr-btn-active-bg: var(--tblr-youtube-darken);--tblr-btn-disabled-bg: var(--tblr-youtube);--tblr-btn-disabled-color: var(--tblr-youtube-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-youtube,.btn-outline.btn-youtube{--tblr-btn-color: var(--tblr-youtube);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-youtube);--tblr-btn-hover-color: var(--tblr-youtube-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-youtube);--tblr-btn-active-color: var(--tblr-youtube-fg);--tblr-btn-active-bg: var(--tblr-youtube);--tblr-btn-active-border-color: var(--tblr-youtube);--tblr-btn-disabled-color: var(--tblr-youtube);--tblr-btn-disabled-border-color: var(--tblr-youtube)}.btn-ghost-youtube,.btn-ghost.btn-youtube{--tblr-btn-color: var(--tblr-youtube);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-youtube-fg);--tblr-btn-hover-bg: var(--tblr-youtube);--tblr-btn-hover-border-color: var(--tblr-youtube);--tblr-btn-active-color: var(--tblr-youtube-fg);--tblr-btn-active-bg: var(--tblr-youtube);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-youtube);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-vimeo{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-vimeo-fg, #ffffff);--tblr-btn-bg: var(--tblr-vimeo);--tblr-btn-hover-color: var(--tblr-vimeo-fg);--tblr-btn-hover-bg: var(--tblr-vimeo-darken);--tblr-btn-active-color: var(--tblr-vimeo-fg);--tblr-btn-active-bg: var(--tblr-vimeo-darken);--tblr-btn-disabled-bg: var(--tblr-vimeo);--tblr-btn-disabled-color: var(--tblr-vimeo-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-vimeo,.btn-outline.btn-vimeo{--tblr-btn-color: var(--tblr-vimeo);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-vimeo);--tblr-btn-hover-color: var(--tblr-vimeo-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-vimeo);--tblr-btn-active-color: var(--tblr-vimeo-fg);--tblr-btn-active-bg: var(--tblr-vimeo);--tblr-btn-active-border-color: var(--tblr-vimeo);--tblr-btn-disabled-color: var(--tblr-vimeo);--tblr-btn-disabled-border-color: var(--tblr-vimeo)}.btn-ghost-vimeo,.btn-ghost.btn-vimeo{--tblr-btn-color: var(--tblr-vimeo);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-vimeo-fg);--tblr-btn-hover-bg: var(--tblr-vimeo);--tblr-btn-hover-border-color: var(--tblr-vimeo);--tblr-btn-active-color: var(--tblr-vimeo-fg);--tblr-btn-active-bg: var(--tblr-vimeo);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-vimeo);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-dribbble{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-dribbble-fg, #ffffff);--tblr-btn-bg: var(--tblr-dribbble);--tblr-btn-hover-color: var(--tblr-dribbble-fg);--tblr-btn-hover-bg: var(--tblr-dribbble-darken);--tblr-btn-active-color: var(--tblr-dribbble-fg);--tblr-btn-active-bg: var(--tblr-dribbble-darken);--tblr-btn-disabled-bg: var(--tblr-dribbble);--tblr-btn-disabled-color: var(--tblr-dribbble-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-dribbble,.btn-outline.btn-dribbble{--tblr-btn-color: var(--tblr-dribbble);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-dribbble);--tblr-btn-hover-color: var(--tblr-dribbble-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-dribbble);--tblr-btn-active-color: var(--tblr-dribbble-fg);--tblr-btn-active-bg: var(--tblr-dribbble);--tblr-btn-active-border-color: var(--tblr-dribbble);--tblr-btn-disabled-color: var(--tblr-dribbble);--tblr-btn-disabled-border-color: var(--tblr-dribbble)}.btn-ghost-dribbble,.btn-ghost.btn-dribbble{--tblr-btn-color: var(--tblr-dribbble);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-dribbble-fg);--tblr-btn-hover-bg: var(--tblr-dribbble);--tblr-btn-hover-border-color: var(--tblr-dribbble);--tblr-btn-active-color: var(--tblr-dribbble-fg);--tblr-btn-active-bg: var(--tblr-dribbble);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-dribbble);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-github{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-github-fg, #ffffff);--tblr-btn-bg: var(--tblr-github);--tblr-btn-hover-color: var(--tblr-github-fg);--tblr-btn-hover-bg: var(--tblr-github-darken);--tblr-btn-active-color: var(--tblr-github-fg);--tblr-btn-active-bg: var(--tblr-github-darken);--tblr-btn-disabled-bg: var(--tblr-github);--tblr-btn-disabled-color: var(--tblr-github-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-github,.btn-outline.btn-github{--tblr-btn-color: var(--tblr-github);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-github);--tblr-btn-hover-color: var(--tblr-github-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-github);--tblr-btn-active-color: var(--tblr-github-fg);--tblr-btn-active-bg: var(--tblr-github);--tblr-btn-active-border-color: var(--tblr-github);--tblr-btn-disabled-color: var(--tblr-github);--tblr-btn-disabled-border-color: var(--tblr-github)}.btn-ghost-github,.btn-ghost.btn-github{--tblr-btn-color: var(--tblr-github);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-github-fg);--tblr-btn-hover-bg: var(--tblr-github);--tblr-btn-hover-border-color: var(--tblr-github);--tblr-btn-active-color: var(--tblr-github-fg);--tblr-btn-active-bg: var(--tblr-github);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-github);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-instagram{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-instagram-fg, #ffffff);--tblr-btn-bg: var(--tblr-instagram);--tblr-btn-hover-color: var(--tblr-instagram-fg);--tblr-btn-hover-bg: var(--tblr-instagram-darken);--tblr-btn-active-color: var(--tblr-instagram-fg);--tblr-btn-active-bg: var(--tblr-instagram-darken);--tblr-btn-disabled-bg: var(--tblr-instagram);--tblr-btn-disabled-color: var(--tblr-instagram-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-instagram,.btn-outline.btn-instagram{--tblr-btn-color: var(--tblr-instagram);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-instagram);--tblr-btn-hover-color: var(--tblr-instagram-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-instagram);--tblr-btn-active-color: var(--tblr-instagram-fg);--tblr-btn-active-bg: var(--tblr-instagram);--tblr-btn-active-border-color: var(--tblr-instagram);--tblr-btn-disabled-color: var(--tblr-instagram);--tblr-btn-disabled-border-color: var(--tblr-instagram)}.btn-ghost-instagram,.btn-ghost.btn-instagram{--tblr-btn-color: var(--tblr-instagram);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-instagram-fg);--tblr-btn-hover-bg: var(--tblr-instagram);--tblr-btn-hover-border-color: var(--tblr-instagram);--tblr-btn-active-color: var(--tblr-instagram-fg);--tblr-btn-active-bg: var(--tblr-instagram);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-instagram);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-pinterest{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-pinterest-fg, #ffffff);--tblr-btn-bg: var(--tblr-pinterest);--tblr-btn-hover-color: var(--tblr-pinterest-fg);--tblr-btn-hover-bg: var(--tblr-pinterest-darken);--tblr-btn-active-color: var(--tblr-pinterest-fg);--tblr-btn-active-bg: var(--tblr-pinterest-darken);--tblr-btn-disabled-bg: var(--tblr-pinterest);--tblr-btn-disabled-color: var(--tblr-pinterest-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-pinterest,.btn-outline.btn-pinterest{--tblr-btn-color: var(--tblr-pinterest);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-pinterest);--tblr-btn-hover-color: var(--tblr-pinterest-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-pinterest);--tblr-btn-active-color: var(--tblr-pinterest-fg);--tblr-btn-active-bg: var(--tblr-pinterest);--tblr-btn-active-border-color: var(--tblr-pinterest);--tblr-btn-disabled-color: var(--tblr-pinterest);--tblr-btn-disabled-border-color: var(--tblr-pinterest)}.btn-ghost-pinterest,.btn-ghost.btn-pinterest{--tblr-btn-color: var(--tblr-pinterest);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-pinterest-fg);--tblr-btn-hover-bg: var(--tblr-pinterest);--tblr-btn-hover-border-color: var(--tblr-pinterest);--tblr-btn-active-color: var(--tblr-pinterest-fg);--tblr-btn-active-bg: var(--tblr-pinterest);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-pinterest);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-vk{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-vk-fg, #ffffff);--tblr-btn-bg: var(--tblr-vk);--tblr-btn-hover-color: var(--tblr-vk-fg);--tblr-btn-hover-bg: var(--tblr-vk-darken);--tblr-btn-active-color: var(--tblr-vk-fg);--tblr-btn-active-bg: var(--tblr-vk-darken);--tblr-btn-disabled-bg: var(--tblr-vk);--tblr-btn-disabled-color: var(--tblr-vk-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-vk,.btn-outline.btn-vk{--tblr-btn-color: var(--tblr-vk);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-vk);--tblr-btn-hover-color: var(--tblr-vk-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-vk);--tblr-btn-active-color: var(--tblr-vk-fg);--tblr-btn-active-bg: var(--tblr-vk);--tblr-btn-active-border-color: var(--tblr-vk);--tblr-btn-disabled-color: var(--tblr-vk);--tblr-btn-disabled-border-color: var(--tblr-vk)}.btn-ghost-vk,.btn-ghost.btn-vk{--tblr-btn-color: var(--tblr-vk);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-vk-fg);--tblr-btn-hover-bg: var(--tblr-vk);--tblr-btn-hover-border-color: var(--tblr-vk);--tblr-btn-active-color: var(--tblr-vk-fg);--tblr-btn-active-bg: var(--tblr-vk);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-vk);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-rss{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-rss-fg, #ffffff);--tblr-btn-bg: var(--tblr-rss);--tblr-btn-hover-color: var(--tblr-rss-fg);--tblr-btn-hover-bg: var(--tblr-rss-darken);--tblr-btn-active-color: var(--tblr-rss-fg);--tblr-btn-active-bg: var(--tblr-rss-darken);--tblr-btn-disabled-bg: var(--tblr-rss);--tblr-btn-disabled-color: var(--tblr-rss-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-rss,.btn-outline.btn-rss{--tblr-btn-color: var(--tblr-rss);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-rss);--tblr-btn-hover-color: var(--tblr-rss-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-rss);--tblr-btn-active-color: var(--tblr-rss-fg);--tblr-btn-active-bg: var(--tblr-rss);--tblr-btn-active-border-color: var(--tblr-rss);--tblr-btn-disabled-color: var(--tblr-rss);--tblr-btn-disabled-border-color: var(--tblr-rss)}.btn-ghost-rss,.btn-ghost.btn-rss{--tblr-btn-color: var(--tblr-rss);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-rss-fg);--tblr-btn-hover-bg: var(--tblr-rss);--tblr-btn-hover-border-color: var(--tblr-rss);--tblr-btn-active-color: var(--tblr-rss-fg);--tblr-btn-active-bg: var(--tblr-rss);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-rss);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-flickr{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-flickr-fg, #ffffff);--tblr-btn-bg: var(--tblr-flickr);--tblr-btn-hover-color: var(--tblr-flickr-fg);--tblr-btn-hover-bg: var(--tblr-flickr-darken);--tblr-btn-active-color: var(--tblr-flickr-fg);--tblr-btn-active-bg: var(--tblr-flickr-darken);--tblr-btn-disabled-bg: var(--tblr-flickr);--tblr-btn-disabled-color: var(--tblr-flickr-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-flickr,.btn-outline.btn-flickr{--tblr-btn-color: var(--tblr-flickr);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-flickr);--tblr-btn-hover-color: var(--tblr-flickr-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-flickr);--tblr-btn-active-color: var(--tblr-flickr-fg);--tblr-btn-active-bg: var(--tblr-flickr);--tblr-btn-active-border-color: var(--tblr-flickr);--tblr-btn-disabled-color: var(--tblr-flickr);--tblr-btn-disabled-border-color: var(--tblr-flickr)}.btn-ghost-flickr,.btn-ghost.btn-flickr{--tblr-btn-color: var(--tblr-flickr);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-flickr-fg);--tblr-btn-hover-bg: var(--tblr-flickr);--tblr-btn-hover-border-color: var(--tblr-flickr);--tblr-btn-active-color: var(--tblr-flickr-fg);--tblr-btn-active-bg: var(--tblr-flickr);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-flickr);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-bitbucket{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-bitbucket-fg, #ffffff);--tblr-btn-bg: var(--tblr-bitbucket);--tblr-btn-hover-color: var(--tblr-bitbucket-fg);--tblr-btn-hover-bg: var(--tblr-bitbucket-darken);--tblr-btn-active-color: var(--tblr-bitbucket-fg);--tblr-btn-active-bg: var(--tblr-bitbucket-darken);--tblr-btn-disabled-bg: var(--tblr-bitbucket);--tblr-btn-disabled-color: var(--tblr-bitbucket-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-bitbucket,.btn-outline.btn-bitbucket{--tblr-btn-color: var(--tblr-bitbucket);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-bitbucket);--tblr-btn-hover-color: var(--tblr-bitbucket-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-bitbucket);--tblr-btn-active-color: var(--tblr-bitbucket-fg);--tblr-btn-active-bg: var(--tblr-bitbucket);--tblr-btn-active-border-color: var(--tblr-bitbucket);--tblr-btn-disabled-color: var(--tblr-bitbucket);--tblr-btn-disabled-border-color: var(--tblr-bitbucket)}.btn-ghost-bitbucket,.btn-ghost.btn-bitbucket{--tblr-btn-color: var(--tblr-bitbucket);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-bitbucket-fg);--tblr-btn-hover-bg: var(--tblr-bitbucket);--tblr-btn-hover-border-color: var(--tblr-bitbucket);--tblr-btn-active-color: var(--tblr-bitbucket-fg);--tblr-btn-active-bg: var(--tblr-bitbucket);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-bitbucket);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-tabler{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-tabler-fg, #ffffff);--tblr-btn-bg: var(--tblr-tabler);--tblr-btn-hover-color: var(--tblr-tabler-fg);--tblr-btn-hover-bg: var(--tblr-tabler-darken);--tblr-btn-active-color: var(--tblr-tabler-fg);--tblr-btn-active-bg: var(--tblr-tabler-darken);--tblr-btn-disabled-bg: var(--tblr-tabler);--tblr-btn-disabled-color: var(--tblr-tabler-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-tabler,.btn-outline.btn-tabler{--tblr-btn-color: var(--tblr-tabler);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-tabler);--tblr-btn-hover-color: var(--tblr-tabler-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-tabler);--tblr-btn-active-color: var(--tblr-tabler-fg);--tblr-btn-active-bg: var(--tblr-tabler);--tblr-btn-active-border-color: var(--tblr-tabler);--tblr-btn-disabled-color: var(--tblr-tabler);--tblr-btn-disabled-border-color: var(--tblr-tabler)}.btn-ghost-tabler,.btn-ghost.btn-tabler{--tblr-btn-color: var(--tblr-tabler);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-tabler-fg);--tblr-btn-hover-bg: var(--tblr-tabler);--tblr-btn-hover-border-color: var(--tblr-tabler);--tblr-btn-active-color: var(--tblr-tabler-fg);--tblr-btn-active-bg: var(--tblr-tabler);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-tabler);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-sm,.btn-group-sm>.btn{--tblr-btn-line-height: 1.3333333333;--tblr-btn-icon-size: 1rem}.btn-lg,.btn-group-lg>.btn{--tblr-btn-line-height: 1.5rem;--tblr-btn-icon-size: 1.5rem}.btn-xl,.btn-group-xl>.btn{--tblr-btn-line-height: 2;--tblr-btn-icon-size: 2rem;--tblr-btn-padding-y: .6875rem;--tblr-btn-padding-x: 2rem;--tblr-btn-font-size: 1.5rem}.btn-pill{padding-right:1.5em;padding-left:1.5em;border-radius:10rem}.btn-pill[class*=btn-icon]{padding:.375rem 15px}.btn-square{border-radius:0}.btn-icon,.btn-action{padding-left:0;padding-right:0}.btn-icon .icon,.btn-action .icon{margin:calc(-1 * var(--tblr-btn-padding-x))}.btn-list{--tblr-list-gap: .5rem;display:flex;flex-wrap:wrap;gap:var(--tblr-list-gap)}.btn-floating{position:fixed;z-index:1030;bottom:1rem;left:1rem;box-shadow:var(--tblr-shadow-dropdown)}.btn-loading{position:relative;color:transparent!important;text-shadow:none!important;pointer-events:none}.btn-loading>*{opacity:0}.btn-loading:after{content:"";display:inline-block;vertical-align:text-bottom;border:2px var(--tblr-border-style) currentColor;border-right-color:transparent;border-radius:100rem;color:var(--tblr-btn-color);position:absolute;width:var(--tblr-btn-icon-size);height:var(--tblr-btn-icon-size);left:calc(50% - var(--tblr-btn-icon-size) / 2);top:calc(50% - var(--tblr-btn-icon-size) / 2);animation:spinner-border .75s linear infinite}.btn-action{--tblr-border-color: transparent;color:var(--tblr-secondary);border-radius:var(--tblr-border-radius);background:transparent;box-shadow:none}.btn-action:after{content:none}.btn-action:focus{outline:none;box-shadow:none}.btn-action:hover,.btn-action.show{color:var(--tblr-body-color);background:var(--tblr-active-bg);border-color:transparent}.btn-action.show{color:var(--tblr-primary)}.btn-actions{display:flex}.btn-animate-icon .icon{transition:transform .3s ease}.btn-animate-icon:hover .icon,.btn-animate-icon:focus-visible .icon{transform:translate(4px)}.btn-animate-icon.btn-animate-icon-rotate:hover .icon,.btn-animate-icon.btn-animate-icon-rotate:focus-visible .icon{transform:rotate(90deg)}.btn-animate-icon.btn-animate-icon-move-start:hover .icon,.btn-animate-icon.btn-animate-icon-move-start:focus-visible .icon{transform:translate(-4px)}.btn-animate-icon.btn-animate-icon-pulse:hover .icon,.btn-animate-icon.btn-animate-icon-pulse:focus-visible .icon{transform:none;animation:pulse .9s}.btn-animate-icon.btn-animate-icon-shake:hover .icon,.btn-animate-icon.btn-animate-icon-shake:focus-visible .icon{transform:none;animation:shake .9s}.btn-animate-icon.btn-animate-icon-tada:hover .icon,.btn-animate-icon.btn-animate-icon-tada:focus-visible .icon{transform:none;animation:tada .9s}.btn-group,.btn-group-vertical{box-shadow:var(--tblr-shadow-input)}.btn-group>.btn-check:checked+.btn,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:5}.btn-group>.btn-check:focus+.btn,.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus{z-index:1}.calendar{display:block;font-size:.765625rem;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius)}.calendar-nav{display:flex;align-items:center}.calendar-title{flex:1;text-align:center}.calendar-body,.calendar-header{display:flex;flex-wrap:wrap;justify-content:flex-start;padding:.5rem 0}.calendar-header{color:var(--tblr-secondary)}.calendar-date{flex:0 0 14.2857142857%;max-width:14.2857142857%;padding:.2rem;text-align:center;border:0}.calendar-date.prev-month,.calendar-date.next-month{opacity:.25}.calendar-date .date-item{position:relative;display:inline-block;width:1.4rem;height:1.4rem;line-height:1.4rem;color:#66758c;text-align:center;text-decoration:none;white-space:nowrap;vertical-align:middle;cursor:pointer;background:0 0;border:var(--tblr-border-width) var(--tblr-border-style) transparent;border-radius:100rem;outline:0;transition:background .3s,border .3s,box-shadow .32s,color .3s}@media(prefers-reduced-motion:reduce){.calendar-date .date-item{transition:none}}.calendar-date .date-item:hover{color:var(--tblr-primary);text-decoration:none;background:#fefeff;border-color:var(--tblr-border-color)}.calendar-date .date-today{color:var(--tblr-primary);border-color:var(--tblr-border-color)}.calendar-range{position:relative}.calendar-range:before{position:absolute;top:50%;right:0;left:0;height:1.4rem;content:"";background:rgba(var(--tblr-primary-rgb),.1);transform:translateY(-50%)}.calendar-range.range-start .date-item,.calendar-range.range-end .date-item{color:#fff;background:var(--tblr-primary);border-color:var(--tblr-primary)}.calendar-range.range-start:before{left:50%}.calendar-range.range-end:before{right:50%}.carousel-indicators-vertical{left:auto;top:0;margin:0 1rem 0 0;flex-direction:column}.carousel-indicators-vertical [data-bs-target]{margin:3px 0;width:3px;height:30px;border:0;border-left:10px var(--tblr-border-style) transparent;border-right:10px var(--tblr-border-style) transparent}.carousel-indicators-dot [data-bs-target]{width:.5rem;height:.5rem;border-radius:100rem;border:10px var(--tblr-border-style) transparent;margin:0}.carousel-indicators-thumb [data-bs-target]{width:2rem;height:auto;background:no-repeat center/cover;border:0;border-radius:var(--tblr-border-radius);box-shadow:rgba(var(--tblr-body-color-rgb),.04) 0 2px 4px;margin:0 3px;opacity:.75}@media(min-width:992px){.carousel-indicators-thumb [data-bs-target]{width:4rem}}.carousel-indicators-thumb [data-bs-target]:before{content:"";padding-top:var(--tblr-aspect-ratio, 100%);display:block}.carousel-indicators-thumb.carousel-indicators-vertical [data-bs-target]{margin:3px 0}.carousel-caption-background{background:red;position:absolute;left:0;right:0;bottom:0;height:90%;background:linear-gradient(0deg,#1f2937e6,#1f293700)}.card{transition:transform .3s ease-out,opacity .3s ease-out,box-shadow .3s ease-out}@media(prefers-reduced-motion:reduce){.card{transition:none}}@media print{.card{border:none;box-shadow:none}}a.card{color:inherit}a.card:hover{text-decoration:none;box-shadow:rgba(var(--tblr-body-color-rgb),.16) 0 2px 16px}.card .card{box-shadow:none}.card-borderless,.card-borderless .card-header,.card-borderless .card-footer{border-color:transparent}.card-stamp{--tblr-stamp-size: 7rem;position:absolute;top:0;right:0;width:calc(var(--tblr-stamp-size) * 1);height:calc(var(--tblr-stamp-size) * 1);max-height:100%;border-top-right-radius:6px;opacity:.2;overflow:hidden;pointer-events:none}.card-stamp-lg{--tblr-stamp-size: 13rem}.card-stamp-icon{background:var(--tblr-secondary);color:var(--tblr-card-bg, var(--tblr-bg-surface));display:flex;align-items:center;justify-content:center;border-radius:100rem;width:calc(var(--tblr-stamp-size) * 1);height:calc(var(--tblr-stamp-size) * 1);position:relative;top:calc(var(--tblr-stamp-size) * -.25);right:calc(var(--tblr-stamp-size) * -.25);font-size:calc(var(--tblr-stamp-size) * .75);transform:rotate(10deg)}.card-stamp-icon .icon{stroke-width:2;width:calc(var(--tblr-stamp-size) * .75);height:calc(var(--tblr-stamp-size) * .75)}.card-img,.card-img-start{border-top-left-radius:calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)));border-bottom-left-radius:calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)))}.card-img,.card-img-end{border-top-right-radius:calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)));border-bottom-right-radius:calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)))}.card-img-overlay{display:flex;flex-direction:column;justify-content:flex-end}.card-img-overlay-dark{background-image:linear-gradient(180deg,#0000,#0009)}.card-inactive{pointer-events:none;box-shadow:none}.card-inactive .card-body{opacity:.64}.card-active{--tblr-card-border-color: var(--tblr-primary);--tblr-card-bg: var(--tblr-active-bg)}.card-btn{display:flex;align-items:center;justify-content:center;padding:1rem 1.25rem;text-align:center;transition:background .3s}@media(prefers-reduced-motion:reduce){.card-btn{transition:none}}.card-btn{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);flex:1;color:inherit;font-weight:var(--tblr-font-weight-medium)}.card-btn:hover{text-decoration:none;background:rgba(var(--tblr-primary-rgb),.04)}.card-btn+.card-btn{border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.card-stacked{--tblr-card-stacked-offset: .25rem;position:relative}.card-stacked:after{position:absolute;top:calc(-1 * var(--tblr-card-stacked-offset));right:var(--tblr-card-stacked-offset);left:var(--tblr-card-stacked-offset);height:var(--tblr-card-stacked-offset);content:"";background:var(--tblr-card-bg, var(--tblr-bg-surface));border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-card-border-color);border-radius:var(--tblr-card-border-radius) var(--tblr-card-border-radius) 0 0}.card-cover{position:relative;padding:1rem 1.25rem;background:#666 no-repeat center/cover}.card-cover:before{position:absolute;inset:0;content:"";background:#1f29377a}.card-cover:first-child,.card-cover:first-child:before{border-radius:6px 6px 0 0}.card-cover-blurred:before{backdrop-filter:blur(2px)}.card-actions{margin:-.5rem -.5rem -.5rem auto;padding-left:.5rem}.card-actions a{text-decoration:none}.card-header{color:inherit;display:flex;align-items:center;background:transparent}.card-header:first-child{border-radius:var(--tblr-card-border-radius) var(--tblr-card-border-radius) 0 0}.card-header-light{border-bottom-color:transparent;background:var(--tblr-bg-surface-tertiary)}.card-header-tabs{background:var(--tblr-bg-surface-tertiary);flex:1;margin:calc(var(--tblr-card-cap-padding-y) * -1) calc(var(--tblr-card-cap-padding-x) * -1) calc(var(--tblr-card-cap-padding-y) * -1);padding:calc(var(--tblr-card-cap-padding-y) * .5) calc(var(--tblr-card-cap-padding-x) * .5) 0;border-radius:var(--tblr-card-border-radius) var(--tblr-card-border-radius) 0 0}.card-header-pills{flex:1;margin-top:-.5rem;margin-bottom:-.5rem}.card-rotate-left,.card-rotate-start{transform:rotate(-1.5deg)}.card-rotate-right,.card-rotate-end{transform:rotate(1.5deg)}.card-link{color:inherit}.card-link:hover{color:inherit;text-decoration:none;box-shadow:0 1px 6px #00000014}.card-link-rotate:hover{transform:rotate(1.5deg);opacity:1}.card-link-pop:hover{transform:translateY(-2px);opacity:1}.card-footer{margin-top:auto}.card-footer:last-child{border-radius:0 0 var(--tblr-card-border-radius) var(--tblr-card-border-radius)}.card-footer-transparent{background:transparent;border-color:transparent;padding-top:0}.card-footer-borderless{border-top:none}.card-progress{height:.25rem}.card-progress:last-child{border-radius:0 0 2px 2px}.card-progress:first-child{border-radius:2px 2px 0 0}.card-meta{color:var(--tblr-secondary)}.card-title{display:block;margin:0 0 1rem;font-size:1rem;font-weight:var(--tblr-font-weight-medium);color:inherit;line-height:1.5rem}a.card-title:hover{color:inherit}.card-header .card-title{margin:0}.card-subtitle{margin-bottom:1.25rem;color:var(--tblr-secondary);font-weight:400}.card-header .card-subtitle{margin:0}.card-title .card-subtitle{margin:0 0 0 .25rem;font-size:.875rem}.card-body{position:relative}.card-body>:last-child{margin-bottom:0}.card-sm>.card-body{padding:1rem}@media(min-width:768px){.card-md>.card-body{padding:2.5rem}}@media(min-width:768px){.card-lg>.card-body{padding:2rem}}@media(min-width:992px){.card-lg>.card-body{padding:4rem}}@media print{.card-body{padding:0}}.card-body+.card-body{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.card-body-scrollable{overflow:auto}.card-options{top:1.5rem;right:.75rem;display:flex;margin-left:auto}.card-options-link{display:inline-block;min-width:1rem;margin-left:.25rem;color:var(--tblr-secondary)}.card-status-top{position:absolute;top:0;right:0;left:0;height:2px;border-radius:var(--tblr-card-border-radius) var(--tblr-card-border-radius) 0 0}.card-status-start{position:absolute;right:auto;bottom:0;width:2px;height:100%;border-radius:var(--tblr-card-border-radius) 0 0 var(--tblr-card-border-radius)}.card-status-bottom{position:absolute;top:initial;bottom:0;width:100%;height:2px;border-radius:0 0 var(--tblr-card-border-radius) var(--tblr-card-border-radius)}.card-table{margin-bottom:0!important}.card-table tr td:first-child,.card-table tr th:first-child{padding-left:1.25rem;border-left:0}.card-table tr td:last-child,.card-table tr th:last-child{padding-right:1.25rem;border-right:0}.card-table thead tr:first-child,.card-table tbody tr:first-child,.card-table tfoot tr:first-child,.card-table thead tr:first-child td,.card-table thead tr:first-child th,.card-table tbody tr:first-child td,.card-table tbody tr:first-child th,.card-table tfoot tr:first-child td,.card-table tfoot tr:first-child th{border-top:0}.card-body+.card-table{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-table-border-color)}.card-code{padding:0}.card-code .highlight{margin:0;border:0}.card-code pre{margin:0!important;border:0!important}.card-chart{position:relative;z-index:1;height:3.5rem}.card-avatar{margin-left:auto;margin-right:auto;box-shadow:0 0 0 .25rem var(--tblr-card-bg, var(--tblr-bg-surface));margin-top:calc(-1 * var(--tblr-avatar-size) * .5)}.card-body+.card-list-group{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.card-list-group .list-group-item{padding-right:1.25rem;padding-left:1.25rem;border-right:0;border-left:0;border-radius:0}.card-list-group .list-group-item:last-child{border-bottom:0}.card-list-group .list-group-item:first-child{border-top:0}.card-tabs .nav-tabs{position:relative;z-index:1000;border-bottom:0}.card-tabs .nav-tabs .nav-link{background:var(--tblr-bg-surface-tertiary);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.card-tabs .nav-tabs .nav-link.active,.card-tabs .nav-tabs .nav-link:active,.card-tabs .nav-tabs .nav-link:hover{border-color:var(--tblr-border-color-translucent);color:var(--tblr-body-color)}.card-tabs .nav-tabs .nav-link.active{color:inherit;background:var(--tblr-card-bg, var(--tblr-bg-surface));border-bottom-color:transparent}.card-tabs .nav-tabs .nav-item:not(:first-child) .nav-link{border-top-left-radius:0}.card-tabs .nav-tabs .nav-item:not(:last-child) .nav-link{border-top-right-radius:0}.card-tabs .nav-tabs .nav-item+.nav-item{margin-left:calc(-1 * var(--tblr-border-width))}.card-tabs .nav-tabs-bottom,.card-tabs .nav-tabs-bottom .nav-link{margin-bottom:0}.card-tabs .nav-tabs-bottom .nav-link.active{border-top-color:transparent}.card-tabs .nav-tabs-bottom .nav-item{margin-top:calc(-1 * var(--tblr-border-width));margin-bottom:0}.card-tabs .nav-tabs-bottom .nav-item .nav-link{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);border-radius:0 0 var(--tblr-border-radius-lg) var(--tblr-border-radius-lg)}.card-tabs .nav-tabs-bottom .nav-item:not(:first-child) .nav-link{border-bottom-left-radius:0}.card-tabs .nav-tabs-bottom .nav-item:not(:last-child) .nav-link{border-bottom-right-radius:0}.card-tabs .card{border-bottom-left-radius:0}.card-tabs .nav-tabs+.tab-content .card{border-bottom-left-radius:var(--tblr-card-border-radius);border-top-left-radius:0}.card-note{--tblr-card-bg: #fff7dd;--tblr-card-border-color: #fff1c9}.btn-close{--tblr-btn-close-color: currentColor;--tblr-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%231f2937'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e");--tblr-btn-close-opacity: .4;--tblr-btn-close-hover-opacity: .75;--tblr-btn-close-focus-shadow: 0 0 0 .25rem rgba(var(--tblr-primary-rgb), .25);--tblr-btn-close-focus-opacity: 1;--tblr-btn-close-disabled-opacity: .25;--tblr-btn-close-size: 1em;width:var(--tblr-btn-close-size);height:var(--tblr-btn-close-size);padding:.25em;color:var(--tblr-btn-close-color);mask:var(--tblr-btn-close-bg) no-repeat center/calc(var(--tblr-btn-close-size) * .75);background-color:var(--tblr-btn-close-color);border:0;border-radius:var(--tblr-border-radius);opacity:var(--tblr-btn-close-opacity);cursor:pointer;display:block}.btn-close:hover{color:var(--tblr-btn-close-color);text-decoration:none;opacity:var(--tblr-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--tblr-btn-close-focus-shadow);opacity:var(--tblr-btn-close-focus-opacity)}.btn-close:disabled,.btn-close.disabled{pointer-events:none;user-select:none;opacity:var(--tblr-btn-close-disabled-opacity)}.dropdown-menu{user-select:none;background-clip:border-box}.dropdown-menu.card{padding:0;min-width:25rem;display:none}.dropdown-menu.card.show{display:flex}.dropdown-item{min-width:11rem;display:flex;align-items:center;margin:0;line-height:1.4285714286;gap:.5rem}.dropdown-item-icon{width:1.25rem!important;height:1.25rem!important;margin-right:.5rem;color:var(--tblr-secondary);opacity:.7;text-align:center}.dropdown-item-indicator{margin-right:.5rem;margin-left:-.25rem;height:1.25rem;display:inline-flex;line-height:1;vertical-align:bottom;align-items:center}.dropdown-header{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);padding-bottom:.25rem;pointer-events:none}.dropdown-menu-scrollable{height:auto;max-height:13rem;overflow-x:hidden}.dropdown-menu-column{min-width:11rem}.dropdown-menu-column .dropdown-item{min-width:0}.dropdown-menu-columns{display:flex;flex:0 .25rem}.dropdown-menu-arrow:before{content:"";position:absolute;top:-.25rem;left:.75rem;display:block;background:inherit;width:14px;height:14px;transform:rotate(45deg);transform-origin:center;border:1px solid;border-color:inherit;z-index:-1;clip:rect(0px,9px,9px,0px)}.dropdown-menu-arrow.dropdown-menu-end:before{right:.75rem;left:auto}.dropend>.dropdown-menu{margin-top:calc(-.25rem - 1px);margin-left:-.25rem}.dropend .dropdown-toggle:after{margin-left:auto}.dropdown-menu-card{padding:0;min-width:20rem}.dropdown-menu-card>.card{margin:0;border:0;box-shadow:none}.datagrid{--tblr-datagrid-padding: 1.5rem;--tblr-datagrid-item-width: 15rem;display:grid;grid-gap:var(--tblr-datagrid-padding);grid-template-columns:repeat(auto-fit,minmax(var(--tblr-datagrid-item-width),1fr))}.datagrid-title{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);margin-bottom:.25rem}.empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:1rem;text-align:center}@media(min-width:768px){.empty{padding:3rem}}.empty-icon{margin:0 0 1rem;width:3rem;height:3rem;line-height:1;color:var(--tblr-secondary)}.empty-icon svg{width:100%;height:100%}.empty-img{margin:0 0 2rem;line-height:1}.empty-header{margin:0 0 1rem;font-size:4rem;font-weight:var(--tblr-font-weight-light);line-height:1;color:var(--tblr-secondary)}.empty-title{font-size:1.25rem;line-height:1.75rem;font-weight:var(--tblr-font-weight-bold)}.empty-title,.empty-subtitle{margin:0 0 .5rem}.empty-action{margin-top:1.5rem}.empty-bordered{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius)}.row>*{min-width:0}.col-separator{border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.container-slim{--tblr-gutter-x: calc(var(--tblr-page-padding) * 2);--tblr-gutter-y: 0;width:100%;padding-right:calc(var(--tblr-gutter-x) * .5);padding-left:calc(var(--tblr-gutter-x) * .5);margin-right:auto;margin-left:auto;max-width:16rem}.container-tight{--tblr-gutter-x: calc(var(--tblr-page-padding) * 2);--tblr-gutter-y: 0;width:100%;padding-right:calc(var(--tblr-gutter-x) * .5);padding-left:calc(var(--tblr-gutter-x) * .5);margin-right:auto;margin-left:auto;max-width:30rem}.container-narrow{--tblr-gutter-x: calc(var(--tblr-page-padding) * 2);--tblr-gutter-y: 0;width:100%;padding-right:calc(var(--tblr-gutter-x) * .5);padding-left:calc(var(--tblr-gutter-x) * .5);margin-right:auto;margin-left:auto;max-width:61.875rem}.row-0{margin-right:0;margin-left:0}.row-0>.col,.row-0>[class*=col-]{padding-right:0;padding-left:0}.row-0 .card{margin-bottom:0}.row-sm{margin-right:-.375rem;margin-left:-.375rem}.row-sm>.col,.row-sm>[class*=col-]{padding-right:.375rem;padding-left:.375rem}.row-sm .card{margin-bottom:.75rem}.row-md{margin-right:-1.5rem;margin-left:-1.5rem}.row-md>.col,.row-md>[class*=col-]{padding-right:1.5rem;padding-left:1.5rem}.row-md .card{margin-bottom:3rem}.row-lg{margin-right:-3rem;margin-left:-3rem}.row-lg>.col,.row-lg>[class*=col-]{padding-right:3rem;padding-left:3rem}.row-lg .card{margin-bottom:6rem}.row-deck>.col,.row-deck>[class*=col-]{display:flex;align-items:stretch}.row-deck>.col .card,.row-deck>[class*=col-] .card{flex:1 1 auto}.row-cards{--tblr-gutter-x: var(--tblr-page-padding);--tblr-gutter-y: var(--tblr-page-padding);min-width:0}.row-cards .row-cards{flex:1}.space-y{display:flex;flex-direction:column;gap:1rem}.space-x{display:flex;gap:1rem}.space-y-0{display:flex;flex-direction:column;gap:0}.space-x-0{display:flex;gap:0}.space-y-1{display:flex;flex-direction:column;gap:.25rem}.space-x-1{display:flex;gap:.25rem}.space-y-2{display:flex;flex-direction:column;gap:.5rem}.space-x-2{display:flex;gap:.5rem}.space-y-3{display:flex;flex-direction:column;gap:1rem}.space-x-3{display:flex;gap:1rem}.space-y-4{display:flex;flex-direction:column;gap:1.5rem}.space-x-4{display:flex;gap:1.5rem}.space-y-5{display:flex;flex-direction:column;gap:2rem}.space-x-5{display:flex;gap:2rem}.space-y-6{display:flex;flex-direction:column;gap:2.5rem}.space-x-6{display:flex;gap:2.5rem}.divide-y>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y>:not(template):not(:first-child){padding-top:1rem!important}.divide-y>:not(template):not(:last-child){padding-bottom:1rem!important}.divide-x>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x>:not(template):not(:first-child){padding-left:1rem!important}.divide-x>:not(template):not(:last-child){padding-right:1rem!important}.divide-y-0>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-0>:not(template):not(:first-child){padding-top:0!important}.divide-y-0>:not(template):not(:last-child){padding-bottom:0!important}.divide-x-0>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-0>:not(template):not(:first-child){padding-left:0!important}.divide-x-0>:not(template):not(:last-child){padding-right:0!important}.divide-y-1>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-1>:not(template):not(:first-child){padding-top:.25rem!important}.divide-y-1>:not(template):not(:last-child){padding-bottom:.25rem!important}.divide-x-1>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-1>:not(template):not(:first-child){padding-left:.25rem!important}.divide-x-1>:not(template):not(:last-child){padding-right:.25rem!important}.divide-y-2>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-2>:not(template):not(:first-child){padding-top:.5rem!important}.divide-y-2>:not(template):not(:last-child){padding-bottom:.5rem!important}.divide-x-2>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-2>:not(template):not(:first-child){padding-left:.5rem!important}.divide-x-2>:not(template):not(:last-child){padding-right:.5rem!important}.divide-y-3>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-3>:not(template):not(:first-child){padding-top:1rem!important}.divide-y-3>:not(template):not(:last-child){padding-bottom:1rem!important}.divide-x-3>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-3>:not(template):not(:first-child){padding-left:1rem!important}.divide-x-3>:not(template):not(:last-child){padding-right:1rem!important}.divide-y-4>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-4>:not(template):not(:first-child){padding-top:1.5rem!important}.divide-y-4>:not(template):not(:last-child){padding-bottom:1.5rem!important}.divide-x-4>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-4>:not(template):not(:first-child){padding-left:1.5rem!important}.divide-x-4>:not(template):not(:last-child){padding-right:1.5rem!important}.divide-y-5>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-5>:not(template):not(:first-child){padding-top:2rem!important}.divide-y-5>:not(template):not(:last-child){padding-bottom:2rem!important}.divide-x-5>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-5>:not(template):not(:first-child){padding-left:2rem!important}.divide-x-5>:not(template):not(:last-child){padding-right:2rem!important}.divide-y-6>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-6>:not(template):not(:first-child){padding-top:2.5rem!important}.divide-y-6>:not(template):not(:last-child){padding-bottom:2.5rem!important}.divide-x-6>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-6>:not(template):not(:first-child){padding-left:2.5rem!important}.divide-x-6>:not(template):not(:last-child){padding-right:2.5rem!important}.divide-y-fill{display:flex;flex-direction:column;height:100%}.divide-y-fill>:not(template){flex:1;display:flex;justify-content:center;flex-direction:column}.icon{--tblr-icon-size: 1.25rem;width:var(--tblr-icon-size);height:var(--tblr-icon-size);font-size:var(--tblr-icon-size);vertical-align:bottom;stroke-width:1.5}.icon:hover{text-decoration:none}.icon-inline{--tblr-icon-size: 1rem;vertical-align:-.2rem}.icon-filled{fill:currentColor}.icon-sm{--tblr-icon-size: 1rem;stroke-width:1}.icon-md{--tblr-icon-size: 2.5rem;stroke-width:1}.icon-lg{--tblr-icon-size: 3.5rem;stroke-width:1}.icon-pulse{transition:all .15s ease 0s;animation:pulse 2s ease infinite;animation-fill-mode:both}.icon-tada{transition:all .15s ease 0s;animation:tada 3s ease infinite;animation-fill-mode:both}.icon-rotate{transition:all .15s ease 0s;animation:rotate-360 3s linear infinite;animation-fill-mode:both}.img-responsive{--tblr-img-responsive-ratio: 75%;background:no-repeat center/cover;padding-top:var(--tblr-img-responsive-ratio)}.img-responsive-grid{padding-top:calc(var(--tblr-img-responsive-ratio) - var(--tblr-gutter-y) / 2)}.img-responsive-1x1{--tblr-img-responsive-ratio: 100%}.img-responsive-2x1{--tblr-img-responsive-ratio: 50%}.img-responsive-1x2{--tblr-img-responsive-ratio: 200%}.img-responsive-3x1{--tblr-img-responsive-ratio: 33.3333333333%}.img-responsive-1x3{--tblr-img-responsive-ratio: 300%}.img-responsive-4x1{--tblr-img-responsive-ratio: 25%}.img-responsive-1x4{--tblr-img-responsive-ratio: 400%}.img-responsive-4x3{--tblr-img-responsive-ratio: 75%}.img-responsive-3x4{--tblr-img-responsive-ratio: 133.3333333333%}.img-responsive-16x9{--tblr-img-responsive-ratio: 56.25%}.img-responsive-9x16{--tblr-img-responsive-ratio: 177.7777777778%}.img-responsive-21x9{--tblr-img-responsive-ratio: 42.8571428571%}.img-responsive-9x21{--tblr-img-responsive-ratio: 233.3333333333%}.img-bg{background:no-repeat center/cover}textarea[cols]{height:auto}.col-form-label,.form-label{display:block;font-weight:var(--tblr-font-weight-medium)}.col-form-label.required:after,.form-label.required:after{content:"*";margin-left:.25rem;color:#d63939}.form-label-description{float:right;font-weight:var(--tblr-font-weight-normal);color:var(--tblr-secondary)}.form-hint{display:block;color:var(--tblr-secondary)}.form-hint:last-child{margin-bottom:0}.form-hint+.form-control{margin-top:.25rem}.form-label+.form-hint{margin-top:-.25rem}.input-group+.form-hint,.form-control+.form-hint,.form-select+.form-hint{margin-top:.5rem;color:var(--tblr-secondary)}.form-select:-moz-focusring{color:var(--tblr-body-color)}.form-control:-webkit-autofill{box-shadow:0 0 0 1000px var(--tblr-bg-surface-secondary) inset;color:var(--tblr-body-color);-webkit-text-fill-color:var(--tblr-body-color)}.form-control:disabled,.form-control.disabled{color:var(--tblr-secondary);user-select:none}.form-control[size]{width:auto}.form-control-light{background-color:var(--tblr-gray-100);border-color:transparent}.form-control-dark{background-color:#0000001a;color:#fff;border-color:transparent}.form-control-dark:focus{background-color:#0000001a;box-shadow:none;border-color:#ffffff3d}.form-control-dark::placeholder{color:#fff9}.form-control-rounded{border-radius:10rem}.form-control-flush{padding:0;background:none!important;border-color:transparent!important;resize:none;box-shadow:none!important;line-height:inherit}.form-footer{margin-top:2rem}.form-fieldset{padding:1rem;margin-bottom:1rem;background:var(--tblr-bg-surface-secondary);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius)}fieldset:empty{display:none}.form-help{display:inline-flex;font-weight:var(--tblr-font-weight-bold);align-items:center;justify-content:center;width:1.125rem;height:1.125rem;font-size:.75rem;color:var(--tblr-secondary);text-align:center;text-decoration:none;cursor:pointer;user-select:none;background:var(--tblr-gray-100);border-radius:100rem;transition:background-color .3s,color .3s}@media(prefers-reduced-motion:reduce){.form-help{transition:none}}.form-help:hover,.form-help[aria-describedby]{color:#fff;background:var(--tblr-primary)}.input-group{box-shadow:var(--tblr-shadow-input);border-radius:var(--tblr-border-radius)}.input-group .form-control,.input-group .btn{box-shadow:none}.input-group-link{font-size:.75rem}.input-group-flat:focus-within{box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25);border-radius:var(--tblr-border-radius)}.input-group-flat:focus-within .form-control,.input-group-flat:focus-within .input-group-text{border-color:#80c2be!important}.input-group-flat .form-control:focus{border-color:var(--tblr-border-color);box-shadow:none}.input-group-flat .form-control:not(:last-child){border-right:0}.input-group-flat .form-control:not(:first-child){border-left:0}.input-group-flat .input-group-text{background:var(--tblr-bg-forms);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.input-group-flat .input-group-text{transition:none}}.input-group-flat .input-group-text:first-child{padding-right:0}.input-group-flat .input-group-text:last-child{padding-left:0}.form-file-button{margin-left:0;border-left:0}label[for=floating-input]{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(max-width:575.98px){.form-control,.form-select{font-size:1rem}}.input-icon{position:relative}.input-icon .form-control:not(:last-child),.input-icon .form-select:not(:last-child){padding-right:2.5rem}.input-icon .form-control:not(:first-child),.input-icon .form-select:not(:last-child){padding-left:2.5rem}.input-icon-addon{position:absolute;top:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;min-width:2.5rem;color:var(--tblr-icon-color);pointer-events:none;font-size:1.2em}.input-icon-addon:last-child{right:0;left:auto}.form-colorinput{position:relative;display:inline-block;margin:0;line-height:1;cursor:pointer}.form-colorinput-input{position:absolute;z-index:-1;opacity:0}.form-colorinput-color{display:block;width:1.5rem;height:1.5rem;color:#fff;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);border-radius:var(--tblr-border-radius);box-shadow:0 1px 2px #0000000d}.form-colorinput-color:before{position:absolute;top:0;left:0;width:100%;height:100%;content:"";background:no-repeat center center/1.25rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/%3e%3c/svg%3e");opacity:0;transition:opacity .3s}@media(prefers-reduced-motion:reduce){.form-colorinput-color:before{transition:none}}.form-colorinput-input:checked~.form-colorinput-color:before{opacity:1}.form-colorinput-input:focus~.form-colorinput-color{border-color:var(--tblr-primary);box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-colorinput-light .form-colorinput-color:before{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3e%3cpath fill='none' stroke='%231f2937' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/%3e%3c/svg%3e")}.form-imagecheck{--tblr-form-imagecheck-radius: var(--tblr-border-radius);position:relative;margin:0;cursor:pointer}.form-imagecheck-input{position:absolute;z-index:-1;opacity:0}.form-imagecheck-figure{position:relative;display:block;margin:0;user-select:none;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-form-imagecheck-radius)}.form-imagecheck-input:focus~.form-imagecheck-figure{border-color:var(--tblr-primary);box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-imagecheck-input:checked~.form-imagecheck-figure{border-color:var(--tblr-primary)}.form-imagecheck-figure:before{position:absolute;top:.25rem;left:.25rem;z-index:1;display:block;width:1.25rem;height:1.25rem;color:#fff;pointer-events:none;content:"";user-select:none;background:var(--tblr-bg-forms);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius);transition:opacity .3s}@media(prefers-reduced-motion:reduce){.form-imagecheck-figure:before{transition:none}}.form-imagecheck-input:checked~.form-imagecheck-figure:before{background-color:var(--tblr-primary);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/%3e%3c/svg%3e");background-repeat:repeat;background-position:center;background-size:1.25rem;border-color:var(--tblr-border-color-translucent)}.form-imagecheck-input[type=radio]~.form-imagecheck-figure:before{border-radius:50%}.form-imagecheck-input[type=radio]:checked~.form-imagecheck-figure:before{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3ccircle r='3' fill='%23ffffff' cx='8' cy='8' /%3e%3c/svg%3e")}.form-imagecheck-image{max-width:100%;display:block;opacity:.64;transition:opacity .3s}@media(prefers-reduced-motion:reduce){.form-imagecheck-image{transition:none}}.form-imagecheck-image:first-child{border-top-left-radius:calc(var(--tblr-form-imagecheck-radius) - 1px);border-top-right-radius:calc(var(--tblr-form-imagecheck-radius) - 1px)}.form-imagecheck-image:last-child{border-bottom-right-radius:calc(var(--tblr-form-imagecheck-radius) - 1px);border-bottom-left-radius:calc(var(--tblr-form-imagecheck-radius) - 1px)}.form-imagecheck:hover .form-imagecheck-image,.form-imagecheck-input:focus~.form-imagecheck-figure .form-imagecheck-image,.form-imagecheck-input:checked~.form-imagecheck-figure .form-imagecheck-image{opacity:1}.form-imagecheck-caption{padding:.25rem;font-size:.765625rem;color:var(--tblr-secondary);text-align:center;transition:color .3s}@media(prefers-reduced-motion:reduce){.form-imagecheck-caption{transition:none}}.form-imagecheck:hover .form-imagecheck-caption,.form-imagecheck-input:focus~.form-imagecheck-figure .form-imagecheck-caption,.form-imagecheck-input:checked~.form-imagecheck-figure .form-imagecheck-caption{color:var(--tblr-body-color)}.form-selectgroup{display:inline-flex;margin:0 -.5rem -.5rem 0;flex-wrap:wrap}.form-selectgroup .form-selectgroup-item{margin:0 .5rem .5rem 0}.form-selectgroup-vertical{flex-direction:column}.form-selectgroup-item{display:block;position:relative}.form-selectgroup-input{position:absolute;top:0;left:0;z-index:-1;opacity:0}.form-selectgroup-label{position:relative;display:block;min-width:calc(1.25rem + 1.125rem + calc(var(--tblr-border-width) * 2));margin:0;padding:.5625rem 1rem;font-size:.875rem;line-height:1.25rem;color:var(--tblr-secondary);background:var(--tblr-bg-forms);text-align:center;cursor:pointer;user-select:none;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius);box-shadow:var(--tblr-shadow-input);transition:border-color .3s,background .3s,color .3s}@media(prefers-reduced-motion:reduce){.form-selectgroup-label{transition:none}}.form-selectgroup-label .icon:only-child{margin:0 -.25rem}.form-selectgroup-label:hover{color:var(--tblr-body-color)}.form-selectgroup-check{display:inline-block;width:1.25rem;height:1.25rem;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);vertical-align:middle;box-shadow:var(--tblr-shadow-input)}.form-selectgroup-input[type=checkbox]+.form-selectgroup-label .form-selectgroup-check{border-radius:var(--tblr-border-radius)}.form-selectgroup-input[type=radio]+.form-selectgroup-label .form-selectgroup-check{border-radius:50%}.form-selectgroup-input:checked+.form-selectgroup-label .form-selectgroup-check{background-color:var(--tblr-primary);background-repeat:repeat;background-position:center;background-size:1.25rem;border-color:var(--tblr-border-color-translucent)}.form-selectgroup-input[type=checkbox]:checked+.form-selectgroup-label .form-selectgroup-check{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/%3e%3c/svg%3e")}.form-selectgroup-input[type=radio]:checked+.form-selectgroup-label .form-selectgroup-check{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3ccircle r='3' fill='%23ffffff' cx='8' cy='8' /%3e%3c/svg%3e")}.form-selectgroup-check-floated{position:absolute;top:.5625rem;right:.5625rem}.form-selectgroup-input:checked+.form-selectgroup-label{z-index:1;color:var(--tblr-primary);background:rgba(var(--tblr-primary-rgb),.04);border-color:var(--tblr-primary)}.form-selectgroup-input:focus+.form-selectgroup-label{z-index:2;color:var(--tblr-primary);border-color:var(--tblr-primary);box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-selectgroup-boxes .form-selectgroup-label{text-align:left;padding:1.25rem 1rem;color:inherit}.form-selectgroup-boxes .form-selectgroup-input:checked+.form-selectgroup-label{color:inherit}.form-selectgroup-boxes .form-selectgroup-input:checked+.form-selectgroup-label .form-selectgroup-title{color:var(--tblr-primary)}.form-selectgroup-boxes .form-selectgroup-input:checked+.form-selectgroup-label .form-selectgroup-label-content{opacity:1}.form-selectgroup-pills{flex-wrap:wrap;align-items:flex-start}.form-selectgroup-pills .form-selectgroup-item{flex-grow:0}.form-selectgroup-pills .form-selectgroup-label{border-radius:50px}.form-control-color::-webkit-color-swatch{border:none}[type=search]::-webkit-search-cancel-button{-webkit-appearance:none}.form-control::file-selector-button{background-color:var(--tblr-btn-color, var(--tblr-tertiary-bg))}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--tblr-btn-color, var(--tblr-secondary-bg))}.form-check{user-select:none}.form-check.form-check-highlight .form-check-input:not(:checked)~.form-check-label{color:var(--tblr-secondary)}.form-check .form-check-label-off{color:var(--tblr-secondary)}.form-check .form-check-input:checked~.form-check-label-off{display:none}.form-check .form-check-input:not(:checked)~.form-check-label-on{display:none}.form-check-input{background-size:1.25rem;margin-top:0rem;box-shadow:var(--tblr-shadow-input)}.form-switch .form-check-input{transition:background-color .3s,background-position .3s}@media(prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-check-label{display:block}.form-check-label.required:after{content:"*";margin-left:.25rem;color:#d63939}.form-check-description{display:block;color:var(--tblr-secondary);font-size:.75rem;margin-top:.25rem}.form-check-single,.form-check-single .form-check-input{margin:0}.form-switch .form-check-input{height:1.25rem;margin-top:0rem}.form-switch-lg{padding-left:3.5rem;min-height:1.5rem}.form-switch-lg .form-check-input{height:1.5rem;width:2.75rem;background-size:1.5rem;margin-left:-3.5rem}.form-switch-lg .form-check-label{padding-top:.125rem}.form-check-input:checked{border:none}.form-select.is-invalid-lite,.form-control.is-invalid-lite,.form-select.is-valid-lite,.form-control.is-valid-lite{border-color:var(--tblr-border-color)!important}.legend{--tblr-legend-size: .75em;display:inline-block;background:var(--tblr-border-color);width:var(--tblr-legend-size);height:var(--tblr-legend-size);border-radius:var(--tblr-border-radius-sm);border:1px solid var(--tblr-border-color-translucent)}.list-group{margin-left:0;margin-right:0}.list-group-header{background:var(--tblr-bg-surface-tertiary);padding:.5rem 1.25rem;font-size:.75rem;font-weight:var(--tblr-font-weight-medium);line-height:1;text-transform:uppercase;color:var(--tblr-gray-500);border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.list-group-flush>.list-group-header:last-child{border-bottom-width:0}.list-group-item{background-color:inherit}.list-group-item.active{background-color:rgba(var(--tblr-secondary-rgb),.08);border-left-color:#00857d;border-left-width:2px}.list-group-item.disabled,.list-group-item:disabled{color:#6b7280;background-color:rgba(var(--tblr-secondary-rgb),.08)}.list-bordered .list-item{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);margin-top:-1px}.list-bordered .list-item:first-child{border-top:none}.list-group-hoverable .list-group-item:active,.list-group-hoverable .list-group-item:focus,.list-group-hoverable .list-group-item:hover{background-color:rgba(var(--tblr-secondary-rgb),.08)}.list-group-hoverable .list-group-item-actions{opacity:0;transition:opacity .3s}@media(prefers-reduced-motion:reduce){.list-group-hoverable .list-group-item-actions{transition:none}}.list-group-hoverable .list-group-item:hover .list-group-item-actions,.list-group-hoverable .list-group-item-actions.show{opacity:1}.list-group-transparent{--tblr-list-group-border-radius: 0;margin:0 -1.25rem}.list-group-transparent .list-group-item{background:none;border:0}.list-group-transparent .list-group-item .icon{color:var(--tblr-secondary)}.list-group-transparent .list-group-item.active{font-weight:var(--tblr-font-weight-bold);color:inherit;background:var(--tblr-active-bg)}.list-group-transparent .list-group-item.active .icon{color:inherit}.list-separated{display:flex;flex-direction:column;gap:1rem}.list-inline{margin:0}.list-inline-item:not(:last-child){margin-right:auto;margin-inline-end:.5rem}.list-inline-dots .list-inline-item+.list-inline-item:before{content:" \b7 ";margin-inline-end:.5rem}.loader{position:relative;display:block;width:2.5rem;height:2.5rem;color:#066fd1;vertical-align:middle}.loader:after{position:absolute;top:0;left:0;width:100%;height:100%;content:"";border:1px var(--tblr-border-style);border-color:transparent;border-top-color:currentColor;border-left-color:currentColor;border-radius:100rem;animation:rotate-360 .6s linear;animation-iteration-count:infinite}.dimmer{position:relative}.dimmer .loader{position:absolute;top:50%;right:0;left:0;display:none;margin:0 auto;transform:translateY(-50%)}.dimmer.active .loader{display:block}.dimmer.active .dimmer-content{pointer-events:none;opacity:.1}@keyframes animated-dots{0%{transform:translate(-100%)}}.animated-dots{display:inline-block;overflow:hidden;vertical-align:bottom}.animated-dots:after{display:inline-block;content:"...";animation:animated-dots 1.2s steps(4,jump-none) infinite}.modal-content>.btn-close,.modal-header>.btn-close{position:absolute;top:0;right:0;width:3.5rem;height:3.5rem;margin:0;padding:0;z-index:10}.modal-body{scrollbar-color:color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent) transparent}.modal-body::-webkit-scrollbar{width:1rem;height:1rem;transition:background .3s}@media(prefers-reduced-motion:reduce){.modal-body::-webkit-scrollbar{transition:none}}.modal-body::-webkit-scrollbar-thumb{border-radius:1rem;border:5px solid transparent;box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent)}.modal-body::-webkit-scrollbar-track{background:transparent}.modal-body:hover::-webkit-scrollbar-thumb{box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 40%,transparent)}.modal-body::-webkit-scrollbar-corner{background:transparent}.modal-body .modal-title{margin-bottom:1rem}.modal-body+.modal-body{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.modal-status{position:absolute;top:0;left:0;right:0;height:2px;background:var(--tblr-secondary);border-radius:var(--tblr-border-radius-lg) var(--tblr-border-radius-lg) 0 0}.modal-header{align-items:center;min-height:3.5rem;background:transparent;padding:0 3.5rem 0 1.5rem}.modal-title{font-size:1rem;font-weight:var(--tblr-font-weight-bold);color:inherit;line-height:1.4285714286}.modal-footer{padding-top:.75rem;padding-bottom:.75rem}.modal-blur{backdrop-filter:blur(4px)}.modal-full-width{max-width:none;margin:0 .5rem}.nav{--tblr-nav-link-hover-bg: color-mix(in srgb, var(--tblr-nav-link-color) 4%, transparent)}.nav-vertical,.nav-vertical .nav{flex-direction:column;flex-wrap:nowrap}.nav-vertical .nav{margin-left:1.25rem;border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);padding-left:.5rem}.nav-vertical .nav-link.active,.nav-vertical .nav-item.show .nav-link{font-weight:var(--tblr-font-weight-bold);color:var(--tblr-nav-link-active-color)}.nav-vertical.nav-pills{margin:0 -.75rem}.nav-bordered{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.nav-bordered .nav-item+.nav-item{margin-left:1.25rem}.nav-bordered .nav-link{padding-left:0;padding-right:0;margin:0 0 calc(-1 * var(--tblr-border-width));border:0;border-bottom:2px var(--tblr-border-style) transparent}.nav-bordered .nav-link:hover{background-color:transparent}.nav-bordered .nav-link.active,.nav-bordered .nav-item.show .nav-link{color:var(--tblr-primary);border-color:var(--tblr-primary)}.nav-underline .nav-link{border-radius:0}.nav-link{display:flex;transition:color .3s,background-color .3s}@media(prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link{align-items:center}.nav-link:hover,.nav-link:focus{background-color:var(--tblr-nav-link-hover-bg)}.nav-link-toggle{margin-left:auto;padding:0 .25rem;transition:transform .3s}@media(prefers-reduced-motion:reduce){.nav-link-toggle{transition:none}}.nav-link-toggle:after{content:"";display:inline-block;vertical-align:.306em;width:.36em;height:.36em;border-bottom:1px var(--tblr-border-style);border-left:1px var(--tblr-border-style);margin-right:.1em;margin-left:.4em;transform:rotate(-45deg)}.nav-link-toggle:after{margin:0}.nav-link[aria-expanded=true] .nav-link-toggle{transform:rotate(180deg)}.nav-link-icon{width:1.25rem;height:1.25rem;margin-right:.5rem;color:inherit}.nav-link-icon svg{display:block;height:100%}.nav-fill .nav-item .nav-link{justify-content:center}.stars{display:inline-flex;color:#9ca3af;font-size:.75rem}.stars .star:not(:first-child){margin-left:.25rem}.pagination{margin:0;--tblr-pagination-gap: .25rem;user-select:none;gap:var(--tblr-pagination-gap);line-height:var(--tblr-body-line-height)}.page-link{min-width:2rem;border-radius:var(--tblr-pagination-border-radius)}.page-item:not(.active) .page-link:hover{background:var(--tblr-pagination-hover-bg)}.page-text{padding-left:.5rem;padding-right:.5rem}.page-item{text-align:center}.page-item.page-prev,.page-item.page-next{flex:0 0 50%;text-align:left}.page-item.page-next{margin-left:auto;text-align:right}.page-item-subtitle{margin-bottom:2px;font-size:12px;color:var(--tblr-secondary);text-transform:uppercase}.page-item.disabled .page-item-subtitle{color:var(--tblr-disabled-color)}.page-item-title{font-size:1rem;font-weight:var(--tblr-font-weight-normal);color:var(--tblr-body-color)}.page-link:hover .page-item-title{color:#00857d}.page-item.disabled .page-item-title{color:var(--tblr-disabled-color)}.pagination-outline{--tblr-pagination-border-color: var(--tblr-border-color);--tblr-pagination-disabled-border-color: var(--tblr-border-color);--tblr-pagination-border-width: 1px}.pagination-circle{--tblr-pagination-border-radius: var(--tblr-border-radius-pill)}@keyframes progress-indeterminate{0%{right:100%;left:-35%}to,60%{right:-90%;left:100%}}.progress{position:relative;width:100%;line-height:.5rem;appearance:none}.progress::-webkit-progress-bar{background:var(--tblr-progress-bg)}.progress::-webkit-progress-value{background-color:var(--tblr-primary)}.progress::-moz-progress-bar{background-color:var(--tblr-primary)}.progress::-ms-fill{background-color:var(--tblr-primary);border:none}.progress-sm{height:.25rem}.progress-bar{height:100%}.progress-bar-indeterminate:after,.progress-bar-indeterminate:before{position:absolute;top:0;bottom:0;left:0;content:"";background-color:inherit;will-change:left,right}.progress-bar-indeterminate:before{animation:progress-indeterminate 1.5s cubic-bezier(.65,.815,.735,.395) infinite}.progress-separated .progress-bar{box-shadow:0 0 0 2px var(--tblr-card-bg, var(--tblr-bg-surface))}.progressbg{position:relative;padding:.25rem .5rem;display:flex}.progressbg-text{position:relative;z-index:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.progressbg-progress{position:absolute;inset:0;z-index:0;height:100%;background:transparent;pointer-events:none}.progressbg-value{font-weight:var(--tblr-font-weight-medium);margin-left:auto;padding-left:2rem}.ribbon{--tblr-ribbon-margin: .25rem;--tblr-ribbon-border-radius: var(--tblr-border-radius);position:absolute;top:.75rem;right:calc(-1 * var(--tblr-ribbon-margin));z-index:1;padding:.25rem .75rem;font-size:.625rem;font-weight:var(--tblr-font-weight-bold);line-height:1;color:#fff;text-align:center;text-transform:uppercase;background:var(--tblr-primary);border-color:var(--tblr-primary);border-radius:var(--tblr-ribbon-border-radius) 0 var(--tblr-ribbon-border-radius) var(--tblr-ribbon-border-radius);display:inline-flex;align-items:center;justify-content:center;min-height:2rem;min-width:2rem}.ribbon:before{position:absolute;right:0;bottom:100%;width:0;height:0;content:"";filter:brightness(70%);border:calc(var(--tblr-ribbon-margin) * .5) var(--tblr-border-style);border-color:inherit;border-top-color:transparent;border-right-color:transparent}.ribbon.bg-blue{border-color:var(--tblr-blue)}.ribbon.bg-blue-lt{border-color:rgba(var(--tblr-blue-rgb),.1)!important}.ribbon.bg-azure{border-color:var(--tblr-azure)}.ribbon.bg-azure-lt{border-color:rgba(var(--tblr-azure-rgb),.1)!important}.ribbon.bg-indigo{border-color:var(--tblr-indigo)}.ribbon.bg-indigo-lt{border-color:rgba(var(--tblr-indigo-rgb),.1)!important}.ribbon.bg-purple{border-color:var(--tblr-purple)}.ribbon.bg-purple-lt{border-color:rgba(var(--tblr-purple-rgb),.1)!important}.ribbon.bg-pink{border-color:var(--tblr-pink)}.ribbon.bg-pink-lt{border-color:rgba(var(--tblr-pink-rgb),.1)!important}.ribbon.bg-red{border-color:var(--tblr-red)}.ribbon.bg-red-lt{border-color:rgba(var(--tblr-red-rgb),.1)!important}.ribbon.bg-orange{border-color:var(--tblr-orange)}.ribbon.bg-orange-lt{border-color:rgba(var(--tblr-orange-rgb),.1)!important}.ribbon.bg-yellow{border-color:var(--tblr-yellow)}.ribbon.bg-yellow-lt{border-color:rgba(var(--tblr-yellow-rgb),.1)!important}.ribbon.bg-lime{border-color:var(--tblr-lime)}.ribbon.bg-lime-lt{border-color:rgba(var(--tblr-lime-rgb),.1)!important}.ribbon.bg-green{border-color:var(--tblr-green)}.ribbon.bg-green-lt{border-color:rgba(var(--tblr-green-rgb),.1)!important}.ribbon.bg-teal{border-color:var(--tblr-teal)}.ribbon.bg-teal-lt{border-color:rgba(var(--tblr-teal-rgb),.1)!important}.ribbon.bg-cyan{border-color:var(--tblr-cyan)}.ribbon.bg-cyan-lt{border-color:rgba(var(--tblr-cyan-rgb),.1)!important}.ribbon .icon{width:1.25rem;height:1.25rem;font-size:1.25rem}.ribbon-top{top:calc(-1 * var(--tblr-ribbon-margin));right:.75rem;width:2rem;padding:.5rem 0;border-radius:0 var(--tblr-ribbon-border-radius) var(--tblr-ribbon-border-radius) var(--tblr-ribbon-border-radius)}.ribbon-top:before{top:0;right:100%;bottom:auto;border-color:inherit;border-top-color:transparent;border-left-color:transparent}.ribbon-top.ribbon-start{right:auto;left:.75rem}.ribbon-top.ribbon-start:before{top:0;right:100%;left:auto}.ribbon-start{right:auto;left:calc(-1 * var(--tblr-ribbon-margin));border-radius:0 var(--tblr-ribbon-border-radius) var(--tblr-ribbon-border-radius) var(--tblr-ribbon-border-radius)}.ribbon-start:before{top:auto;bottom:100%;left:0;border-color:inherit;border-top-color:transparent;border-left-color:transparent}.ribbon-bottom{top:auto;bottom:.75rem}.ribbon-bookmark{padding-left:.25rem;border-radius:0 0 var(--tblr-ribbon-border-radius) 0}.ribbon-bookmark:after{position:absolute;top:0;right:100%;display:block;width:0;height:0;content:"";border:1rem var(--tblr-border-style);border-color:inherit;border-right-width:0;border-left-color:transparent;border-left-width:.5rem}.ribbon-bookmark.ribbon-left{padding-right:.5rem}.ribbon-bookmark.ribbon-left:after{right:auto;left:100%;border-right-color:transparent;border-right-width:.5rem;border-left-width:0}.ribbon-bookmark.ribbon-top{padding-right:0;padding-bottom:.25rem;padding-left:0;border-radius:0 var(--tblr-ribbon-border-radius) 0 0}.ribbon-bookmark.ribbon-top:after{top:100%;right:0;left:0;border-color:inherit;border-width:1rem;border-top-width:0;border-bottom-color:transparent;border-bottom-width:.5rem}.markdown{line-height:2}.markdown>:first-child{margin-top:0}.markdown>:last-child,.markdown>:last-child .highlight{margin-bottom:0}@media(min-width:768px){.markdown>hr,.markdown>.hr{margin-top:3em;margin-bottom:3em}}.markdown>h1,.markdown>.h1,.markdown>h2,.markdown>.h2,.markdown>h3,.markdown>.h3,.markdown>h4,.markdown>.h4,.markdown>h5,.markdown>.h5,.markdown>h6,.markdown>.h6{font-weight:var(--tblr-font-weight-bold)}.markdown>h2,.markdown>.h2,.markdown>h3,.markdown>.h3,.markdown>h4,.markdown>.h4,.markdown>h5,.markdown>.h5,.markdown>h6,.markdown>.h6{margin-top:2.5rem}.markdown>table{font-size:var(--tblr-body-font-size)}.markdown>blockquote{font-size:1rem;margin:1.5rem 0;padding:.5rem 1.5rem}.markdown>img,.markdown>p>img{border-radius:var(--tblr-border-radius);border:1px solid var(--tblr-border-color)}.markdown pre{max-height:20rem}.placeholder:not(.btn):not([class*=bg-]){background-color:currentColor!important}.placeholder:not(.avatar):not([class*=card-img-]){border-radius:var(--tblr-border-radius)}.nav-segmented{--tblr-nav-bg: var(--tblr-bg-surface-tertiary);--tblr-nav-padding: 2px;--tblr-nav-height: 2.5rem;--tblr-nav-gap: .25rem;--tblr-nav-active-bg: var(--tblr-bg-surface);--tblr-nav-font-size: inherit;--tblr-nav-radius: 6px;--tblr-nav-link-disabled-color: var(--tblr-disabled-color);--tblr-nav-link-gap: .25rem;--tblr-nav-link-padding-x: .75rem;--tblr-nav-link-icon-size: 1.25rem;display:inline-flex;flex-wrap:wrap;gap:var(--tblr-nav-gap);padding:var(--tblr-nav-padding);list-style:none;background:var(--tblr-nav-bg);border-radius:calc(var(--tblr-nav-radius) + var(--tblr-nav-padding));box-shadow:inset 0 0 0 1px #0000000a}.nav-segmented .nav-link{display:inline-flex;gap:calc(.25rem + var(--tblr-nav-link-gap));align-items:center;margin:0;font-size:var(--tblr-nav-font-size);min-width:calc(var(--tblr-nav-height) - 2 * var(--tblr-nav-padding));height:calc(var(--tblr-nav-height) - 2 * var(--tblr-nav-padding));padding:0 calc(var(--tblr-nav-link-padding-x) - 2px);border:1px solid transparent;background:transparent;color:var(--tblr-secondary);text-align:center;text-decoration:none;white-space:nowrap;cursor:pointer;transition:background-color .3s,color .3s;border-radius:var(--tblr-nav-radius);flex-grow:1;justify-content:center}.nav-segmented .nav-link:hover,.nav-segmented .nav-link.hover{background:#0000000a;color:var(--tblr-body-color)}.nav-segmented .nav-link.disabled,.nav-segmented .nav-link:disabled{color:var(--tblr-nav-link-disabled-color);cursor:not-allowed}.nav-segmented .nav-link-input:checked+.nav-link,.nav-segmented .nav-link.active{color:var(--tblr-body-color);background:var(--tblr-nav-active-bg);border-color:var(--tblr-border-color)}.nav-segmented .nav-link-input{display:none}.nav-segmented .nav-link-icon{width:var(--tblr-nav-link-icon-size);height:var(--tblr-nav-link-icon-size);margin:0 -.25rem;color:inherit}.nav-segmented-vertical{flex-direction:column}.nav-segmented-vertical .nav-link{justify-content:flex-start}.nav-sm{--tblr-nav-height: 2rem;--tblr-nav-font-size: var(--tblr-font-size-h5);--tblr-nav-radius: 4px;--tblr-nav-link-padding-x: .5rem;--tblr-nav-link-gap: .25rem;--tblr-nav-link-icon-size: 1rem}.nav-lg{--tblr-nav-height: 3rem;--tblr-nav-font-size: var(--tblr-font-size-h3);--tblr-nav-radius: 8px;--tblr-nav-link-padding-x: 1rem;--tblr-nav-link-gap: .5rem;--tblr-nav-link-icon-size: 1.5rem}.steps{--tblr-steps-color: var(--tblr-primary);--tblr-steps-inactive-color: var(--tblr-border-color);--tblr-steps-dot-size: .5rem;--tblr-steps-border-width: 2px;display:flex;flex-wrap:nowrap;width:100%;padding:0;margin:0;list-style:none}.steps-blue{--tblr-steps-color: var(--tblr-blue)}.steps-blue-lt{--tblr-steps-color: var(--tblr-blue-lt)}.steps-azure{--tblr-steps-color: var(--tblr-azure)}.steps-azure-lt{--tblr-steps-color: var(--tblr-azure-lt)}.steps-indigo{--tblr-steps-color: var(--tblr-indigo)}.steps-indigo-lt{--tblr-steps-color: var(--tblr-indigo-lt)}.steps-purple{--tblr-steps-color: var(--tblr-purple)}.steps-purple-lt{--tblr-steps-color: var(--tblr-purple-lt)}.steps-pink{--tblr-steps-color: var(--tblr-pink)}.steps-pink-lt{--tblr-steps-color: var(--tblr-pink-lt)}.steps-red{--tblr-steps-color: var(--tblr-red)}.steps-red-lt{--tblr-steps-color: var(--tblr-red-lt)}.steps-orange{--tblr-steps-color: var(--tblr-orange)}.steps-orange-lt{--tblr-steps-color: var(--tblr-orange-lt)}.steps-yellow{--tblr-steps-color: var(--tblr-yellow)}.steps-yellow-lt{--tblr-steps-color: var(--tblr-yellow-lt)}.steps-lime{--tblr-steps-color: var(--tblr-lime)}.steps-lime-lt{--tblr-steps-color: var(--tblr-lime-lt)}.steps-green{--tblr-steps-color: var(--tblr-green)}.steps-green-lt{--tblr-steps-color: var(--tblr-green-lt)}.steps-teal{--tblr-steps-color: var(--tblr-teal)}.steps-teal-lt{--tblr-steps-color: var(--tblr-teal-lt)}.steps-cyan{--tblr-steps-color: var(--tblr-cyan)}.steps-cyan-lt{--tblr-steps-color: var(--tblr-cyan-lt)}.step-item{position:relative;flex:1 1 0;min-height:1rem;margin-top:0;color:inherit;text-align:center;cursor:default;padding-top:calc(var(--tblr-steps-dot-size))}a.step-item{cursor:pointer}a.step-item:hover{color:inherit}.step-item:after,.step-item:before{background:var(--tblr-steps-color)}.step-item:not(:last-child):after{position:absolute;left:50%;width:100%;content:"";transform:translateY(-50%)}.step-item:after{top:calc(var(--tblr-steps-dot-size) * .5);height:var(--tblr-steps-border-width)}.step-item:before{content:"";position:absolute;top:0;left:50%;z-index:1;box-sizing:content-box;display:flex;align-items:center;justify-content:center;border-radius:100rem;transform:translate(-50%);color:var(--tblr-white);width:var(--tblr-steps-dot-size);height:var(--tblr-steps-dot-size)}.step-item.active{font-weight:var(--tblr-font-weight-bold)}.step-item.active:after{background:var(--tblr-steps-inactive-color)}.step-item.active~.step-item{color:var(--tblr-disabled-color)}.step-item.active~.step-item:after,.step-item.active~.step-item:before{background:var(--tblr-steps-inactive-color)}.steps-counter{--tblr-steps-dot-size: 1.5rem;counter-reset:steps}.steps-counter .step-item{counter-increment:steps}.steps-counter .step-item:before{content:counter(steps)}.steps-vertical{--tblr-steps-dot-offset: 6px;flex-direction:column}.steps-vertical.steps-counter{--tblr-steps-dot-offset: -2px}.steps-vertical .step-item{text-align:left;padding-top:0;padding-left:calc(var(--tblr-steps-dot-size) + 1rem);min-height:auto}.steps-vertical .step-item:not(:first-child){margin-top:1rem}.steps-vertical .step-item:before{top:var(--tblr-steps-dot-offset);left:0;transform:translate(0)}.steps-vertical .step-item:not(:last-child):after{position:absolute;content:"";transform:translate(-50%);top:var(--tblr-steps-dot-offset);left:calc(var(--tblr-steps-dot-size) * .5);width:var(--tblr-steps-border-width);height:calc(100% + 1rem)}@keyframes status-pulsate-main{40%{transform:scale(1.25)}60%{transform:scale(1.25)}}@keyframes status-pulsate-secondary{10%{transform:scale(1)}30%{transform:scale(3)}80%{transform:scale(3)}to{transform:scale(1)}}@keyframes status-pulsate-tertiary{25%{transform:scale(1)}80%{transform:scale(3);opacity:0}to{transform:scale(3);opacity:0}}.status{--tblr-status-height: 1.5rem;--tblr-status-color: #6b7280;--tblr-status-color-rgb: 107, 114, 128;display:inline-flex;align-items:center;height:var(--tblr-status-height);padding:.25rem .75rem;gap:.5rem;color:var(--tblr-status-color);background:rgba(var(--tblr-status-color-rgb),.1);font-size:.875rem;text-transform:none;letter-spacing:normal;border-radius:100rem;font-weight:var(--tblr-font-weight-medium);line-height:1;margin:0}.status .status-dot{background:var(--tblr-status-color)}.status .icon{font-size:1.25rem}.status-lite{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)!important;background:transparent!important;color:var(--tblr-body-color)!important}.status-primary{--tblr-status-color: #00857D;--tblr-status-color-rgb: 0, 133, 125}.status-secondary{--tblr-status-color: #6b7280;--tblr-status-color-rgb: 107, 114, 128}.status-success{--tblr-status-color: #2fb344;--tblr-status-color-rgb: 47, 179, 68}.status-info{--tblr-status-color: #4299e1;--tblr-status-color-rgb: 66, 153, 225}.status-warning{--tblr-status-color: #f59f00;--tblr-status-color-rgb: 245, 159, 0}.status-danger{--tblr-status-color: #d63939;--tblr-status-color-rgb: 214, 57, 57}.status-light{--tblr-status-color: #f9fafb;--tblr-status-color-rgb: 249, 250, 251}.status-dark{--tblr-status-color: #1f2937;--tblr-status-color-rgb: 31, 41, 55}.status-muted{--tblr-status-color: #6b7280;--tblr-status-color-rgb: 107, 114, 128}.status-blue{--tblr-status-color: #066fd1;--tblr-status-color-rgb: 6, 111, 209}.status-azure{--tblr-status-color: #4299e1;--tblr-status-color-rgb: 66, 153, 225}.status-indigo{--tblr-status-color: #4263eb;--tblr-status-color-rgb: 66, 99, 235}.status-purple{--tblr-status-color: #ae3ec9;--tblr-status-color-rgb: 174, 62, 201}.status-pink{--tblr-status-color: #d6336c;--tblr-status-color-rgb: 214, 51, 108}.status-red{--tblr-status-color: #d63939;--tblr-status-color-rgb: 214, 57, 57}.status-orange{--tblr-status-color: #f76707;--tblr-status-color-rgb: 247, 103, 7}.status-yellow{--tblr-status-color: #f59f00;--tblr-status-color-rgb: 245, 159, 0}.status-lime{--tblr-status-color: #74b816;--tblr-status-color-rgb: 116, 184, 22}.status-green{--tblr-status-color: #2fb344;--tblr-status-color-rgb: 47, 179, 68}.status-teal{--tblr-status-color: #0ca678;--tblr-status-color-rgb: 12, 166, 120}.status-cyan{--tblr-status-color: #17a2b8;--tblr-status-color-rgb: 23, 162, 184}.status-dot{--tblr-status-dot-color: var(--tblr-status-color, #6b7280);--tblr-status-size: .5rem;position:relative;display:inline-block;width:var(--tblr-status-size);height:var(--tblr-status-size);background:var(--tblr-status-dot-color);border-radius:100rem}.status-dot-animated:before{content:"";position:absolute;inset:0;z-index:0;background:inherit;border-radius:inherit;opacity:.6;animation:1s linear 2s backwards infinite status-pulsate-tertiary}.status-indicator{--tblr-status-indicator-size: 2.5rem;--tblr-status-indicator-color: var(--tblr-status-color, #6b7280);display:block;position:relative;width:var(--tblr-status-indicator-size);height:var(--tblr-status-indicator-size)}.status-indicator-circle{--tblr-status-circle-size: .75rem;position:absolute;left:50%;top:50%;margin:calc(var(--tblr-status-circle-size) / -2) 0 0 calc(var(--tblr-status-circle-size) / -2);width:var(--tblr-status-circle-size);height:var(--tblr-status-circle-size);border-radius:100rem;background:var(--tblr-status-color)}.status-indicator-circle:nth-child(1){z-index:3}.status-indicator-circle:nth-child(2){z-index:2;opacity:.1}.status-indicator-circle:nth-child(3){z-index:1;opacity:.3}.status-indicator-animated .status-indicator-circle:nth-child(1){animation:2s linear 1s infinite backwards status-pulsate-main}.status-indicator-animated .status-indicator-circle:nth-child(2){animation:2s linear 1s infinite backwards status-pulsate-secondary}.status-indicator-animated .status-indicator-circle:nth-child(3){animation:2s linear 1s infinite backwards status-pulsate-tertiary}.switch-icon{display:inline-block;line-height:1;border:0;padding:0;background:transparent;width:1.25rem;height:1.25rem;vertical-align:bottom;position:relative;cursor:pointer}.switch-icon.disabled{pointer-events:none;opacity:.4}.switch-icon:focus{outline:none}.switch-icon svg{display:block;width:100%;height:100%}.switch-icon .switch-icon-a,.switch-icon .switch-icon-b{display:block;width:100%;height:100%}.switch-icon .switch-icon-a{opacity:1}.switch-icon .switch-icon-b{position:absolute;top:0;left:0;opacity:0}.switch-icon.active .switch-icon-a{opacity:0}.switch-icon.active .switch-icon-b{opacity:1}.switch-icon-fade .switch-icon-a,.switch-icon-fade .switch-icon-b{transition:opacity .5s}@media(prefers-reduced-motion:reduce){.switch-icon-fade .switch-icon-a,.switch-icon-fade .switch-icon-b{transition:none}}.switch-icon-scale .switch-icon-a,.switch-icon-scale .switch-icon-b{transition:opacity .5s,transform 0s .5s}@media(prefers-reduced-motion:reduce){.switch-icon-scale .switch-icon-a,.switch-icon-scale .switch-icon-b{transition:none}}.switch-icon-scale .switch-icon-b{transform:scale(1.5)}.switch-icon-scale.active .switch-icon-a,.switch-icon-scale.active .switch-icon-b{transition:opacity 0s,transform .5s}@media(prefers-reduced-motion:reduce){.switch-icon-scale.active .switch-icon-a,.switch-icon-scale.active .switch-icon-b{transition:none}}.switch-icon-scale.active .switch-icon-b{transform:scale(1)}.switch-icon-flip{perspective:10em}.switch-icon-flip .switch-icon-a,.switch-icon-flip .switch-icon-b{backface-visibility:hidden;transform-style:preserve-3d;transition:opacity 0s .2s,transform .4s ease-in-out}@media(prefers-reduced-motion:reduce){.switch-icon-flip .switch-icon-a,.switch-icon-flip .switch-icon-b{transition:none}}.switch-icon-flip .switch-icon-a{opacity:1;transform:rotateY(0)}.switch-icon-flip .switch-icon-b{opacity:1;transform:rotateY(-180deg)}.switch-icon-flip.active .switch-icon-a{opacity:1;transform:rotateY(180deg)}.switch-icon-flip.active .switch-icon-b{opacity:1;transform:rotateY(0)}.switch-icon-slide-up,.switch-icon-slide-left,.switch-icon-slide-start,.switch-icon-slide-right,.switch-icon-slide-end,.switch-icon-slide-down{overflow:hidden}.switch-icon-slide-up .switch-icon-a,.switch-icon-slide-up .switch-icon-b,.switch-icon-slide-left .switch-icon-a,.switch-icon-slide-left .switch-icon-b,.switch-icon-slide-start .switch-icon-a,.switch-icon-slide-start .switch-icon-b,.switch-icon-slide-right .switch-icon-a,.switch-icon-slide-right .switch-icon-b,.switch-icon-slide-end .switch-icon-a,.switch-icon-slide-end .switch-icon-b,.switch-icon-slide-down .switch-icon-a,.switch-icon-slide-down .switch-icon-b{transition:opacity .3s,transform .3s}@media(prefers-reduced-motion:reduce){.switch-icon-slide-up .switch-icon-a,.switch-icon-slide-up .switch-icon-b,.switch-icon-slide-left .switch-icon-a,.switch-icon-slide-left .switch-icon-b,.switch-icon-slide-start .switch-icon-a,.switch-icon-slide-start .switch-icon-b,.switch-icon-slide-right .switch-icon-a,.switch-icon-slide-right .switch-icon-b,.switch-icon-slide-end .switch-icon-a,.switch-icon-slide-end .switch-icon-b,.switch-icon-slide-down .switch-icon-a,.switch-icon-slide-down .switch-icon-b{transition:none}}.switch-icon-slide-up .switch-icon-a,.switch-icon-slide-left .switch-icon-a,.switch-icon-slide-start .switch-icon-a,.switch-icon-slide-right .switch-icon-a,.switch-icon-slide-end .switch-icon-a,.switch-icon-slide-down .switch-icon-a{transform:translateY(0)}.switch-icon-slide-up .switch-icon-b,.switch-icon-slide-left .switch-icon-b,.switch-icon-slide-start .switch-icon-b,.switch-icon-slide-right .switch-icon-b,.switch-icon-slide-end .switch-icon-b,.switch-icon-slide-down .switch-icon-b{transform:translateY(100%)}.switch-icon-slide-up.active .switch-icon-a,.switch-icon-slide-left.active .switch-icon-a,.switch-icon-slide-start.active .switch-icon-a,.switch-icon-slide-right.active .switch-icon-a,.switch-icon-slide-end.active .switch-icon-a,.switch-icon-slide-down.active .switch-icon-a{transform:translateY(-100%)}.switch-icon-slide-up.active .switch-icon-b,.switch-icon-slide-left.active .switch-icon-b,.switch-icon-slide-start.active .switch-icon-b,.switch-icon-slide-right.active .switch-icon-b,.switch-icon-slide-end.active .switch-icon-b,.switch-icon-slide-down.active .switch-icon-b{transform:translateY(0)}.switch-icon-slide-left .switch-icon-a,.switch-icon-slide-start .switch-icon-a{transform:translate(0)}.switch-icon-slide-left .switch-icon-b,.switch-icon-slide-start .switch-icon-b{transform:translate(100%)}.switch-icon-slide-left.active .switch-icon-a,.switch-icon-slide-start.active .switch-icon-a{transform:translate(-100%)}.switch-icon-slide-left.active .switch-icon-b,.switch-icon-slide-start.active .switch-icon-b,.switch-icon-slide-right .switch-icon-a,.switch-icon-slide-end .switch-icon-a{transform:translate(0)}.switch-icon-slide-right .switch-icon-b,.switch-icon-slide-end .switch-icon-b{transform:translate(-100%)}.switch-icon-slide-right.active .switch-icon-a,.switch-icon-slide-end.active .switch-icon-a{transform:translate(100%)}.switch-icon-slide-right.active .switch-icon-b,.switch-icon-slide-end.active .switch-icon-b{transform:translate(0)}.switch-icon-slide-down .switch-icon-a{transform:translateY(0)}.switch-icon-slide-down .switch-icon-b{transform:translateY(-100%)}.switch-icon-slide-down.active .switch-icon-a{transform:translateY(100%)}.switch-icon-slide-down.active .switch-icon-b{transform:translateY(0)}.table thead th,.markdown>table thead th{background:var(--tblr-bg-surface-tertiary);font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);padding-top:.5rem;padding-bottom:.5rem;white-space:nowrap}@media print{.table thead th,.markdown>table thead th{background:transparent}}.table-responsive .table,.table-responsive .markdown>table{margin-bottom:0}.table-responsive+.card-footer{border-top:0}.table-transparent thead th{background:transparent}.table-nowrap>:not(caption)>*>*{white-space:nowrap}.table-vcenter>:not(caption)>*>*{vertical-align:middle}.table-center>:not(caption)>*>*{text-align:center}.td-truncate{max-width:1px;width:100%}.table-mobile{display:block}.table-mobile thead{display:none}.table-mobile tbody,.table-mobile tr{display:flex;flex-direction:column}.table-mobile td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile .btn{display:block}@media(max-width:575.98px){.table-mobile-sm{display:block}.table-mobile-sm thead{display:none}.table-mobile-sm tbody,.table-mobile-sm tr{display:flex;flex-direction:column}.table-mobile-sm td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile-sm td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile-sm tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile-sm .btn{display:block}}@media(max-width:767.98px){.table-mobile-md{display:block}.table-mobile-md thead{display:none}.table-mobile-md tbody,.table-mobile-md tr{display:flex;flex-direction:column}.table-mobile-md td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile-md td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile-md tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile-md .btn{display:block}}@media(max-width:991.98px){.table-mobile-lg{display:block}.table-mobile-lg thead{display:none}.table-mobile-lg tbody,.table-mobile-lg tr{display:flex;flex-direction:column}.table-mobile-lg td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile-lg td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile-lg tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile-lg .btn{display:block}}@media(max-width:1199.98px){.table-mobile-xl{display:block}.table-mobile-xl thead{display:none}.table-mobile-xl tbody,.table-mobile-xl tr{display:flex;flex-direction:column}.table-mobile-xl td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile-xl td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile-xl tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile-xl .btn{display:block}}@media(max-width:1399.98px){.table-mobile-xxl{display:block}.table-mobile-xxl thead{display:none}.table-mobile-xxl tbody,.table-mobile-xxl tr{display:flex;flex-direction:column}.table-mobile-xxl td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile-xxl td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile-xxl tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile-xxl .btn{display:block}}.table-sort{font:inherit;color:inherit;text-transform:inherit;letter-spacing:inherit;border:0;background:inherit;display:block;width:100%;text-align:inherit;transition:color .3s}@media(prefers-reduced-motion:reduce){.table-sort{transition:none}}.table-sort{margin:-.5rem;padding:.5rem}.table-sort:hover,.table-sort.asc,.table-sort.desc{color:var(--tblr-body-color)}.table-sort:after{content:"";display:inline-flex;width:1rem;height:1rem;vertical-align:bottom;mask-image:url("data:image/svg+xml,");background:currentColor;margin-left:.25rem}.table-sort.asc:after{mask-image:url("data:image/svg+xml,")}.table-sort.desc:after{mask-image:url("data:image/svg+xml,")}.table-borderless thead th{background:transparent}.table-selectable tbody tr .on-checked{display:none}.table-selectable tbody tr .on-unchecked{display:initial}.table-selectable tbody tr:has(.table-selectable-check:checked){background-color:var(--tblr-active-bg)}.table-selectable tbody tr:has(.table-selectable-check:checked) .on-checked{display:initial}.table-selectable tbody tr:has(.table-selectable-check:checked) .on-unchecked{display:none}.tag{--tblr-tag-height: 1.5rem;border:1px solid var(--tblr-border-color);display:inline-flex;align-items:center;height:var(--tblr-tag-height);border-radius:var(--tblr-border-radius);padding:0 .5rem;background:var(--tblr-bg-surface);box-shadow:var(--tblr-shadow-input);gap:.5rem}.tag .btn-close{margin-right:-.25rem;margin-left:-.125rem;padding:0;width:1rem;height:1rem;font-size:.5rem}.tag-badge{--tblr-badge-font-size: .625rem;--tblr-badge-padding-x: .25rem;--tblr-badge-padding-y: .125rem;margin-right:-.25rem}.tag-avatar,.tag-flag,.tag-payment,.tag-icon,.tag-check{margin-left:-.25rem}.tag-icon{color:var(--tblr-secondary);margin-right:-.125rem;width:1rem;height:1rem}.tag-check{width:1rem;height:1rem;background-size:1rem}.tags-list{--tblr-list-gap: .5rem;display:flex;flex-wrap:wrap;gap:var(--tblr-list-gap)}.toast{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);box-shadow:#1f29370a 0 2px 4px}.toast .toast-header{user-select:none}.toast button[data-bs-dismiss=toast]{outline:none}.toast-primary{--tblr-toast-color: #00857D}.toast-secondary{--tblr-toast-color: #6b7280}.toast-success{--tblr-toast-color: #2fb344}.toast-info{--tblr-toast-color: #4299e1}.toast-warning{--tblr-toast-color: #f59f00}.toast-danger{--tblr-toast-color: #d63939}.toast-light{--tblr-toast-color: #f9fafb}.toast-dark{--tblr-toast-color: #1f2937}.toast-muted{--tblr-toast-color: #6b7280}.toast-blue{--tblr-toast-color: #066fd1}.toast-azure{--tblr-toast-color: #4299e1}.toast-indigo{--tblr-toast-color: #4263eb}.toast-purple{--tblr-toast-color: #ae3ec9}.toast-pink{--tblr-toast-color: #d6336c}.toast-red{--tblr-toast-color: #d63939}.toast-orange{--tblr-toast-color: #f76707}.toast-yellow{--tblr-toast-color: #f59f00}.toast-lime{--tblr-toast-color: #74b816}.toast-green{--tblr-toast-color: #2fb344}.toast-teal{--tblr-toast-color: #0ca678}.toast-cyan{--tblr-toast-color: #17a2b8}.toolbar{display:flex;flex-wrap:nowrap;flex-shrink:0;margin:0 -.5rem}.toolbar>*{margin:0 .5rem}.tracking{--tblr-tracking-height: 1.5rem;--tblr-tracking-gap-width: .125rem;--tblr-tracking-block-border-radius: var(--tblr-border-radius);display:flex;gap:var(--tblr-tracking-gap-width)}.tracking-squares{--tblr-tracking-block-border-radius: var(--tblr-border-radius-sm)}.tracking-squares .tracking-block{height:auto}.tracking-squares .tracking-block:before{content:"";display:block;padding-top:100%}.tracking-block{flex:1;border-radius:var(--tblr-tracking-block-border-radius);height:var(--tblr-tracking-height);min-width:.25rem;background:var(--tblr-border-color)}.timeline{--tblr-timeline-icon-size: 2.5rem;position:relative;list-style:none;padding:0}.timeline-event{position:relative}.timeline-event:not(:last-child){margin-bottom:var(--tblr-page-padding)}.timeline-event:not(:last-child):before{content:"";position:absolute;top:var(--tblr-timeline-icon-size);left:calc(var(--tblr-timeline-icon-size) / 2);bottom:calc(-1 * var(--tblr-page-padding));width:var(--tblr-border-width);background-color:var(--tblr-border-color);border-radius:var(--tblr-border-radius)}.timeline-event-icon{position:absolute;display:flex;align-items:center;justify-content:center;width:var(--tblr-timeline-icon-size, 2.5rem);height:var(--tblr-timeline-icon-size, 2.5rem);background:var(--tblr-bg-surface-secondary);color:var(--tblr-secondary);border-radius:var(--tblr-border-radius);z-index:5}.timeline-event-card{margin-left:calc(var(--tblr-timeline-icon-size, 2.5rem) + var(--tblr-page-padding))}.timeline-simple .timeline-event-icon{display:none}.timeline-simple .timeline-event-card{margin-left:0}.hr-text{display:flex;align-items:center;margin:2rem 0;font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);height:1px}.hr-text:after,.hr-text:before{flex:1 1 auto;height:1px;background-color:var(--tblr-border-color)}.hr-text:before{content:"";margin-right:.5rem}.hr-text:after{content:"";margin-left:.5rem}.hr-text>*:first-child{padding-right:.5rem;padding-left:0;color:var(--tblr-secondary)}.hr-text.hr-text-left:before,.hr-text.hr-text-start:before{content:none}.hr-text.hr-text-left>*:first-child,.hr-text.hr-text-start>*:first-child{padding-right:.5rem;padding-left:.5rem}.hr-text.hr-text-right:before,.hr-text.hr-text-end:before{content:""}.hr-text.hr-text-right:after,.hr-text.hr-text-end:after{content:none}.hr-text.hr-text-right>*:first-child,.hr-text.hr-text-end>*:first-child{padding-right:0;padding-left:.5rem}.card>.hr-text{margin:0}.hr-text-spaceless{margin:-.5rem 0}.lead{color:var(--tblr-secondary);font-size:inherit}a{text-decoration-skip-ink:auto;color:color-mix(in srgb,transparent,var(--tblr-link-color) var(--tblr-link-opacity, 100%))}a:hover{color:color-mix(in srgb,transparent,var(--tblr-link-hover-color) var(--tblr-link-opacity, 100%))}h1 a,h2 a,h3 a,.field-group h2 a,h4 a,h5 a,h6 a,.h1 a,.h2 a,.h3 a,.h4 a,.h5 a,.h6 a,h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover,.h1 a:hover,.h2 a:hover,.h3 a:hover,.h4 a:hover,.h5 a:hover,.h6 a:hover{color:inherit}h1,.h1{font-size:var(--tblr-font-size-h1);line-height:var(--tblr-line-height-h1)}h2,.h2{font-size:var(--tblr-font-size-h2);line-height:var(--tblr-line-height-h2)}h3,.field-group h2,.field-group .h2,.h3{font-size:var(--tblr-font-size-h3);line-height:var(--tblr-line-height-h3)}h4,.h4{font-size:var(--tblr-font-size-h4);line-height:var(--tblr-line-height-h4)}h5,.h5{font-size:var(--tblr-font-size-h5);line-height:var(--tblr-line-height-h5)}h6,.h6{font-size:var(--tblr-font-size-h6);line-height:var(--tblr-line-height-h6)}.fs-base{font-size:var(--tblr-body-font-size)}strong,.strong,b{font-weight:var(--tblr-font-weight-bold)}blockquote{padding:1rem;border-left:2px var(--tblr-border-style) var(--tblr-border-color)}blockquote p{margin-bottom:1rem}blockquote cite{display:block;text-align:right}blockquote cite:before{content:"\2014 "}ul,ol{padding-left:1.5rem}hr,.hr{margin:2rem 0}dl dd:last-child{margin-bottom:0}pre{--tblr-scrollbar-color: var(--tblr-light);padding:1rem;background:var(--tblr-bg-surface-dark);color:var(--tblr-light);border-radius:var(--tblr-border-radius);line-height:1.4285714286}pre{scrollbar-color:color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent) transparent}pre::-webkit-scrollbar{width:1rem;height:1rem;transition:background .3s}@media(prefers-reduced-motion:reduce){pre::-webkit-scrollbar{transition:none}}pre::-webkit-scrollbar-thumb{border-radius:1rem;border:5px solid transparent;box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent)}pre::-webkit-scrollbar-track{background:transparent}pre:hover::-webkit-scrollbar-thumb{box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 40%,transparent)}pre::-webkit-scrollbar-corner{background:transparent}pre code{background:transparent;padding:0}code{background:var(--tblr-code-bg);padding:2px 4px;border-radius:var(--tblr-border-radius)}abbr{text-decoration:underline dotted;cursor:help;text-decoration-skip-ink:none}kbd,.kbd{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);display:inline-block;box-sizing:border-box;max-width:100%;font-size:var(--tblr-font-size-h5);font-weight:var(--tblr-font-weight-medium);line-height:1;vertical-align:baseline;border-radius:var(--tblr-border-radius)}img{max-width:100%;height:auto}.list-unstyled{margin-left:0}::selection,.text-selected{background-color:color-mix(in srgb,var(--tblr-primary) 10%,transparent)}.text-selected{display:inline-block}[class^=link-].disabled,[class*=" link-"].disabled{color:var(--tblr-disabled-color)!important;pointer-events:none}a:hover:has(.icon){text-decoration:none}.link-hoverable{border-radius:var(--tblr-border-radius);transition:background-color .15s ease-in-out}.link-hoverable:hover{text-decoration:none;color:var(--tblr-primary);background:color-mix(in srgb,var(--tblr-secondary) 4%,transparent)}.subheader{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary)}.mention{display:inline-block;box-shadow:var(--tblr-shadow-border);border-radius:var(--tblr-border-radius-pill);line-height:1.3333333333em;font-size:.8571428571em;color:var(--tblr-body-color);background:var(--tblr-bg-surface-tertiary);padding:.1666666667em .6666666667em;font-weight:var(--tblr-font-weight-medium)}a.mention{cursor:pointer}a.mention:hover,a.mention.hover{background:var(--tblr-bg-surface-secondary);text-decoration:underline}.mention-avatar,.mention-app,.mention-color{width:1.1666666667em;height:1.1666666667em;border-radius:var(--tblr-border-radius-pill);margin:-.1666666667em .3333333333em 0 -.3333333333em;display:inline-flex;background:no-repeat center center/cover;box-shadow:var(--tblr-shadow-border);vertical-align:middle;text-align:center}.mention-app{box-shadow:none;background:none;border-radius:0}.mention-count{color:var(--tblr-secondary);margin-left:.6666666667em}.text-incorrect{background:color-mix(in srgb,var(--tblr-red) 4%,transparent);text-decoration:underline;text-decoration-thickness:1px;text-decoration-color:var(--tblr-red)}.text-correct{background:color-mix(in srgb,var(--tblr-green) 4%,transparent);text-decoration:underline;text-decoration-thickness:1px;text-decoration-color:var(--tblr-green)}.steps{--tblr-steps-padding: 2rem;--tblr-steps-item-size: 1.5rem;margin-left:1rem;padding-left:var(--tblr-steps-padding);counter-reset:step;border-left:1px solid var(--tblr-border-color);margin-bottom:2rem}.steps h3,.steps .field-group h2,.field-group .steps h2,.steps .field-group .h2,.field-group .steps .h2,.steps .h3{counter-increment:step}.steps h3:not(:first-child),.steps .field-group h2:not(:first-child),.field-group .steps h2:not(:first-child),.steps .field-group .h2:not(:first-child),.field-group .steps .h2:not(:first-child),.steps .h3:not(:first-child){margin-top:2.5rem!important}.steps h3:before,.steps .field-group h2:before,.field-group .steps h2:before,.steps .field-group .h2:before,.field-group .steps .h2:before,.steps .h3:before{content:counter(step);display:inline-block;position:absolute;margin-top:1px;margin-left:calc(-1 * var(--tblr-steps-padding) - var(--tblr-steps-item-size) / 2);width:var(--tblr-steps-item-size);height:var(--tblr-steps-item-size);text-align:center;color:var(--tblr-body-color);border:1px solid var(--tblr-border-color);background:var(--tblr-bg-surface);border-radius:var(--tblr-border-radius);line-height:calc(var(--tblr-steps-item-size) - 2px);font-size:var(--tblr-font-size-h4);font-weight:var(--tblr-font-weight-bold)}.steps>:last-child{margin-bottom:0}.callout{margin-bottom:1.5rem;border:1px solid var(--tblr-primary-200);border-radius:var(--tblr-border-radius);padding:.5rem 1rem;background:var(--tblr-primary-lt)}.callout>:last-child{margin-bottom:0}.chart{display:block;min-height:10rem}.chart text{font-family:inherit}.chart-sm{height:2.5rem}.chart-lg{height:15rem}.chart-square{height:5.75rem}.chart-sparkline{position:relative;width:4rem;height:2.5rem;line-height:1;min-height:0!important}.chart-sparkline-sm{height:1.5rem}.chart-sparkline-square{width:2.5rem}.chart-sparkline-wide{width:6rem}.chart-sparkline-label{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:.625rem}.chart-sparkline-label .icon{width:1rem;height:1rem;font-size:1rem}.offcanvas-header{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.offcanvas-footer{padding:1.5rem}.offcanvas-title{font-size:1rem;font-weight:var(--tblr-font-weight-medium);line-height:1.5rem}.offcanvas-narrow{width:20rem}.chat-bubbles{display:flex;flex-direction:column;gap:1rem}.chat-bubble{background:var(--tblr-bg-surface-secondary);border-radius:var(--tblr-border-radius-lg);padding:1rem;position:relative}.chat-bubble-me{background-color:var(--tblr-primary-lt);box-shadow:none}.chat-bubble-title{margin-bottom:.25rem}.chat-bubble-author{font-weight:600}.chat-bubble-date{color:var(--tblr-secondary)}.chat-bubble-body>*:last-child{margin-bottom:0}.signature{--tblr-signature-padding: var(--tblr-spacer-1);--tblr-signature-border-radius: var(--tblr-border-radius);border:var(--tblr-border-width) solid var(--tblr-border-color);padding:var(--tblr-signature-padding);border-radius:var(--tblr-border-radius)}.signature-canvas{border:var(--tblr-border-width) dashed var(--tblr-border-color);border-radius:calc(var(--tblr-signature-border-radius) - var(--tblr-signature-padding));display:block;cursor:crosshair;width:100%}.clearfix:after{display:block;clear:both;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--tblr-border-width);min-height:1em;background-color:currentcolor;opacity:.16}.stretched-link:after{position:absolute;inset:0;z-index:1;content:""}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden:not(caption),.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption){position:absolute!important}.visually-hidden *,.visually-hidden-focusable:not(:focus):not(:focus-within) *{overflow:hidden!important}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:sticky;top:0;z-index:1020}.sticky-bottom{position:sticky;bottom:0;z-index:1020}@media(min-width:576px){.sticky-sm-top{position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:768px){.sticky-md-top{position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:992px){.sticky-lg-top{position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:1200px){.sticky-xl-top{position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:1400px){.sticky-xxl-top{position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:sticky;bottom:0;z-index:1020}}.ratio{position:relative;width:100%}.ratio:before{display:block;padding-top:var(--tblr-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--tblr-aspect-ratio: 100%}.ratio-2x1{--tblr-aspect-ratio: 50%}.ratio-1x2{--tblr-aspect-ratio: 200%}.ratio-3x1{--tblr-aspect-ratio: 33.3333333333%}.ratio-1x3{--tblr-aspect-ratio: 300%}.ratio-4x1{--tblr-aspect-ratio: 25%}.ratio-1x4{--tblr-aspect-ratio: 400%}.ratio-4x3{--tblr-aspect-ratio: 75%}.ratio-3x4{--tblr-aspect-ratio: 133.3333333333%}.ratio-16x9{--tblr-aspect-ratio: 56.25%}.ratio-9x16{--tblr-aspect-ratio: 177.7777777778%}.ratio-21x9{--tblr-aspect-ratio: 42.8571428571%}.ratio-9x21{--tblr-aspect-ratio: 233.3333333333%}.focus-ring:focus{outline:0;box-shadow:var(--tblr-focus-ring-x, 0) var(--tblr-focus-ring-y, 0) var(--tblr-focus-ring-blur, 0) var(--tblr-focus-ring-width) var(--tblr-focus-ring-color)}.bg-white-overlay{color:#fff;background-color:#f9fafb3d}.bg-dark-overlay{color:#fff;background-color:#1f29373d}.bg-cover{background-repeat:no-repeat;background-size:cover;background-position:center}.bg-primary{background-color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-primary-lt{color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-primary-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-primary{border-color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-primary{--tblr-gradient-from: var(--tblr-primary)}.bg-gradient-to-primary{--tblr-gradient-to: var(--tblr-primary)}.bg-gradient-via-primary{--tblr-gradient-via: var(--tblr-primary);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-primary{color:#f9fafb!important;background-color:RGBA(var(--tblr-primary-rgb),var(--tblr-bg-opacity, 1))!important}.link-primary{color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-primary:hover,.link-primary:focus{color:RGBA(0,106,100,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(0,106,100,var(--tblr-link-underline-opacity, 1))!important}.bg-secondary{background-color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-secondary-lt{color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-secondary-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-secondary{border-color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-secondary{--tblr-gradient-from: var(--tblr-secondary)}.bg-gradient-to-secondary{--tblr-gradient-to: var(--tblr-secondary)}.bg-gradient-via-secondary{--tblr-gradient-via: var(--tblr-secondary);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-secondary,.text-bg-gray{color:#f9fafb!important;background-color:RGBA(var(--tblr-secondary-rgb),var(--tblr-bg-opacity, 1))!important}.link-secondary{color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-secondary:hover,.link-secondary:focus{color:RGBA(86,91,102,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(86,91,102,var(--tblr-link-underline-opacity, 1))!important}.bg-success{background-color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-success-lt{color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-success-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-success{border-color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-success{--tblr-gradient-from: var(--tblr-success)}.bg-gradient-to-success{--tblr-gradient-to: var(--tblr-success)}.bg-gradient-via-success{--tblr-gradient-via: var(--tblr-success);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-success{color:#f9fafb!important;background-color:RGBA(var(--tblr-success-rgb),var(--tblr-bg-opacity, 1))!important}.link-success{color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-success:hover,.link-success:focus{color:RGBA(38,143,54,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(38,143,54,var(--tblr-link-underline-opacity, 1))!important}.bg-info{background-color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-info-lt{color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-info-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-info{border-color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-info{--tblr-gradient-from: var(--tblr-info)}.bg-gradient-to-info{--tblr-gradient-to: var(--tblr-info)}.bg-gradient-via-info{--tblr-gradient-via: var(--tblr-info);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-info{color:#f9fafb!important;background-color:RGBA(var(--tblr-info-rgb),var(--tblr-bg-opacity, 1))!important}.link-info{color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-info:hover,.link-info:focus{color:RGBA(53,122,180,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(53,122,180,var(--tblr-link-underline-opacity, 1))!important}.bg-warning{background-color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-warning-lt{color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-warning-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-warning{border-color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-warning{--tblr-gradient-from: var(--tblr-warning)}.bg-gradient-to-warning{--tblr-gradient-to: var(--tblr-warning)}.bg-gradient-via-warning{--tblr-gradient-via: var(--tblr-warning);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-warning{color:#f9fafb!important;background-color:RGBA(var(--tblr-warning-rgb),var(--tblr-bg-opacity, 1))!important}.link-warning{color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-warning:hover,.link-warning:focus{color:RGBA(196,127,0,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(196,127,0,var(--tblr-link-underline-opacity, 1))!important}.bg-danger{background-color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-danger-lt{color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-danger-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-danger{border-color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-danger{--tblr-gradient-from: var(--tblr-danger)}.bg-gradient-to-danger{--tblr-gradient-to: var(--tblr-danger)}.bg-gradient-via-danger{--tblr-gradient-via: var(--tblr-danger);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-danger{color:#f9fafb!important;background-color:RGBA(var(--tblr-danger-rgb),var(--tblr-bg-opacity, 1))!important}.link-danger{color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-danger:hover,.link-danger:focus{color:RGBA(171,46,46,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(171,46,46,var(--tblr-link-underline-opacity, 1))!important}.bg-light{background-color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-light-lt{color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-light-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-light{border-color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-light{--tblr-gradient-from: var(--tblr-light)}.bg-gradient-to-light{--tblr-gradient-to: var(--tblr-light)}.bg-gradient-via-light{--tblr-gradient-via: var(--tblr-light);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-light,.text-bg-white{color:#1f2937!important;background-color:RGBA(var(--tblr-light-rgb),var(--tblr-bg-opacity, 1))!important}.link-light{color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-light:hover,.link-light:focus{color:RGBA(250,251,252,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(250,251,252,var(--tblr-link-underline-opacity, 1))!important}.bg-dark{background-color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-dark-lt{color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-dark-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-dark{border-color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-dark{--tblr-gradient-from: var(--tblr-dark)}.bg-gradient-to-dark{--tblr-gradient-to: var(--tblr-dark)}.bg-gradient-via-dark{--tblr-gradient-via: var(--tblr-dark);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-dark,.text-bg-black{color:#f9fafb!important;background-color:RGBA(var(--tblr-dark-rgb),var(--tblr-bg-opacity, 1))!important}.link-dark{color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-dark:hover,.link-dark:focus{color:RGBA(25,33,44,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(25,33,44,var(--tblr-link-underline-opacity, 1))!important}.bg-muted{background-color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-muted-lt{color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-muted-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-muted{border-color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-muted{--tblr-gradient-from: var(--tblr-muted)}.bg-gradient-to-muted{--tblr-gradient-to: var(--tblr-muted)}.bg-gradient-via-muted{--tblr-gradient-via: var(--tblr-muted);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-muted{color:#f9fafb!important;background-color:RGBA(var(--tblr-muted-rgb),var(--tblr-bg-opacity, 1))!important}.link-muted{color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-muted:hover,.link-muted:focus{color:RGBA(86,91,102,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(86,91,102,var(--tblr-link-underline-opacity, 1))!important}.bg-blue{background-color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-blue-lt{color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-blue-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-blue{border-color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-blue{--tblr-gradient-from: var(--tblr-blue)}.bg-gradient-to-blue{--tblr-gradient-to: var(--tblr-blue)}.bg-gradient-via-blue{--tblr-gradient-via: var(--tblr-blue);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-blue{color:#f9fafb!important;background-color:RGBA(var(--tblr-blue-rgb),var(--tblr-bg-opacity, 1))!important}.link-blue{color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-blue:hover,.link-blue:focus{color:RGBA(5,89,167,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(5,89,167,var(--tblr-link-underline-opacity, 1))!important}.bg-azure{background-color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-azure-lt{color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-azure-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-azure{border-color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-azure{--tblr-gradient-from: var(--tblr-azure)}.bg-gradient-to-azure{--tblr-gradient-to: var(--tblr-azure)}.bg-gradient-via-azure{--tblr-gradient-via: var(--tblr-azure);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-azure{color:#f9fafb!important;background-color:RGBA(var(--tblr-azure-rgb),var(--tblr-bg-opacity, 1))!important}.link-azure{color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-azure:hover,.link-azure:focus{color:RGBA(53,122,180,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(53,122,180,var(--tblr-link-underline-opacity, 1))!important}.bg-indigo{background-color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-indigo-lt{color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-indigo-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-indigo{border-color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-indigo{--tblr-gradient-from: var(--tblr-indigo)}.bg-gradient-to-indigo{--tblr-gradient-to: var(--tblr-indigo)}.bg-gradient-via-indigo{--tblr-gradient-via: var(--tblr-indigo);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-indigo{color:#f9fafb!important;background-color:RGBA(var(--tblr-indigo-rgb),var(--tblr-bg-opacity, 1))!important}.link-indigo{color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-indigo:hover,.link-indigo:focus{color:RGBA(53,79,188,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(53,79,188,var(--tblr-link-underline-opacity, 1))!important}.bg-purple{background-color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-purple-lt{color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-purple-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-purple{border-color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-purple{--tblr-gradient-from: var(--tblr-purple)}.bg-gradient-to-purple{--tblr-gradient-to: var(--tblr-purple)}.bg-gradient-via-purple{--tblr-gradient-via: var(--tblr-purple);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-purple{color:#f9fafb!important;background-color:RGBA(var(--tblr-purple-rgb),var(--tblr-bg-opacity, 1))!important}.link-purple{color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-purple:hover,.link-purple:focus{color:RGBA(139,50,161,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(139,50,161,var(--tblr-link-underline-opacity, 1))!important}.bg-pink{background-color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-pink-lt{color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-pink-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-pink{border-color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-pink{--tblr-gradient-from: var(--tblr-pink)}.bg-gradient-to-pink{--tblr-gradient-to: var(--tblr-pink)}.bg-gradient-via-pink{--tblr-gradient-via: var(--tblr-pink);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-pink{color:#f9fafb!important;background-color:RGBA(var(--tblr-pink-rgb),var(--tblr-bg-opacity, 1))!important}.link-pink{color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-pink:hover,.link-pink:focus{color:RGBA(171,41,86,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(171,41,86,var(--tblr-link-underline-opacity, 1))!important}.bg-red{background-color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-red-lt{color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-red-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-red{border-color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-red{--tblr-gradient-from: var(--tblr-red)}.bg-gradient-to-red{--tblr-gradient-to: var(--tblr-red)}.bg-gradient-via-red{--tblr-gradient-via: var(--tblr-red);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-red{color:#f9fafb!important;background-color:RGBA(var(--tblr-red-rgb),var(--tblr-bg-opacity, 1))!important}.link-red{color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-red:hover,.link-red:focus{color:RGBA(171,46,46,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(171,46,46,var(--tblr-link-underline-opacity, 1))!important}.bg-orange{background-color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-orange-lt{color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-orange-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-orange{border-color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-orange{--tblr-gradient-from: var(--tblr-orange)}.bg-gradient-to-orange{--tblr-gradient-to: var(--tblr-orange)}.bg-gradient-via-orange{--tblr-gradient-via: var(--tblr-orange);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-orange{color:#f9fafb!important;background-color:RGBA(var(--tblr-orange-rgb),var(--tblr-bg-opacity, 1))!important}.link-orange{color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-orange:hover,.link-orange:focus{color:RGBA(198,82,6,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(198,82,6,var(--tblr-link-underline-opacity, 1))!important}.bg-yellow{background-color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-yellow-lt{color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-yellow-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-yellow{border-color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-yellow{--tblr-gradient-from: var(--tblr-yellow)}.bg-gradient-to-yellow{--tblr-gradient-to: var(--tblr-yellow)}.bg-gradient-via-yellow{--tblr-gradient-via: var(--tblr-yellow);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-yellow{color:#f9fafb!important;background-color:RGBA(var(--tblr-yellow-rgb),var(--tblr-bg-opacity, 1))!important}.link-yellow{color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-yellow:hover,.link-yellow:focus{color:RGBA(196,127,0,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(196,127,0,var(--tblr-link-underline-opacity, 1))!important}.bg-lime{background-color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-lime-lt{color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-lime-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-lime{border-color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-lime{--tblr-gradient-from: var(--tblr-lime)}.bg-gradient-to-lime{--tblr-gradient-to: var(--tblr-lime)}.bg-gradient-via-lime{--tblr-gradient-via: var(--tblr-lime);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-lime{color:#f9fafb!important;background-color:RGBA(var(--tblr-lime-rgb),var(--tblr-bg-opacity, 1))!important}.link-lime{color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-lime:hover,.link-lime:focus{color:RGBA(93,147,18,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(93,147,18,var(--tblr-link-underline-opacity, 1))!important}.bg-green{background-color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-green-lt{color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-green-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-green{border-color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-green{--tblr-gradient-from: var(--tblr-green)}.bg-gradient-to-green{--tblr-gradient-to: var(--tblr-green)}.bg-gradient-via-green{--tblr-gradient-via: var(--tblr-green);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-green{color:#f9fafb!important;background-color:RGBA(var(--tblr-green-rgb),var(--tblr-bg-opacity, 1))!important}.link-green{color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-green:hover,.link-green:focus{color:RGBA(38,143,54,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(38,143,54,var(--tblr-link-underline-opacity, 1))!important}.bg-teal{background-color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-teal-lt{color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-teal-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-teal{border-color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-teal{--tblr-gradient-from: var(--tblr-teal)}.bg-gradient-to-teal{--tblr-gradient-to: var(--tblr-teal)}.bg-gradient-via-teal{--tblr-gradient-via: var(--tblr-teal);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-teal{color:#f9fafb!important;background-color:RGBA(var(--tblr-teal-rgb),var(--tblr-bg-opacity, 1))!important}.link-teal{color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-teal:hover,.link-teal:focus{color:RGBA(10,133,96,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(10,133,96,var(--tblr-link-underline-opacity, 1))!important}.bg-cyan{background-color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-cyan-lt{color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-cyan-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-cyan{border-color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-cyan{--tblr-gradient-from: var(--tblr-cyan)}.bg-gradient-to-cyan{--tblr-gradient-to: var(--tblr-cyan)}.bg-gradient-via-cyan{--tblr-gradient-via: var(--tblr-cyan);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-cyan{color:#f9fafb!important;background-color:RGBA(var(--tblr-cyan-rgb),var(--tblr-bg-opacity, 1))!important}.link-cyan{color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-cyan:hover,.link-cyan:focus{color:RGBA(18,130,147,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(18,130,147,var(--tblr-link-underline-opacity, 1))!important}.bg-white{background-color:color-mix(in srgb,var(--tblr-white) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-white-lt{color:color-mix(in srgb,var(--tblr-white) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-white-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-white{border-color:color-mix(in srgb,var(--tblr-white) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-white{--tblr-gradient-from: var(--tblr-white)}.bg-gradient-to-white{--tblr-gradient-to: var(--tblr-white)}.bg-gradient-via-white{--tblr-gradient-via: var(--tblr-white);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-white{color:#1f2937!important;background-color:RGBA(var(--tblr-white-rgb),var(--tblr-bg-opacity, 1))!important}.link-white{color:color-mix(in srgb,var(--tblr-white) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-white) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-white:hover,.link-white:focus{color:RGBA(255,255,255,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(255,255,255,var(--tblr-link-underline-opacity, 1))!important}.text-primary{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-primary-fg{color:var(--tblr-primary-fg)!important}.text-secondary{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-secondary-fg{color:var(--tblr-secondary-fg)!important}.text-success{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-success-fg{color:var(--tblr-success-fg)!important}.text-info{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-info-fg{color:var(--tblr-info-fg)!important}.text-warning{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-warning-fg{color:var(--tblr-warning-fg)!important}.text-danger{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-danger-fg{color:var(--tblr-danger-fg)!important}.text-light{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-light-fg{color:var(--tblr-light-fg)!important}.text-dark{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-dark-fg{color:var(--tblr-dark-fg)!important}.text-muted{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-muted-fg{color:var(--tblr-muted-fg)!important}.text-blue{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-blue-fg{color:var(--tblr-blue-fg)!important}.text-azure{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-azure-fg{color:var(--tblr-azure-fg)!important}.text-indigo{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-indigo-fg{color:var(--tblr-indigo-fg)!important}.text-purple{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-purple-fg{color:var(--tblr-purple-fg)!important}.text-pink{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-pink-fg{color:var(--tblr-pink-fg)!important}.text-red{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-red-fg{color:var(--tblr-red-fg)!important}.text-orange{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-orange-fg{color:var(--tblr-orange-fg)!important}.text-yellow{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-yellow-fg{color:var(--tblr-yellow-fg)!important}.text-lime{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-lime-fg{color:var(--tblr-lime-fg)!important}.text-green{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-green-fg{color:var(--tblr-green-fg)!important}.text-teal{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-teal-fg{color:var(--tblr-teal-fg)!important}.text-cyan{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-cyan-fg{color:var(--tblr-cyan-fg)!important}.bg-gray-50{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-50) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-50-fg{color:var(--tblr-gray-50-fg)!important}.bg-gray-100{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-100) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-100-fg{color:var(--tblr-gray-100-fg)!important}.bg-gray-200{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-200) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-200-fg{color:var(--tblr-gray-200-fg)!important}.bg-gray-300{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-300) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-300-fg{color:var(--tblr-gray-300-fg)!important}.bg-gray-400{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-400) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-400-fg{color:var(--tblr-gray-400-fg)!important}.bg-gray-500{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-500) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-500-fg{color:var(--tblr-gray-500-fg)!important}.bg-gray-600{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-600) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-600-fg{color:var(--tblr-gray-600-fg)!important}.bg-gray-700{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-700) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-700-fg{color:var(--tblr-gray-700-fg)!important}.bg-gray-800{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-800) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-800-fg{color:var(--tblr-gray-800-fg)!important}.bg-gray-900{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-900) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-900-fg{color:var(--tblr-gray-900-fg)!important}.bg-gray-950{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-950) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-950-fg{color:var(--tblr-gray-950-fg)!important}.bg-x{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-x) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-x-fg{color:var(--tblr-x-fg)!important}.bg-facebook{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-facebook) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-facebook-fg{color:var(--tblr-facebook-fg)!important}.bg-twitter{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-twitter) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-twitter-fg{color:var(--tblr-twitter-fg)!important}.bg-linkedin{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-linkedin) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-linkedin-fg{color:var(--tblr-linkedin-fg)!important}.bg-google{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-google) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-google-fg{color:var(--tblr-google-fg)!important}.bg-youtube{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-youtube) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-youtube-fg{color:var(--tblr-youtube-fg)!important}.bg-vimeo{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-vimeo) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-vimeo-fg{color:var(--tblr-vimeo-fg)!important}.bg-dribbble{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-dribbble) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-dribbble-fg{color:var(--tblr-dribbble-fg)!important}.bg-github{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-github) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-github-fg{color:var(--tblr-github-fg)!important}.bg-instagram{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-instagram) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-instagram-fg{color:var(--tblr-instagram-fg)!important}.bg-pinterest{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-pinterest) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-pinterest-fg{color:var(--tblr-pinterest-fg)!important}.bg-vk{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-vk) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-vk-fg{color:var(--tblr-vk-fg)!important}.bg-rss{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-rss) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-rss-fg{color:var(--tblr-rss-fg)!important}.bg-flickr{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-flickr) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-flickr-fg{color:var(--tblr-flickr-fg)!important}.bg-bitbucket{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-bitbucket) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-bitbucket-fg{color:var(--tblr-bitbucket-fg)!important}.bg-tabler{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-tabler) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-tabler-fg{color:var(--tblr-tabler-fg)!important}.bg-inverted{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-bg-surface-inverted) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.bg-surface{background-color:var(--tblr-bg-surface)!important}.bg-surface-secondary{background-color:var(--tblr-bg-surface-secondary)!important}.bg-surface-tertiary{background-color:var(--tblr-bg-surface-tertiary)!important}.bg-surface-backdrop{background-color:color-mix(in srgb,var(--tblr-gray-800) 24%,transparent)!important}.scrollable{overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch}.scrollable.hover{overflow-y:hidden}.scrollable.hover>*{margin-top:-1px}.scrollable.hover:hover,.scrollable.hover:focus,.scrollable.hover:active{overflow:visible;overflow-y:auto}.touch .scrollable{overflow-y:auto!important}.scroll-x,.scroll-y{overflow:hidden;-webkit-overflow-scrolling:touch}.scroll-y{overflow-y:auto}.scroll-x{overflow-x:auto}.no-scroll{overflow:hidden}.w-0{width:0!important}.h-0{height:0!important}.w-1{width:.25rem!important}.h-1{height:.25rem!important}.w-2{width:.5rem!important}.h-2{height:.5rem!important}.w-3{width:1rem!important}.h-3{height:1rem!important}.w-4{width:1.5rem!important}.h-4{height:1.5rem!important}.w-5{width:2rem!important}.h-5{height:2rem!important}.w-6{width:2.5rem!important}.h-6{height:2.5rem!important}.w-auto{width:auto!important}.h-auto{height:auto!important}.w-px{width:1px!important}.h-px{height:1px!important}.w-full{width:100%!important}.h-full{height:100%!important}.opacity-0{opacity:0!important}.opacity-5{opacity:.05!important}.opacity-10{opacity:.1!important}.opacity-15{opacity:.15!important}.opacity-20{opacity:.2!important}.opacity-25{opacity:.25!important}.opacity-30{opacity:.3!important}.opacity-35{opacity:calc(35 / 100)!important}.opacity-40{opacity:.4!important}.opacity-45{opacity:.45!important}.opacity-50{opacity:.5!important}.opacity-55{opacity:.55!important}.opacity-60{opacity:.6!important}.opacity-65{opacity:.65!important}.opacity-70{opacity:calc(70 / 100)!important}.opacity-75{opacity:.75!important}.opacity-80{opacity:.8!important}.opacity-85{opacity:.85!important}.opacity-90{opacity:.9!important}.opacity-95{opacity:calc(95 / 100)!important}.opacity-100{opacity:1!important}.hover-shadow-sm:hover{box-shadow:0 .125rem .25rem #00000013!important}.hover-shadow:hover{box-shadow:rgba(var(--tblr-body-color-rgb),.04) 0 2px 4px!important}.hover-shadow-lg:hover{box-shadow:0 1rem 3rem #0000002d!important}.hover-shadow-none:hover{box-shadow:none!important}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.subpixel-antialiased{-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.hover-rotate-start,.hover-rotate-end,.hover-scale,.hover-elevate-down,.hover-elevate-up{transition:transform .3s ease}.hover-rotate-start:hover,.hover-rotate-end:hover,.hover-scale:hover,.hover-elevate-down:hover,.hover-elevate-up:hover{will-change:transform}.hover-elevate-up:hover{transform:translateY(-4px)}.hover-elevate-down:hover{transform:translateY(4px)}.hover-scale:hover{transform:scale(1.1)}.hover-rotate-end:hover{transform:rotate(4deg)}.hover-rotate-start:hover{transform:rotate(-4deg)}.ts-control{border:1px solid var(--tblr-border-color);padding:.5625rem 1rem;width:100%;overflow:hidden;position:relative;z-index:1;box-sizing:border-box;box-shadow:none;border-radius:var(--tblr-border-radius);display:flex;flex-wrap:wrap}.ts-wrapper.multi.has-items .ts-control{padding:calc(.5625rem - 1px) 1rem calc(.5625rem - 4px)}.full .ts-control{background-color:var(--tblr-bg-forms)}.disabled .ts-control,.disabled .ts-control *{cursor:default!important}.focus .ts-control{box-shadow:none}.ts-control>*{vertical-align:baseline;display:inline-block}.ts-wrapper.multi .ts-control>div{cursor:pointer;margin:0 3px 3px 0;padding:1px 5px;background:#efefef;color:#1f2937;border:0px solid #e5e7eb;overflow:auto}.ts-wrapper.multi .ts-control>div.active{background:#00857d;color:#fff;border:0px solid rgba(0,0,0,0)}.ts-wrapper.multi.disabled .ts-control>div,.ts-wrapper.multi.disabled .ts-control>div.active{color:#787878;background:#fff;border:0px solid white}.ts-control>input{flex:1 1 auto;min-width:7rem;display:inline-block!important;padding:0!important;min-height:0!important;max-height:none!important;max-width:100%!important;margin:0!important;text-indent:0!important;border:0 none!important;background:none!important;line-height:inherit!important;user-select:auto!important;box-shadow:none!important}.ts-control>input::-ms-clear{display:none}.ts-control>input:focus{outline:none!important}.has-items .ts-control>input{margin:0 4px!important}.ts-control.rtl{text-align:right}.ts-control.rtl.single .ts-control:after{left:calc(1rem + 5px);right:auto}.ts-control.rtl .ts-control>input{margin:0 4px 0 -2px!important}.disabled .ts-control{opacity:1;background-color:var(--tblr-bg-surface-secondary)}.input-hidden .ts-control>input{opacity:0;position:absolute;left:-10000px}.ts-dropdown{position:absolute;top:100%;left:0;width:100%;z-index:10;border:1px solid #d0d0d0;background:var(--tblr-bg-surface);margin:.25rem 0 0;border-top:0 none;box-sizing:border-box;box-shadow:0 1px 3px #0000001a;border-radius:0 0 var(--tblr-border-radius) var(--tblr-border-radius)}.ts-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.ts-dropdown [data-selectable] .highlight{background:#ffed2866;border-radius:1px}.ts-dropdown .option,.ts-dropdown .optgroup-header,.ts-dropdown .no-results,.ts-dropdown .create{padding:3px 1rem}.ts-dropdown .option,.ts-dropdown [data-disabled],.ts-dropdown [data-disabled] [data-selectable].option{cursor:inherit;opacity:.5}.ts-dropdown [data-selectable].option{opacity:1;cursor:pointer}.ts-dropdown .optgroup:first-child .optgroup-header{border-top:0 none}.ts-dropdown .optgroup-header{color:#4b5563;background:var(--tblr-bg-surface);cursor:default}.ts-dropdown .active{background-color:rgba(var(--tblr-secondary-rgb),.08);color:inherit}.ts-dropdown .active.create{color:inherit}.ts-dropdown .create{color:#1f293780}.ts-dropdown .spinner{display:inline-block;width:30px;height:30px;margin:3px 1rem}.ts-dropdown .spinner:after{content:" ";display:block;width:24px;height:24px;margin:3px;border-radius:50%;border:5px solid #d0d0d0;border-color:#d0d0d0 transparent #d0d0d0 transparent;animation:lds-dual-ring 1.2s linear infinite}@keyframes lds-dual-ring{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.ts-dropdown-content{overflow:hidden auto;max-height:200px;scroll-behavior:smooth}.ts-wrapper.plugin-drag_drop .ts-dragging{color:transparent!important}.ts-wrapper.plugin-drag_drop .ts-dragging>*{visibility:hidden!important}.plugin-checkbox_options:not(.rtl) .option input{margin-right:.5rem}.plugin-checkbox_options.rtl .option input{margin-left:.5rem}.plugin-clear_button{--ts-pr-clear-button: 1em}.plugin-clear_button .clear-button{opacity:0;position:absolute;top:50%;transform:translateY(-50%);right:calc(1rem - 5px);margin-right:0!important;background:transparent!important;transition:opacity .5s;cursor:pointer}.plugin-clear_button.form-select .clear-button,.plugin-clear_button.single .clear-button{right:max(var(--ts-pr-caret),1rem)}.plugin-clear_button.focus.has-items .clear-button,.plugin-clear_button:not(.disabled):hover.has-items .clear-button{opacity:1}.ts-wrapper .dropdown-header{position:relative;padding:6px 1rem;border-bottom:1px solid #d0d0d0;background:color-mix(var(--tblr-bg-surface),#d0d0d0,85%);border-radius:var(--tblr-border-radius) var(--tblr-border-radius) 0 0}.ts-wrapper .dropdown-header-close{position:absolute;right:1rem;top:50%;color:#1f2937;opacity:.4;margin-top:-12px;line-height:20px;font-size:20px!important}.ts-wrapper .dropdown-header-close:hover{color:#000}.plugin-dropdown_input.focus.dropdown-active .ts-control{box-shadow:none;border:1px solid var(--tblr-border-color);box-shadow:var(--tblr-shadow-input)}.plugin-dropdown_input .dropdown-input{border:1px solid #d0d0d0;border-width:0 0 1px;display:block;padding:.5625rem 1rem;box-shadow:none;width:100%;background:transparent}.plugin-dropdown_input.focus .ts-dropdown .dropdown-input{border-color:#80c2be;outline:0;box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.plugin-dropdown_input .items-placeholder{border:0 none!important;box-shadow:none!important;width:100%}.plugin-dropdown_input.has-items .items-placeholder,.plugin-dropdown_input.dropdown-active .items-placeholder{display:none!important}.ts-wrapper.plugin-input_autogrow.has-items .ts-control>input{min-width:0}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input{flex:none;min-width:4px}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input::placeholder{color:transparent}.ts-dropdown.plugin-optgroup_columns .ts-dropdown-content{display:flex}.ts-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0 none;flex-grow:1;flex-basis:0;min-width:0}.ts-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0 none}.ts-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.ts-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0 none}.ts-wrapper.plugin-remove_button .item{display:inline-flex;align-items:center}.ts-wrapper.plugin-remove_button .item .remove{color:inherit;text-decoration:none;vertical-align:middle;display:inline-block;padding:0 5px;border-radius:0 2px 2px 0;box-sizing:border-box}.ts-wrapper.plugin-remove_button .item .remove:hover{background:#0000000d}.ts-wrapper.plugin-remove_button.disabled .item .remove:hover{background:none}.ts-wrapper.plugin-remove_button .remove-single{position:absolute;right:0;top:0;font-size:23px}.ts-wrapper.plugin-remove_button:not(.rtl) .item{padding-right:0!important}.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove{border-left:1px solid #e5e7eb;margin-left:5px}.ts-wrapper.plugin-remove_button:not(.rtl) .item.active .remove{border-left-color:#0000}.ts-wrapper.plugin-remove_button:not(.rtl).disabled .item .remove{border-left-color:#fff}.ts-wrapper.plugin-remove_button.rtl .item{padding-left:0!important}.ts-wrapper.plugin-remove_button.rtl .item .remove{border-right:1px solid #e5e7eb;margin-right:5px}.ts-wrapper.plugin-remove_button.rtl .item.active .remove{border-right-color:#0000}.ts-wrapper.plugin-remove_button.rtl.disabled .item .remove{border-right-color:#fff}:root{--ts-pr-clear-button: 0px;--ts-pr-caret: 0px;--ts-pr-min: .75rem}.ts-wrapper.single .ts-control,.ts-wrapper.single .ts-control input{cursor:pointer}.ts-control:not(.rtl){padding-right:max(var(--ts-pr-min),var(--ts-pr-clear-button) + var(--ts-pr-caret))!important}.ts-control.rtl{padding-left:max(var(--ts-pr-min),var(--ts-pr-clear-button) + var(--ts-pr-caret))!important}.ts-wrapper{position:relative}.ts-dropdown,.ts-control,.ts-control input{color:#1f2937;font-family:inherit;font-size:inherit;line-height:1.25rem}.ts-control,.ts-wrapper.single.input-active .ts-control{background:var(--tblr-bg-forms);cursor:text}.ts-hidden-accessible{border:0!important;clip:rect(0 0 0 0)!important;clip-path:inset(50%)!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;white-space:nowrap!important}.ts-dropdown,.ts-dropdown.form-control,.ts-dropdown.form-select{height:auto;padding:0;z-index:1000;background:var(--tblr-bg-surface);border:1px solid var(--tblr-border-color-translucent);border-radius:6px;box-shadow:0 6px 12px #0000002d}.ts-dropdown .optgroup-header{font-size:.765625rem;line-height:1.4285714286}.ts-dropdown .optgroup:first-child:before{display:none}.ts-dropdown .optgroup:before{content:" ";display:block;height:0;margin:var(--tblr-spacer-2) 0;overflow:hidden;border-top:1px solid var(--tblr-border-color-translucent);margin-left:-1rem;margin-right:-1rem}.ts-dropdown .create{padding-left:1rem}.ts-dropdown-content{padding:5px 0}.ts-control{box-shadow:var(--tblr-shadow-input);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.ts-control{transition:none}}.ts-control{display:flex;align-items:center}.focus .ts-control{border-color:#80c2be;outline:0;box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.ts-control .item{display:flex;align-items:center}.ts-control input::placeholder{color:var(--bs-secondary-color, #a7aeb8);opacity:1}.ts-wrapper.is-invalid,select.tomselected.is-invalid+div.ts-wrapper,.was-validated .invalid,.was-validated :invalid+.ts-wrapper{border-color:var(--tblr-form-invalid-color)}.ts-wrapper.is-invalid:not(.single),select.tomselected.is-invalid+div.ts-wrapper:not(.single),.was-validated .invalid:not(.single),.was-validated :invalid+.ts-wrapper:not(.single){background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23d63939' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='18' y1='6' x2='6' y2='18'%3e%3c/line%3e%3cline x1='6' y1='6' x2='18' y2='18'%3e%3c/line%3e%3c/svg%3e");background-position:right 1.53125rem center;background-size:1.8125rem 1.8125rem;background-repeat:no-repeat}.ts-wrapper.is-invalid.single,select.tomselected.is-invalid+div.ts-wrapper.single,.was-validated .invalid.single,.was-validated :invalid+.ts-wrapper.single{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23d63939' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='18' y1='6' x2='6' y2='18'%3e%3c/line%3e%3cline x1='6' y1='6' x2='18' y2='18'%3e%3c/line%3e%3c/svg%3e");background-position:right 1rem center,center right 3rem;background-size:16px 12px,1.8125rem 1.8125rem;background-repeat:no-repeat}.ts-wrapper.is-invalid.focus .ts-control,select.tomselected.is-invalid+div.ts-wrapper.focus .ts-control,.was-validated .invalid.focus .ts-control,.was-validated :invalid+.ts-wrapper.focus .ts-control{border-color:var(--tblr-form-invalid-color);box-shadow:0 0 0 .25rem rgba(var(--tblr-form-invalid-color),.25)}.ts-wrapper.is-valid,.was-validated .valid,.was-validated :valid+.ts-wrapper{border-color:var(--tblr-form-valid-color)}.ts-wrapper.is-valid:not(.single),.was-validated .valid:not(.single),.was-validated :valid+.ts-wrapper:not(.single){background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%232fb344' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='20 6 9 17 4 12'%3e%3c/polyline%3e%3c/svg%3e");background-position:right 1.53125rem center;background-size:1.8125rem 1.8125rem;background-repeat:no-repeat}.ts-wrapper.is-valid.single,.was-validated .valid.single,.was-validated :valid+.ts-wrapper.single{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%232fb344' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='20 6 9 17 4 12'%3e%3c/polyline%3e%3c/svg%3e");background-position:right 1rem center,center right 3rem;background-size:16px 12px,1.8125rem 1.8125rem;background-repeat:no-repeat}.ts-wrapper.is-valid.focus .ts-control,.was-validated .valid.focus .ts-control,.was-validated :valid+.ts-wrapper.focus .ts-control{border-color:var(--tblr-form-valid-color);box-shadow:0 0 0 .25rem rgba(var(--tblr-form-valid-color),.25)}.ts-wrapper{min-height:calc(1.25rem + 1.125rem + calc(var(--tblr-border-width) * 2));display:flex}.input-group-sm>.ts-wrapper,.ts-wrapper.form-select-sm,.ts-wrapper.form-control-sm{min-height:calc(1.25rem + .625rem + calc(var(--tblr-border-width) * 2))}.input-group-sm>.ts-wrapper .ts-control,.ts-wrapper.form-select-sm .ts-control,.ts-wrapper.form-control-sm .ts-control{border-radius:var(--tblr-border-radius-sm);font-size:.75rem}.input-group-sm>.ts-wrapper.has-items .ts-control,.ts-wrapper.form-select-sm.has-items .ts-control,.ts-wrapper.form-control-sm.has-items .ts-control{font-size:.75rem}.input-group-sm>.ts-wrapper.multi.has-items .ts-control,.ts-wrapper.form-select-sm.multi.has-items .ts-control,.ts-wrapper.form-control-sm.multi.has-items .ts-control{padding-top:calc((calc(1.25rem + .625rem + calc(var(--tblr-border-width) * 2)) - 1.25rem * .75rem - calc((var(--tblr-border-width) + 1px) * 2)) / 2)!important}.ts-wrapper.multi.has-items .ts-control{padding-left:calc(1rem - 5px);--ts-pr-min: calc(1rem - 5px) }.ts-wrapper.multi .ts-control>div{border-radius:calc(var(--tblr-border-radius) - 1px)}.input-group-lg>.ts-wrapper,.ts-wrapper.form-control-lg,.ts-wrapper.form-select-lg{min-height:calc(1.25rem + 1.375rem + calc(var(--tblr-border-width) * 2))}.input-group-lg>.ts-wrapper .ts-control,.ts-wrapper.form-control-lg .ts-control,.ts-wrapper.form-select-lg .ts-control{border-radius:var(--tblr-border-radius-lg);font-size:1rem}.ts-wrapper:not(.form-control,.form-select){padding:0;border:none;height:auto;box-shadow:none;background:none}.ts-wrapper:not(.form-control,.form-select).single .ts-control{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right 1rem center;background-size:16px 12px}.ts-wrapper.form-select,.ts-wrapper.single{--ts-pr-caret: 3rem}.ts-wrapper.form-control,.ts-wrapper.form-select{padding:0!important;height:auto;box-shadow:none;display:flex}.ts-wrapper.form-control .ts-control,.ts-wrapper.form-control.single.input-active .ts-control,.ts-wrapper.form-select .ts-control,.ts-wrapper.form-select.single.input-active .ts-control{border:none!important}.ts-wrapper.form-control:not(.disabled) .ts-control,.ts-wrapper.form-control:not(.disabled).single.input-active .ts-control,.ts-wrapper.form-select:not(.disabled) .ts-control,.ts-wrapper.form-select:not(.disabled).single.input-active .ts-control{background:transparent!important}.input-group>.ts-wrapper{flex-grow:1;width:1%}.input-group>.ts-wrapper:not(:nth-child(2))>.ts-control{border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.ts-wrapper:not(:last-child)>.ts-control{border-top-right-radius:0;border-bottom-right-radius:0}.form-select .ts-dropdown,.form-select .ts-control,.form-select .ts-control input{color:var(--tblr-body-color)}:root{--ts-pr-clear-button: 0rem;--ts-pr-caret: 0rem}.ts-input{color:inherit}.focus .ts-control{border-radius:var(--tblr-border-radius)}.ts-control{color:inherit}.ts-control .dropdown-menu{width:100%;height:auto}.ts-wrapper .form-control,.ts-wrapper .form-select,.ts-wrapper.form-control,.ts-wrapper.form-select{box-shadow:var(--tblr-shadow-input)}.ts-wrapper.is-invalid .ts-control,select.tomselected.is-invalid+div.ts-wrapper .ts-control,.ts-wrapper.is-valid .ts-control{--ts-pr-clear-button: 1.5rem}.ts-dropdown{background:var(--tblr-bg-surface);color:var(--tblr-body-color);box-shadow:var(--tblr-shadow-dropdown);z-index:1000}.ts-dropdown .option{padding:.5rem .75rem}.ts-control,.ts-control input{color:var(--tblr-body-color)}.ts-control input::placeholder{color:var(--tblr-tertiary)}.ts-wrapper.multi .ts-control>div,.ts-wrapper.multi.disabled .ts-control>div{background:var(--tblr-bg-surface-secondary);border:1px solid var(--tblr-border-color);color:var(--tblr-body-color)}.ts-wrapper.disabled .ts-control{opacity:1}.ts-wrapper.disabled .ts-control>div.item{color:var(--tblr-gray-500)}html{scroll-behavior:auto!important}mark,.mark{padding-left:0;padding-right:0}.table-responsive .dropdown,.table-responsive .btn-group,.table-responsive .btn-group-vertical{position:static}.progress{min-width:80px}hr.dropdown-divider,.dropdown-divider.hr{margin-bottom:.25rem;margin-top:.25rem}.dropdown-item{font-weight:400}*{font-feature-settings:"liga" 0;font-variant-ligatures:none}@media(min-width:992px){:root,:host{margin-left:0;scrollbar-gutter:stable}}pre{background-color:transparent;color:inherit}.alert{background:var(--tblr-bg-surface)}.badge{user-select:text}.btn{display:inline-block}.btn:focus{border:1px solid var(--tblr-primary-fg);outline:2px solid var(--tblr-primary)!important}.btn-sm,.btn-group-sm>.btn{border-radius:6px}.dropdown-item{display:inline-block}.footer .text-primary{color:#001423!important}.nav-tabs .nav-link{display:inline-block}.page,.page-tabs .nav-tabs .nav-link.active{background-color:var(--tblr-bg-surface-tertiary)!important}.navbar{--tblr-navbar-active-bg: rgba(0, 0, 0, .06)}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{--tblr-alert-color: darken(var(--tblr-warning),10%);--tblr-link-color: #00F2D4;--tblr-link-color-rgb: 0,242,212;--tblr-link-hover-color-rgb: 0,242,212;--tblr-secondary: #9ca3af;--tblr-primary: #00F2D4;--tblr-primary-fg: #001423;--tblr-primary-rgb: 0,242,212;--tblr-btn-active-color: #001423}html[data-bs-theme=dark] ::selection,body[data-bs-theme=dark] html[data-bs-theme=light] ::selection{background-color:rgba(var(--tblr-primary-rgb),.48)}html[data-bs-theme=dark] .btn-primary,body[data-bs-theme=dark] html[data-bs-theme=light] .btn-primary,html[data-bs-theme=dark] .bg-primary .card-title,body[data-bs-theme=dark] html[data-bs-theme=light] .bg-primary .card-title,html[data-bs-theme=dark] .bg-primary a,body[data-bs-theme=dark] html[data-bs-theme=light] .bg-primary a,html[data-bs-theme=dark] .bg-primary i,body[data-bs-theme=dark] html[data-bs-theme=light] .bg-primary i,html[data-bs-theme=dark] .text-bg-primary,body[data-bs-theme=dark] html[data-bs-theme=light] .text-bg-primary{color:#001423!important}html[data-bs-theme=dark] .card,body[data-bs-theme=dark] html[data-bs-theme=light] .card{background:#001423!important}html[data-bs-theme=dark],body[data-bs-theme=dark] html[data-bs-theme=light],html[data-bs-theme=dark] .navbar,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar,html[data-bs-theme=dark] .page-header,body[data-bs-theme=dark] html[data-bs-theme=light] .page-header{background-color:#001423}html[data-bs-theme=dark] .page,body[data-bs-theme=dark] html[data-bs-theme=light] .page,html[data-bs-theme=dark] .page-tabs .nav-tabs .nav-link.active,body[data-bs-theme=dark] html[data-bs-theme=light] .page-tabs .nav-tabs .nav-link.active{background-color:#081b2a!important}html[data-bs-theme=dark] .page-link.active,body[data-bs-theme=dark] html[data-bs-theme=light] .page-link.active,html[data-bs-theme=dark] .active>.page-link,body[data-bs-theme=dark] html[data-bs-theme=light] .active>.page-link{color:#001423}html[data-bs-theme=dark] .text-bg-primary,body[data-bs-theme=dark] html[data-bs-theme=light] .text-bg-primary{color:#001423!important}html[data-bs-theme=dark] .text-muted,body[data-bs-theme=dark] html[data-bs-theme=light] .text-muted{color:var(--tblr-secondary-color)!important}html[data-bs-theme=dark] .text-secondary,body[data-bs-theme=dark] html[data-bs-theme=light] .text-secondary{color:#9ca3af!important}html[data-bs-theme=dark] .footer .text-primary,body[data-bs-theme=dark] html[data-bs-theme=light] .footer .text-primary{color:#fff!important}html[data-bs-theme=dark] .toast,body[data-bs-theme=dark] html[data-bs-theme=light] .toast{color:var(--tblr-body-color)}html[data-bs-theme=dark] .table-primary,body[data-bs-theme=dark] html[data-bs-theme=light] .table-primary{--tblr-table-bg: rgba(var(--tblr-secondary-rgb), .48);--tblr-table-hover-bg: inherit;--tblr-table-hover-color: inherit}pre code{padding:unset}.dropdown-toggle:after{font-family:Material Design Icons;content:"\f0140";padding-right:9px;border-bottom:none;border-left:none;transform:none;vertical-align:.05em;height:auto}.accordion-button-toggle{align-items:center;justify-content:center;flex:0 0 auto}.accordion-button-toggle>i{display:block;line-height:1}:root:not(.dummy)[data-bs-theme=light] .hide-theme-light,:root:not(.dummy)[data-bs-theme=dark] .hide-theme-dark,body[data-bs-theme=dark] [data-bs-theme=light]:root:not(.dummy) .hide-theme-dark{display:none!important}:root:not(.dummy)[data-bs-theme=dark] .hide-theme-light,body[data-bs-theme=dark] [data-bs-theme=light]:root:not(.dummy) .hide-theme-light,:root:not(.dummy)[data-bs-theme=light] .hide-theme-dark{display:inline-flex!important}.ts-wrapper.multi .ts-control{padding:7px 7px 3px}.ts-wrapper.multi .ts-control div{margin:0 4px 4px 0}.badge a{color:inherit;text-decoration:none}.page-body .card{margin-bottom:1rem}.page-body .card .card-header,.page-body .card .card-body,.page-body .card .card-footer{padding:.75rem}.page-body .card .card-header{background:var(--tblr-bg-surface-tertiary)}.page-body .card h2.card-header,.page-body .card .card-header.h2{font-size:var(--tblr-font-size-h5);line-height:var(--tblr-line-height-h5);margin-bottom:0}.page-body .card .list-group-item{padding:.5rem .75rem}.page-body .card .table,.page-body .card .markdown>table{margin-bottom:0}form.object-edit{margin:auto;max-width:800px}.col-form-label.required{font-weight:700}.col-form-label.required:after{position:absolute;display:inline-block;margin-left:0;font-family:Material Design Icons;font-size:8px;content:"\f06c4"}.has-errors input,.has-errors select,.has-errors textarea{border:1px solid #d63939}select[multiple] optgroup{top:0;background-color:var(--bs-body-bg);font-style:normal;font-weight:700}select[multiple] option{padding-left:.5rem}.modifier-select{min-width:10rem;max-width:15rem;width:auto;white-space:nowrap}.page{background-color:var(--tblr-bg-surface-secondary)}.page-header{background-color:var(--tblr-bg-surface);min-height:0}.navbar-vertical.navbar-expand-lg .navbar-collapse .nav-link-icon,.navbar-vertical.navbar-expand-lg .navbar-collapse .nav-link-title{color:#001423}.navbar-vertical.navbar-expand-lg .navbar-collapse .text-secondary{color:#00857d!important}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item a{color:#001423}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item .btn-group{visibility:hidden}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:hover,.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.active{background-color:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:hover a,.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.active a{text-decoration:none}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:hover .btn-group,.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.active .btn-group{visibility:visible}.navbar-vertical.navbar-expand-lg .navbar-nav{z-index:1}@media(max-width:991.98px){.navbar-vertical.navbar-expand-lg .navbar-brand{padding:.2rem 0}}.navbar-vertical.navbar-expand-lg .navbar-brand a:hover{text-decoration:none}.navbar-vertical.navbar-expand-lg img.motif{bottom:0;display:none;left:0;mask-image:linear-gradient(180deg,#0000,#0000004d);opacity:.5;position:fixed;user-drag:none;user-select:none;-moz-user-select:none;-webkit-user-drag:none;-webkit-user-select:none;-ms-user-select:none;width:18rem}@media(min-width:992px){.navbar-vertical.navbar-expand-lg img.motif{display:block}}html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg{background:linear-gradient(180deg,#00857d00,#00857d1a),#fff}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg{background:linear-gradient(180deg,#00f2d400,#00f2d41a),#001423}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .nav-item.dropdown.active:after,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .nav-item.dropdown.active:after{border-color:#00f2d4!important}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .nav-link-title,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .nav-link-title,html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .nav-link-icon,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .nav-link-icon,html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item a,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .dropdown-item a{color:#fff!important}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item.active,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .dropdown-item.active,html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item:hover,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .dropdown-item:hover{background-color:#ffffff0f!important}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .text-secondary,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .text-secondary{color:#00f2d4!important}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg img.motif,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg img.motif{opacity:.25}.progress{height:20px}.progress .progress-label{display:flex;flex-direction:column;justify-content:center;padding-left:.25rem}.table thead th,.markdown>table thead th{font-size:.625rem}table.object-list tbody>tr:last-child>td{border-bottom-width:0}table.object-list th.asc>a:after{content:"\f0140";font-family:Material Design Icons}table.object-list th.desc>a:after{content:"\f0143";font-family:Material Design Icons}table.attr-table th{font-weight:400;width:min-content}table.attr-table th,table.attr-table td{border-bottom-style:dashed}table.attr-table tr:last-child{border-bottom-style:hidden}table.attr-table td{overflow-wrap:anywhere}td pre{margin-bottom:0}table th.orderable a{color:var(--tblr-body-color)}html[data-bs-theme=dark] .table thead th,body[data-bs-theme=dark] html[data-bs-theme=light] .table thead th,body[data-bs-theme=dark] html[data-bs-theme=light] .markdown>table thead th,html[data-bs-theme=dark] .markdown>table thead th{background:#001423!important}.page-tabs{border-bottom:1px solid var(--tblr-border-color-translucent)}.page-tabs .nav-tabs{position:relative;border:none}.page-tabs .nav-tabs .nav-link.active,.page-tabs .nav-tabs .nav-link:active,.page-tabs .nav-tabs .nav-link:hover{border-color:var(--tblr-border-color-translucent);border-bottom-color:transparent}.page-tabs .nav-tabs .nav-link.active{color:inherit;background:var(--tblr-bg-surface-secondary);border-bottom-color:transparent}pre.change-data{border-radius:0;padding:0;margin-inline:-.75rem}pre.change-data>span{display:block;padding-inline:.5rem;max-width:100%;min-width:fit-content;border-left:.25rem solid transparent}pre.change-data>span.added{background-color:var(--tblr-green-200);border-left-color:var(--tblr-green-darken)}pre.change-data>span.removed{background-color:var(--tblr-red-200);border-left-color:var(--tblr-red-darken)}pre.change-diff{border:var(--tblr-border-width) solid transparent}pre.change-diff.change-added{background-color:var(--tblr-green-lt);border-color:var(--tblr-green)}pre.change-diff.change-removed{background-color:var(--tblr-red-lt);border-color:var(--tblr-red)}pre.block{padding:1rem;border:var(--tblr-border-width) solid #e5e7eb;border-radius:6px}.grid-stack .card-header.bg-default{background:var(--tblr-bg-surface-secondary)!important}.grid-stack .card-header a{color:inherit!important}tr[data-cable-status=connected]{background-color:#2fb34426}tr[data-cable-status=planned]{background-color:#066fd126}tr[data-cable-status=decommissioning]{background-color:#f59f0026}tr[data-mark-connected=true]{background-color:#2fb34426}tr[data-virtual=true]{background-color:#00857d26}tr[data-enabled=disabled]{background-color:#9ca3af26}tr[data-cable-status=connected] button.mark-installed{display:none}tr:not([data-cable-status=connected]) button.mark-planned{display:none}.rendered-markdown table{width:100%}.rendered-markdown table th{border-bottom:2px solid #dddddd;padding:8px}.rendered-markdown table td{border-top:1px solid #dddddd;padding:8px}.rendered-markdown table th[align=left]{text-align:left}.rendered-markdown table th[align=center]{text-align:center}.rendered-markdown table th[align=right]{text-align:right}.rendered-markdown p:last-child{margin-bottom:0}td>.rendered-markdown{max-height:200px;overflow-y:scroll}.markdown-widget .preview{border:1px solid #e5e7eb;border-radius:6px;min-height:200px}span.color-label{display:inline-block;width:5rem;height:1rem;padding:.25em .5em;border:1px solid #303030;border-radius:6px}.record-depth{display:inline;user-select:none;opacity:33%}.record-depth span:only-of-type,.record-depth span:last-of-type{margin-right:.25rem}.hide-last-child :last-child{visibility:hidden;opacity:0}.netbox-edition{letter-spacing:.15rem}.btn-float-group,.btn-float-group-right,.btn-float-group-left{position:sticky;bottom:10px;z-index:4}.btn-float-group-left{float:left}.btn-float-group-right{float:right}.btn-float{--tblr-btn-bg: var(--tblr-bg-surface-tertiary) !important}.logo{height:80px}.sso-icon{height:24px}img.plugin-icon{max-width:1.4285em;height:auto}.thumbnail{max-width:200px}.thumbnail img{border:1px solid #606060}.image-preview-popover{--bs-popover-max-width: clamp(240px, 25vw, 640px)}.image-preview-popover .popover-header{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.image-preview-popover .popover-body{display:flex;justify-content:center;align-items:center}.image-preview-popover img{display:block;max-width:100%;max-height:clamp(160px,33vh,640px);height:auto}html[data-bs-theme=dark] img.plugin-icon,body[data-bs-theme=dark] html[data-bs-theme=light] img.plugin-icon{filter:grayscale(100%) invert(100%) brightness(80%)}tr[data-read=True] td{background-color:var(--tblr-bg-surface-secondary);color:#6b7280}.rack-loading-container{min-height:200px;margin-left:30px} +@charset "UTF-8";:root,[data-bs-theme=light]{--tblr-black: #000000;--tblr-white: #ffffff;--tblr-gray: #4b5563;--tblr-gray-dark: #1f2937;--tblr-gray-100: #f3f4f6;--tblr-gray-200: #e5e7eb;--tblr-gray-300: #d1d5db;--tblr-gray-400: #9ca3af;--tblr-gray-500: #6b7280;--tblr-gray-600: #4b5563;--tblr-gray-700: #374151;--tblr-gray-800: #1f2937;--tblr-gray-900: #111827;--tblr-primary: #00857D;--tblr-secondary: #6b7280;--tblr-success: #2fb344;--tblr-info: #4299e1;--tblr-warning: #f59f00;--tblr-danger: #d63939;--tblr-light: #f9fafb;--tblr-dark: #1f2937;--tblr-muted: #6b7280;--tblr-blue: #066fd1;--tblr-azure: #4299e1;--tblr-indigo: #4263eb;--tblr-purple: #ae3ec9;--tblr-pink: #d6336c;--tblr-red: #d63939;--tblr-orange: #f76707;--tblr-yellow: #f59f00;--tblr-lime: #74b816;--tblr-green: #2fb344;--tblr-teal: #0ca678;--tblr-cyan: #17a2b8;--tblr-primary-rgb: 0, 133, 125;--tblr-secondary-rgb: 107, 114, 128;--tblr-success-rgb: 47, 179, 68;--tblr-info-rgb: 66, 153, 225;--tblr-warning-rgb: 245, 159, 0;--tblr-danger-rgb: 214, 57, 57;--tblr-light-rgb: 249, 250, 251;--tblr-dark-rgb: 31, 41, 55;--tblr-muted-rgb: 107, 114, 128;--tblr-blue-rgb: 6, 111, 209;--tblr-azure-rgb: 66, 153, 225;--tblr-indigo-rgb: 66, 99, 235;--tblr-purple-rgb: 174, 62, 201;--tblr-pink-rgb: 214, 51, 108;--tblr-red-rgb: 214, 57, 57;--tblr-orange-rgb: 247, 103, 7;--tblr-yellow-rgb: 245, 159, 0;--tblr-lime-rgb: 116, 184, 22;--tblr-green-rgb: 47, 179, 68;--tblr-teal-rgb: 12, 166, 120;--tblr-cyan-rgb: 23, 162, 184;--tblr-primary-text-emphasis: rgb(0, 53.2, 50);--tblr-secondary-text-emphasis: rgb(42.8, 45.6, 51.2);--tblr-success-text-emphasis: rgb(18.8, 71.6, 27.2);--tblr-info-text-emphasis: rgb(26.4, 61.2, 90);--tblr-warning-text-emphasis: rgb(98, 63.6, 0);--tblr-danger-text-emphasis: rgb(85.6, 22.8, 22.8);--tblr-light-text-emphasis: #374151;--tblr-dark-text-emphasis: #374151;--tblr-primary-bg-subtle: rgb(204, 230.6, 229);--tblr-secondary-bg-subtle: rgb(225.4, 226.8, 229.6);--tblr-success-bg-subtle: rgb(213.4, 239.8, 217.6);--tblr-info-bg-subtle: rgb(217.2, 234.6, 249);--tblr-warning-bg-subtle: rgb(253, 235.8, 204);--tblr-danger-bg-subtle: rgb(246.8, 215.4, 215.4);--tblr-light-bg-subtle: rgb(249, 249.5, 250.5);--tblr-dark-bg-subtle: #9ca3af;--tblr-primary-border-subtle: rgb(153, 206.2, 203);--tblr-secondary-border-subtle: rgb(195.8, 198.6, 204.2);--tblr-success-border-subtle: rgb(171.8, 224.6, 180.2);--tblr-info-border-subtle: rgb(179.4, 214.2, 243);--tblr-warning-border-subtle: rgb(251, 216.6, 153);--tblr-danger-border-subtle: rgb(238.6, 175.8, 175.8);--tblr-light-border-subtle: #e5e7eb;--tblr-dark-border-subtle: #6b7280;--tblr-white-rgb: 255, 255, 255;--tblr-black-rgb: 0, 0, 0;--tblr-font-sans-serif: "Inter", system-ui, sans-serif;--tblr-font-monospace: "Roboto Mono";--tblr-gradient: linear-gradient(180deg, rgba(255, 255, 255, .15), rgba(255, 255, 255, 0));--tblr-body-font-family: var(--tblr-font-sans-serif);--tblr-body-font-size: .875rem;--tblr-body-font-weight: 400;--tblr-body-line-height: 1.4285714286;--tblr-body-color: #1f2937;--tblr-body-color-rgb: 31, 41, 55;--tblr-body-bg: #f9fafb;--tblr-body-bg-rgb: 249, 250, 251;--tblr-emphasis-color: #374151;--tblr-emphasis-color-rgb: 55, 65, 81;--tblr-secondary-color: rgba(31, 41, 55, .75);--tblr-secondary-color-rgb: 31, 41, 55;--tblr-secondary-bg: #e5e7eb;--tblr-secondary-bg-rgb: 229, 231, 235;--tblr-tertiary-color: rgba(31, 41, 55, .5);--tblr-tertiary-color-rgb: 31, 41, 55;--tblr-tertiary-bg: #f3f4f6;--tblr-tertiary-bg-rgb: 243, 244, 246;--tblr-heading-color: inherit;--tblr-link-color: #00857D;--tblr-link-color-rgb: 0, 133, 125;--tblr-link-decoration: none;--tblr-link-hover-color: rgb(0, 106.4, 100);--tblr-link-hover-color-rgb: 0, 106, 100;--tblr-link-hover-decoration: underline;--tblr-code-color: light-dark(var(--tblr-gray-600), var(--tblr-gray-400));--tblr-highlight-color: #1f2937;--tblr-highlight-bg: rgb(253, 235.8, 204);--tblr-border-width: 1px;--tblr-border-style: solid;--tblr-border-color: #e5e7eb;--tblr-border-color-translucent: rgba(4, 32, 69, .1);--tblr-border-radius: 6px;--tblr-border-radius-sm: 4px;--tblr-border-radius-lg: 8px;--tblr-border-radius-xl: 1rem;--tblr-border-radius-xxl: 2rem;--tblr-border-radius-2xl: var(--tblr-border-radius-xxl);--tblr-border-radius-pill: 100rem;--tblr-box-shadow: rgba(var(--tblr-body-color-rgb), .04) 0 2px 4px 0;--tblr-box-shadow-sm: 0 .125rem .25rem rgba(0, 0, 0, .075);--tblr-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, .175);--tblr-box-shadow-inset: 0 0 transparent;--tblr-focus-ring-width: .25rem;--tblr-focus-ring-opacity: .25;--tblr-focus-ring-color: rgba(var(--tblr-primary-rgb), .25);--tblr-form-valid-color: #2fb344;--tblr-form-valid-border-color: #2fb344;--tblr-form-invalid-color: #d63939;--tblr-form-invalid-border-color: #d63939}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{color-scheme:dark;--tblr-body-color: #e5e7eb;--tblr-body-color-rgb: 229, 231, 235;--tblr-body-bg: #111827;--tblr-body-bg-rgb: 17, 24, 39;--tblr-emphasis-color: #ffffff;--tblr-emphasis-color-rgb: 255, 255, 255;--tblr-secondary-color: rgba(229, 231, 235, .75);--tblr-secondary-color-rgb: 229, 231, 235;--tblr-secondary-bg: #1f2937;--tblr-secondary-bg-rgb: 31, 41, 55;--tblr-tertiary-color: rgba(229, 231, 235, .5);--tblr-tertiary-color-rgb: 229, 231, 235;--tblr-tertiary-bg: rgb(24, 32.5, 47);--tblr-tertiary-bg-rgb: 24, 33, 47;--tblr-primary-text-emphasis: rgb(102, 181.8, 177);--tblr-secondary-text-emphasis: rgb(166.2, 170.4, 178.8);--tblr-success-text-emphasis: rgb(130.2, 209.4, 142.8);--tblr-info-text-emphasis: rgb(141.6, 193.8, 237);--tblr-warning-text-emphasis: rgb(249, 197.4, 102);--tblr-danger-text-emphasis: rgb(230.4, 136.2, 136.2);--tblr-light-text-emphasis: #f3f4f6;--tblr-dark-text-emphasis: #d1d5db;--tblr-primary-bg-subtle: rgb(0, 26.6, 25);--tblr-secondary-bg-subtle: rgb(21.4, 22.8, 25.6);--tblr-success-bg-subtle: rgb(9.4, 35.8, 13.6);--tblr-info-bg-subtle: rgb(13.2, 30.6, 45);--tblr-warning-bg-subtle: rgb(49, 31.8, 0);--tblr-danger-bg-subtle: rgb(42.8, 11.4, 11.4);--tblr-light-bg-subtle: #1f2937;--tblr-dark-bg-subtle: rgb(15.5, 20.5, 27.5);--tblr-primary-border-subtle: rgb(0, 79.8, 75);--tblr-secondary-border-subtle: rgb(64.2, 68.4, 76.8);--tblr-success-border-subtle: rgb(28.2, 107.4, 40.8);--tblr-info-border-subtle: rgb(39.6, 91.8, 135);--tblr-warning-border-subtle: rgb(147, 95.4, 0);--tblr-danger-border-subtle: rgb(128.4, 34.2, 34.2);--tblr-light-border-subtle: #374151;--tblr-dark-border-subtle: #1f2937;--tblr-heading-color: inherit;--tblr-link-color: rgb(102, 181.8, 177);--tblr-link-hover-color: rgb(132.6, 196.44, 192.6);--tblr-link-color-rgb: 102, 182, 177;--tblr-link-hover-color-rgb: 133, 196, 193;--tblr-code-color: var(--tblr-gray-300);--tblr-highlight-color: #e5e7eb;--tblr-highlight-bg: rgb(98, 63.6, 0);--tblr-border-color: rgb(45.7069767442, 60.4511627907, 81.0930232558);--tblr-border-color-translucent: rgba(72, 110, 149, .14);--tblr-form-valid-color: rgb(130.2, 209.4, 142.8);--tblr-form-valid-border-color: rgb(130.2, 209.4, 142.8);--tblr-form-invalid-color: rgb(230.4, 136.2, 136.2);--tblr-form-invalid-border-color: rgb(230.4, 136.2, 136.2)}*,*:before,*:after{box-sizing:border-box}@media(prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--tblr-body-font-family);font-size:var(--tblr-body-font-size);font-weight:var(--tblr-body-font-weight);line-height:var(--tblr-body-line-height);color:var(--tblr-body-color);text-align:var(--tblr-body-text-align);background-color:var(--tblr-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}hr,.hr{margin:2rem 0;color:inherit;border:0;border-top:var(--tblr-border-width) solid;opacity:.16}h6,.h6,h5,.h5,h4,.h4,h3,.field-group h2,.field-group .h2,.h3,h2,.h2,h1,.h1{margin-top:0;margin-bottom:var(--tblr-spacer);font-weight:var(--tblr-font-weight-bold);line-height:1.2;color:var(--tblr-heading-color)}h1,.h1{font-size:1.5rem}h2,.h2{font-size:1.25rem}h3,.field-group h2,.field-group .h2,.h3{font-size:1rem}h4,.h4{font-size:.875rem}h5,.h5{font-size:.75rem}h6,.h6{font-size:.625rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{text-decoration:underline dotted;cursor:help;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:600}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small,.small{font-size:.875em}mark,.mark{padding:.1875em;color:var(--tblr-highlight-color);background-color:var(--tblr-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--tblr-link-color-rgb),var(--tblr-link-opacity, 1));text-decoration:none}a:hover{--tblr-link-color-rgb: var(--tblr-link-hover-color-rgb);text-decoration:underline}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}pre,code,kbd,samp{font-family:var(--tblr-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.85714285em;color:var(--tblr-light)}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.85714285em;color:var(--tblr-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.25rem .5rem;font-size:var(--tblr-font-size-h5);color:var(--tblr-text-secondary-dark);background-color:var(--tblr-code-bg);border-radius:4px}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--tblr-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}thead,tbody,tfoot,tr,td,th{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button:not(:disabled),[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;line-height:inherit;font-size:1.5rem}legend+*{clear:left}::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-text,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button{cursor:pointer;filter:grayscale(1)}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:.875rem;font-weight:var(--tblr-font-weight-normal)}.display-1{font-weight:300;line-height:1.2;font-size:5rem}.display-2{font-weight:300;line-height:1.2;font-size:4.5rem}.display-3{font-weight:300;line-height:1.2;font-size:4rem}.display-4{font-weight:300;line-height:1.2;font-size:3.5rem}.display-5{font-weight:300;line-height:1.2;font-size:3rem}.display-6{font-weight:300;line-height:1.2;font-size:2rem}.list-unstyled,.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:.875rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#4b5563}.blockquote-footer:before{content:"\2014\a0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--tblr-body-bg);border:var(--tblr-border-width) solid var(--tblr-border-color);border-radius:var(--tblr-border-radius);box-shadow:var(--tblr-box-shadow-sm);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--tblr-secondary-color)}.container,.container-fluid,.container-xxl,.container-xl,.container-lg,.container-md,.container-sm{--tblr-gutter-x: calc(var(--tblr-page-padding) * 2);--tblr-gutter-y: 0;width:100%;padding-right:calc(var(--tblr-gutter-x) * .5);padding-left:calc(var(--tblr-gutter-x) * .5);margin-right:auto;margin-left:auto}@media(min-width:576px){.container-sm,.container{max-width:540px}}@media(min-width:768px){.container-md,.container-sm,.container{max-width:720px}}@media(min-width:992px){.container-lg,.container-md,.container-sm,.container{max-width:960px}}@media(min-width:1200px){.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1140px}}@media(min-width:1400px){.container-xxl,.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1320px}}:root{--tblr-breakpoint-xs: 0;--tblr-breakpoint-sm: 576px;--tblr-breakpoint-md: 768px;--tblr-breakpoint-lg: 992px;--tblr-breakpoint-xl: 1200px;--tblr-breakpoint-xxl: 1400px}.row{--tblr-gutter-x: var(--tblr-page-padding);--tblr-gutter-y: 0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--tblr-gutter-y));margin-right:calc(-.5 * var(--tblr-gutter-x));margin-left:calc(-.5 * var(--tblr-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--tblr-gutter-x) * .5);padding-left:calc(var(--tblr-gutter-x) * .5);margin-top:var(--tblr-gutter-y)}.grid{display:grid;grid-template-rows:repeat(var(--tblr-rows, 1),1fr);grid-template-columns:repeat(var(--tblr-columns, 12),1fr);gap:var(--tblr-gap, var(--tblr-page-padding))}.grid .g-col-1{grid-column:auto/span 1}.grid .g-col-2{grid-column:auto/span 2}.grid .g-col-3{grid-column:auto/span 3}.grid .g-col-4{grid-column:auto/span 4}.grid .g-col-5{grid-column:auto/span 5}.grid .g-col-6{grid-column:auto/span 6}.grid .g-col-7{grid-column:auto/span 7}.grid .g-col-8{grid-column:auto/span 8}.grid .g-col-9{grid-column:auto/span 9}.grid .g-col-10{grid-column:auto/span 10}.grid .g-col-11{grid-column:auto/span 11}.grid .g-col-12{grid-column:auto/span 12}.grid .g-start-1{grid-column-start:1}.grid .g-start-2{grid-column-start:2}.grid .g-start-3{grid-column-start:3}.grid .g-start-4{grid-column-start:4}.grid .g-start-5{grid-column-start:5}.grid .g-start-6{grid-column-start:6}.grid .g-start-7{grid-column-start:7}.grid .g-start-8{grid-column-start:8}.grid .g-start-9{grid-column-start:9}.grid .g-start-10{grid-column-start:10}.grid .g-start-11{grid-column-start:11}@media(min-width:576px){.grid .g-col-sm-1{grid-column:auto/span 1}.grid .g-col-sm-2{grid-column:auto/span 2}.grid .g-col-sm-3{grid-column:auto/span 3}.grid .g-col-sm-4{grid-column:auto/span 4}.grid .g-col-sm-5{grid-column:auto/span 5}.grid .g-col-sm-6{grid-column:auto/span 6}.grid .g-col-sm-7{grid-column:auto/span 7}.grid .g-col-sm-8{grid-column:auto/span 8}.grid .g-col-sm-9{grid-column:auto/span 9}.grid .g-col-sm-10{grid-column:auto/span 10}.grid .g-col-sm-11{grid-column:auto/span 11}.grid .g-col-sm-12{grid-column:auto/span 12}.grid .g-start-sm-1{grid-column-start:1}.grid .g-start-sm-2{grid-column-start:2}.grid .g-start-sm-3{grid-column-start:3}.grid .g-start-sm-4{grid-column-start:4}.grid .g-start-sm-5{grid-column-start:5}.grid .g-start-sm-6{grid-column-start:6}.grid .g-start-sm-7{grid-column-start:7}.grid .g-start-sm-8{grid-column-start:8}.grid .g-start-sm-9{grid-column-start:9}.grid .g-start-sm-10{grid-column-start:10}.grid .g-start-sm-11{grid-column-start:11}}@media(min-width:768px){.grid .g-col-md-1{grid-column:auto/span 1}.grid .g-col-md-2{grid-column:auto/span 2}.grid .g-col-md-3{grid-column:auto/span 3}.grid .g-col-md-4{grid-column:auto/span 4}.grid .g-col-md-5{grid-column:auto/span 5}.grid .g-col-md-6{grid-column:auto/span 6}.grid .g-col-md-7{grid-column:auto/span 7}.grid .g-col-md-8{grid-column:auto/span 8}.grid .g-col-md-9{grid-column:auto/span 9}.grid .g-col-md-10{grid-column:auto/span 10}.grid .g-col-md-11{grid-column:auto/span 11}.grid .g-col-md-12{grid-column:auto/span 12}.grid .g-start-md-1{grid-column-start:1}.grid .g-start-md-2{grid-column-start:2}.grid .g-start-md-3{grid-column-start:3}.grid .g-start-md-4{grid-column-start:4}.grid .g-start-md-5{grid-column-start:5}.grid .g-start-md-6{grid-column-start:6}.grid .g-start-md-7{grid-column-start:7}.grid .g-start-md-8{grid-column-start:8}.grid .g-start-md-9{grid-column-start:9}.grid .g-start-md-10{grid-column-start:10}.grid .g-start-md-11{grid-column-start:11}}@media(min-width:992px){.grid .g-col-lg-1{grid-column:auto/span 1}.grid .g-col-lg-2{grid-column:auto/span 2}.grid .g-col-lg-3{grid-column:auto/span 3}.grid .g-col-lg-4{grid-column:auto/span 4}.grid .g-col-lg-5{grid-column:auto/span 5}.grid .g-col-lg-6{grid-column:auto/span 6}.grid .g-col-lg-7{grid-column:auto/span 7}.grid .g-col-lg-8{grid-column:auto/span 8}.grid .g-col-lg-9{grid-column:auto/span 9}.grid .g-col-lg-10{grid-column:auto/span 10}.grid .g-col-lg-11{grid-column:auto/span 11}.grid .g-col-lg-12{grid-column:auto/span 12}.grid .g-start-lg-1{grid-column-start:1}.grid .g-start-lg-2{grid-column-start:2}.grid .g-start-lg-3{grid-column-start:3}.grid .g-start-lg-4{grid-column-start:4}.grid .g-start-lg-5{grid-column-start:5}.grid .g-start-lg-6{grid-column-start:6}.grid .g-start-lg-7{grid-column-start:7}.grid .g-start-lg-8{grid-column-start:8}.grid .g-start-lg-9{grid-column-start:9}.grid .g-start-lg-10{grid-column-start:10}.grid .g-start-lg-11{grid-column-start:11}}@media(min-width:1200px){.grid .g-col-xl-1{grid-column:auto/span 1}.grid .g-col-xl-2{grid-column:auto/span 2}.grid .g-col-xl-3{grid-column:auto/span 3}.grid .g-col-xl-4{grid-column:auto/span 4}.grid .g-col-xl-5{grid-column:auto/span 5}.grid .g-col-xl-6{grid-column:auto/span 6}.grid .g-col-xl-7{grid-column:auto/span 7}.grid .g-col-xl-8{grid-column:auto/span 8}.grid .g-col-xl-9{grid-column:auto/span 9}.grid .g-col-xl-10{grid-column:auto/span 10}.grid .g-col-xl-11{grid-column:auto/span 11}.grid .g-col-xl-12{grid-column:auto/span 12}.grid .g-start-xl-1{grid-column-start:1}.grid .g-start-xl-2{grid-column-start:2}.grid .g-start-xl-3{grid-column-start:3}.grid .g-start-xl-4{grid-column-start:4}.grid .g-start-xl-5{grid-column-start:5}.grid .g-start-xl-6{grid-column-start:6}.grid .g-start-xl-7{grid-column-start:7}.grid .g-start-xl-8{grid-column-start:8}.grid .g-start-xl-9{grid-column-start:9}.grid .g-start-xl-10{grid-column-start:10}.grid .g-start-xl-11{grid-column-start:11}}@media(min-width:1400px){.grid .g-col-xxl-1{grid-column:auto/span 1}.grid .g-col-xxl-2{grid-column:auto/span 2}.grid .g-col-xxl-3{grid-column:auto/span 3}.grid .g-col-xxl-4{grid-column:auto/span 4}.grid .g-col-xxl-5{grid-column:auto/span 5}.grid .g-col-xxl-6{grid-column:auto/span 6}.grid .g-col-xxl-7{grid-column:auto/span 7}.grid .g-col-xxl-8{grid-column:auto/span 8}.grid .g-col-xxl-9{grid-column:auto/span 9}.grid .g-col-xxl-10{grid-column:auto/span 10}.grid .g-col-xxl-11{grid-column:auto/span 11}.grid .g-col-xxl-12{grid-column:auto/span 12}.grid .g-start-xxl-1{grid-column-start:1}.grid .g-start-xxl-2{grid-column-start:2}.grid .g-start-xxl-3{grid-column-start:3}.grid .g-start-xxl-4{grid-column-start:4}.grid .g-start-xxl-5{grid-column-start:5}.grid .g-start-xxl-6{grid-column-start:6}.grid .g-start-xxl-7{grid-column-start:7}.grid .g-start-xxl-8{grid-column-start:8}.grid .g-start-xxl-9{grid-column-start:9}.grid .g-start-xxl-10{grid-column-start:10}.grid .g-start-xxl-11{grid-column-start:11}}.col{flex:1 0 0}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--tblr-gutter-x: 0}.g-0,.gy-0{--tblr-gutter-y: 0}.g-1,.gx-1{--tblr-gutter-x: .25rem}.g-1,.gy-1{--tblr-gutter-y: .25rem}.g-2,.gx-2{--tblr-gutter-x: .5rem}.g-2,.gy-2{--tblr-gutter-y: .5rem}.g-3,.gx-3{--tblr-gutter-x: 1rem}.g-3,.gy-3{--tblr-gutter-y: 1rem}.g-4,.gx-4{--tblr-gutter-x: 1.5rem}.g-4,.gy-4{--tblr-gutter-y: 1.5rem}.g-5,.gx-5{--tblr-gutter-x: 2rem}.g-5,.gy-5{--tblr-gutter-y: 2rem}.g-6,.gx-6{--tblr-gutter-x: 2.5rem}.g-6,.gy-6{--tblr-gutter-y: 2.5rem}@media(min-width:576px){.col-sm{flex:1 0 0}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--tblr-gutter-x: 0}.g-sm-0,.gy-sm-0{--tblr-gutter-y: 0}.g-sm-1,.gx-sm-1{--tblr-gutter-x: .25rem}.g-sm-1,.gy-sm-1{--tblr-gutter-y: .25rem}.g-sm-2,.gx-sm-2{--tblr-gutter-x: .5rem}.g-sm-2,.gy-sm-2{--tblr-gutter-y: .5rem}.g-sm-3,.gx-sm-3{--tblr-gutter-x: 1rem}.g-sm-3,.gy-sm-3{--tblr-gutter-y: 1rem}.g-sm-4,.gx-sm-4{--tblr-gutter-x: 1.5rem}.g-sm-4,.gy-sm-4{--tblr-gutter-y: 1.5rem}.g-sm-5,.gx-sm-5{--tblr-gutter-x: 2rem}.g-sm-5,.gy-sm-5{--tblr-gutter-y: 2rem}.g-sm-6,.gx-sm-6{--tblr-gutter-x: 2.5rem}.g-sm-6,.gy-sm-6{--tblr-gutter-y: 2.5rem}}@media(min-width:768px){.col-md{flex:1 0 0}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--tblr-gutter-x: 0}.g-md-0,.gy-md-0{--tblr-gutter-y: 0}.g-md-1,.gx-md-1{--tblr-gutter-x: .25rem}.g-md-1,.gy-md-1{--tblr-gutter-y: .25rem}.g-md-2,.gx-md-2{--tblr-gutter-x: .5rem}.g-md-2,.gy-md-2{--tblr-gutter-y: .5rem}.g-md-3,.gx-md-3{--tblr-gutter-x: 1rem}.g-md-3,.gy-md-3{--tblr-gutter-y: 1rem}.g-md-4,.gx-md-4{--tblr-gutter-x: 1.5rem}.g-md-4,.gy-md-4{--tblr-gutter-y: 1.5rem}.g-md-5,.gx-md-5{--tblr-gutter-x: 2rem}.g-md-5,.gy-md-5{--tblr-gutter-y: 2rem}.g-md-6,.gx-md-6{--tblr-gutter-x: 2.5rem}.g-md-6,.gy-md-6{--tblr-gutter-y: 2.5rem}}@media(min-width:992px){.col-lg{flex:1 0 0}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--tblr-gutter-x: 0}.g-lg-0,.gy-lg-0{--tblr-gutter-y: 0}.g-lg-1,.gx-lg-1{--tblr-gutter-x: .25rem}.g-lg-1,.gy-lg-1{--tblr-gutter-y: .25rem}.g-lg-2,.gx-lg-2{--tblr-gutter-x: .5rem}.g-lg-2,.gy-lg-2{--tblr-gutter-y: .5rem}.g-lg-3,.gx-lg-3{--tblr-gutter-x: 1rem}.g-lg-3,.gy-lg-3{--tblr-gutter-y: 1rem}.g-lg-4,.gx-lg-4{--tblr-gutter-x: 1.5rem}.g-lg-4,.gy-lg-4{--tblr-gutter-y: 1.5rem}.g-lg-5,.gx-lg-5{--tblr-gutter-x: 2rem}.g-lg-5,.gy-lg-5{--tblr-gutter-y: 2rem}.g-lg-6,.gx-lg-6{--tblr-gutter-x: 2.5rem}.g-lg-6,.gy-lg-6{--tblr-gutter-y: 2.5rem}}@media(min-width:1200px){.col-xl{flex:1 0 0}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--tblr-gutter-x: 0}.g-xl-0,.gy-xl-0{--tblr-gutter-y: 0}.g-xl-1,.gx-xl-1{--tblr-gutter-x: .25rem}.g-xl-1,.gy-xl-1{--tblr-gutter-y: .25rem}.g-xl-2,.gx-xl-2{--tblr-gutter-x: .5rem}.g-xl-2,.gy-xl-2{--tblr-gutter-y: .5rem}.g-xl-3,.gx-xl-3{--tblr-gutter-x: 1rem}.g-xl-3,.gy-xl-3{--tblr-gutter-y: 1rem}.g-xl-4,.gx-xl-4{--tblr-gutter-x: 1.5rem}.g-xl-4,.gy-xl-4{--tblr-gutter-y: 1.5rem}.g-xl-5,.gx-xl-5{--tblr-gutter-x: 2rem}.g-xl-5,.gy-xl-5{--tblr-gutter-y: 2rem}.g-xl-6,.gx-xl-6{--tblr-gutter-x: 2.5rem}.g-xl-6,.gy-xl-6{--tblr-gutter-y: 2.5rem}}@media(min-width:1400px){.col-xxl{flex:1 0 0}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--tblr-gutter-x: 0}.g-xxl-0,.gy-xxl-0{--tblr-gutter-y: 0}.g-xxl-1,.gx-xxl-1{--tblr-gutter-x: .25rem}.g-xxl-1,.gy-xxl-1{--tblr-gutter-y: .25rem}.g-xxl-2,.gx-xxl-2{--tblr-gutter-x: .5rem}.g-xxl-2,.gy-xxl-2{--tblr-gutter-y: .5rem}.g-xxl-3,.gx-xxl-3{--tblr-gutter-x: 1rem}.g-xxl-3,.gy-xxl-3{--tblr-gutter-y: 1rem}.g-xxl-4,.gx-xxl-4{--tblr-gutter-x: 1.5rem}.g-xxl-4,.gy-xxl-4{--tblr-gutter-y: 1.5rem}.g-xxl-5,.gx-xxl-5{--tblr-gutter-x: 2rem}.g-xxl-5,.gy-xxl-5{--tblr-gutter-y: 2rem}.g-xxl-6,.gx-xxl-6{--tblr-gutter-x: 2.5rem}.g-xxl-6,.gy-xxl-6{--tblr-gutter-y: 2.5rem}}.table,.markdown>table{--tblr-table-color-type: initial;--tblr-table-bg-type: initial;--tblr-table-color-state: initial;--tblr-table-bg-state: initial;--tblr-table-color: inherit;--tblr-table-bg: transparent;--tblr-table-border-color: var(--tblr-border-color-translucent);--tblr-table-accent-bg: transparent;--tblr-table-striped-color: inherit;--tblr-table-striped-bg: var(--tblr-bg-surface-tertiary);--tblr-table-active-color: inherit;--tblr-table-active-bg: var(--tblr-active-bg);--tblr-table-hover-color: inherit;--tblr-table-hover-bg: rgba(var(--tblr-emphasis-color-rgb), .075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--tblr-table-border-color)}.table>:not(caption)>*>*,.markdown>table>:not(caption)>*>*{padding:.5rem;color:var(--tblr-table-color-state, var(--tblr-table-color-type, var(--tblr-table-color)));background-color:var(--tblr-table-bg);border-bottom-width:var(--tblr-border-width);box-shadow:inset 0 0 0 9999px var(--tblr-table-bg-state, var(--tblr-table-bg-type, var(--tblr-table-accent-bg)))}.table>tbody,.markdown>table>tbody{vertical-align:inherit}.table>thead,.markdown>table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--tblr-border-width) * 2) solid var(--tblr-border-color-translucent)}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*,.markdown>table>:not(caption)>*>*{padding:.25rem}.table-bordered>:not(caption)>*,.markdown>table>:not(caption)>*{border-width:var(--tblr-border-width) 0}.table-bordered>:not(caption)>*>*,.markdown>table>:not(caption)>*>*{border-width:0 var(--tblr-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(2n)>*{--tblr-table-color-type: var(--tblr-table-striped-color);--tblr-table-bg-type: var(--tblr-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--tblr-table-color-type: var(--tblr-table-striped-color);--tblr-table-bg-type: var(--tblr-table-striped-bg)}.table-active{--tblr-table-color-state: var(--tblr-table-active-color);--tblr-table-bg-state: var(--tblr-table-active-bg)}.table-hover>tbody>tr:hover>*{--tblr-table-color-state: var(--tblr-table-hover-color);--tblr-table-bg-state: var(--tblr-table-hover-bg)}.table-primary{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(204, 230.6, 229);--tblr-table-border-color: rgb(169.4, 192.68, 194.2);--tblr-table-striped-bg: rgb(195.35, 221.12, 220.3);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(186.7, 211.64, 211.6);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(191.025, 216.38, 215.95);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-secondary{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(225.4, 226.8, 229.6);--tblr-table-border-color: rgb(186.52, 189.64, 194.68);--tblr-table-striped-bg: rgb(215.68, 217.51, 220.87);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(205.96, 208.22, 212.14);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(210.82, 212.865, 216.505);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-success{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(213.4, 239.8, 217.6);--tblr-table-border-color: rgb(176.92, 200.04, 185.08);--tblr-table-striped-bg: rgb(204.28, 229.86, 209.47);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(195.16, 219.92, 201.34);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(199.72, 224.89, 205.405);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-info{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(217.2, 234.6, 249);--tblr-table-border-color: rgb(179.96, 195.88, 210.2);--tblr-table-striped-bg: rgb(207.89, 224.92, 239.3);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(198.58, 215.24, 229.6);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(203.235, 220.08, 234.45);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-warning{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(253, 235.8, 204);--tblr-table-border-color: rgb(208.6, 196.84, 174.2);--tblr-table-striped-bg: rgb(241.9, 226.06, 196.55);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(230.8, 216.32, 189.1);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(236.35, 221.19, 192.825);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-danger{--tblr-table-color: #1f2937;--tblr-table-bg: rgb(246.8, 215.4, 215.4);--tblr-table-border-color: rgb(203.64, 180.52, 183.32);--tblr-table-striped-bg: rgb(236.01, 206.68, 207.38);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(225.22, 197.96, 199.36);--tblr-table-active-color: #f9fafb;--tblr-table-hover-bg: rgb(230.615, 202.32, 203.37);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-light{--tblr-table-color: #1f2937;--tblr-table-bg: #f9fafb;--tblr-table-border-color: rgb(205.4, 208.2, 211.8);--tblr-table-striped-bg: rgb(238.1, 239.55, 241.2);--tblr-table-striped-color: #1f2937;--tblr-table-active-bg: rgb(227.2, 229.1, 231.4);--tblr-table-active-color: #1f2937;--tblr-table-hover-bg: rgb(232.65, 234.325, 236.3);--tblr-table-hover-color: #1f2937;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-dark{--tblr-table-color: #f9fafb;--tblr-table-bg: #1f2937;--tblr-table-border-color: rgb(74.6, 82.8, 94.2);--tblr-table-striped-bg: rgb(41.9, 51.45, 64.8);--tblr-table-striped-color: #f9fafb;--tblr-table-active-bg: rgb(52.8, 61.9, 74.6);--tblr-table-active-color: #f9fafb;--tblr-table-hover-bg: rgb(47.35, 56.675, 69.7);--tblr-table-hover-color: #f9fafb;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem;font-size:.875rem;font-weight:var(--tblr-font-weight-medium)}.col-form-label{padding-top:calc(.5625rem + var(--tblr-border-width));padding-bottom:calc(.5625rem + var(--tblr-border-width));margin-bottom:0;font-size:inherit;font-weight:var(--tblr-font-weight-medium);line-height:1.25rem}.col-form-label-lg{padding-top:calc(.6875rem + var(--tblr-border-width));padding-bottom:calc(.6875rem + var(--tblr-border-width));font-size:1rem}.col-form-label-sm{padding-top:calc(.3125rem + var(--tblr-border-width));padding-bottom:calc(.3125rem + var(--tblr-border-width));font-size:.75rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--tblr-secondary-color)}.form-control{display:block;width:100%;padding:.5625rem 1rem;font-family:var(--tblr-body-font-family);font-size:.875rem;font-weight:400;line-height:1.25rem;color:var(--tblr-body-color);appearance:none;background-color:var(--tblr-bg-forms);background-clip:padding-box;border:var(--tblr-border-width) solid var(--tblr-border-color);border-radius:var(--tblr-border-radius);box-shadow:var(--tblr-shadow-input);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--tblr-body-color);background-color:var(--tblr-bg-forms);border-color:#80c2be;outline:0;box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.25rem;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::placeholder{color:var(--tblr-tertiary);opacity:1}.form-control:disabled{background-color:var(--tblr-bg-surface-secondary);opacity:1}.form-control::file-selector-button{padding:.5625rem 1rem;margin:-.5625rem -1rem;margin-inline-end:1rem;color:var(--tblr-body-color);background-color:var(--tblr-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--tblr-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--tblr-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.5625rem 0;margin-bottom:0;line-height:1.25rem;color:var(--tblr-body-color);background-color:transparent;border:solid transparent;border-width:var(--tblr-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.25rem + .625rem + calc(var(--tblr-border-width) * 2));padding:.3125rem .5rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.form-control-sm::file-selector-button{padding:.3125rem .5rem;margin:-.3125rem -.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.25rem + 1.375rem + calc(var(--tblr-border-width) * 2));padding:.6875rem 1.5rem;font-size:1rem;border-radius:var(--tblr-border-radius-lg)}.form-control-lg::file-selector-button{padding:.6875rem 1.5rem;margin:-.6875rem -1.5rem;margin-inline-end:1.5rem}textarea.form-control{min-height:calc(1.25rem + 1.125rem + calc(var(--tblr-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.25rem + .625rem + calc(var(--tblr-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.25rem + 1.375rem + calc(var(--tblr-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.25rem + 1.125rem + calc(var(--tblr-border-width) * 2));padding:.5625rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--tblr-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--tblr-border-radius)}.form-control-color.form-control-sm{height:calc(1.25rem + .625rem + calc(var(--tblr-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.25rem + 1.375rem + calc(var(--tblr-border-width) * 2))}.form-select{--tblr-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.5625rem 3rem .5625rem 1rem;font-family:var(--tblr-body-font-family);font-size:.875rem;font-weight:400;line-height:1.25rem;color:var(--tblr-body-color);appearance:none;background-color:var(--tblr-bg-forms);background-image:var(--tblr-form-select-bg-img),var(--tblr-form-select-bg-icon, none);background-repeat:no-repeat;background-position:right 1rem center;background-size:16px 12px;border:var(--tblr-border-width) solid var(--tblr-border-color);border-radius:var(--tblr-border-radius);box-shadow:var(--tblr-shadow-input);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#80c2be;outline:0;box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:1rem;background-image:none}.form-select:disabled{background-color:var(--tblr-bg-surface-secondary)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--tblr-body-color)}.form-select-sm{padding-top:.3125rem;padding-bottom:.3125rem;padding-left:.5rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.form-select-lg{padding-top:.6875rem;padding-bottom:.6875rem;padding-left:1.5rem;font-size:1rem;border-radius:var(--tblr-border-radius-lg)}[data-bs-theme=dark] .form-select,body[data-bs-theme=dark] [data-bs-theme=light] .form-select{--tblr-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23e5e7eb' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.25rem;padding-left:2rem;margin-bottom:.75rem}.form-check .form-check-input{float:left;margin-left:-2rem}.form-check-reverse{padding-right:2rem;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-2rem;margin-left:0}.form-check-input{--tblr-form-check-bg: var(--tblr-bg-forms);flex-shrink:0;width:1.25rem;height:1.25rem;margin-top:.0892857143rem;vertical-align:top;appearance:none;background-color:var(--tblr-form-check-bg);background-image:var(--tblr-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:var(--tblr-border-radius)}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#80c2be;outline:0;box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-check-input:checked{background-color:var(--tblr-primary);border-color:var(--tblr-border-color-translucent)}.form-check-input:checked[type=checkbox]{--tblr-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--tblr-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3ccircle r='3' fill='%23ffffff' cx='8' cy='8' /%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:var(--tblr-primary);border-color:var(--tblr-primary);--tblr-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input[disabled]~.form-check-label,.form-check-input:disabled~.form-check-label{cursor:default;opacity:.7}.form-switch{padding-left:2.5rem}.form-switch .form-check-input{--tblr-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23e5e7eb'/%3e%3c/svg%3e");width:2rem;margin-left:-2.5rem;background-image:var(--tblr-form-switch-bg);background-position:left center;border-radius:2rem;transition:background-position .15s ease-in-out}.form-switch .form-check-input:focus{--tblr-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgb%28127.5, 194, 190%29'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--tblr-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23ffffff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5rem;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5rem;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check[disabled]+.btn,.btn-check:disabled+.btn{pointer-events:none;filter:none;opacity:.4}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--tblr-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.25rem;padding:0;appearance:none;background-color:transparent}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #f9fafb,0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #f9fafb,0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.375rem;appearance:none;background-color:var(--tblr-primary);border:2px var(--tblr-border-style) #ffffff;border-radius:1rem;box-shadow:0 .1rem .25rem #0000001a;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b3dad8}.form-range::-webkit-slider-runnable-track{width:100%;height:.25rem;color:transparent;cursor:pointer;background-color:var(--tblr-border-color);border-color:transparent;border-radius:1rem;box-shadow:var(--tblr-box-shadow-inset)}.form-range::-moz-range-thumb{width:1rem;height:1rem;appearance:none;background-color:var(--tblr-primary);border:2px var(--tblr-border-style) #ffffff;border-radius:1rem;box-shadow:0 .1rem .25rem #0000001a;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{transition:none}}.form-range::-moz-range-thumb:active{background-color:#b3dad8}.form-range::-moz-range-track{width:100%;height:.25rem;color:transparent;cursor:pointer;background-color:var(--tblr-border-color);border-color:transparent;border-radius:1rem;box-shadow:var(--tblr-box-shadow-inset)}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--tblr-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--tblr-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--tblr-border-width) * 2));min-height:calc(3.5rem + calc(var(--tblr-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;max-width:100%;height:100%;padding:1rem;overflow:hidden;color:rgba(var(--tblr-body-color-rgb),.65);text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--tblr-border-width) solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media(prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem}.form-floating>.form-control::placeholder,.form-floating>.form-control-plaintext::placeholder{color:transparent}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown),.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill,.form-floating>.form-control-plaintext:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem;padding-left:1rem}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-control-plaintext~label,.form-floating>.form-select~label{transform:scale(.85) translateY(-.5rem) translate(.15rem)}.form-floating>.form-control:-webkit-autofill~label{transform:scale(.85) translateY(-.5rem) translate(.15rem)}.form-floating>textarea:focus~label:after,.form-floating>textarea:not(:placeholder-shown)~label:after{position:absolute;inset:1rem .5rem;z-index:-1;height:1.5em;content:"";background-color:var(--tblr-bg-forms);border-radius:var(--tblr-border-radius)}.form-floating>textarea:disabled~label:after{background-color:var(--tblr-bg-surface-secondary)}.form-floating>.form-control-plaintext~label{border-width:var(--tblr-border-width) 0}.form-floating>:disabled~label,.form-floating>.form-control:disabled~label{color:#4b5563}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select,.input-group>.form-floating{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus,.input-group>.form-floating:focus-within{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.5625rem 1rem;font-size:.875rem;font-weight:400;line-height:1.25rem;color:var(--tblr-gray-500);text-align:center;white-space:nowrap;background-color:var(--tblr-bg-surface-secondary);border:var(--tblr-border-width) solid var(--tblr-border-color);border-radius:var(--tblr-border-radius)}.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text,.input-group-lg>.btn{padding:.6875rem 1.5rem;font-size:1rem;border-radius:var(--tblr-border-radius-lg)}.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text,.input-group-sm>.btn{padding:.3125rem .5rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:4rem}.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(-1 * var(--tblr-border-width));border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--tblr-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:var(--tblr-spacer-1) var(--tblr-spacer-3);margin-top:.1rem;font-size:.765625rem;color:#fff;background-color:var(--tblr-success);border-radius:var(--tblr-border-radius)}.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip,.is-valid~.valid-feedback,.is-valid~.valid-tooltip{display:block}.was-validated .form-control:valid,.form-control.is-valid{border-color:var(--tblr-form-valid-border-color);padding-right:2.375rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%232fb344' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='20 6 9 17 4 12'%3e%3c/polyline%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right 1.53125rem center;background-size:1.8125rem 1.8125rem}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:var(--tblr-form-valid-border-color);box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:2.375rem;background-position:top 1.53125rem right 1.53125rem}.was-validated .form-select:valid,.form-select.is-valid{border-color:var(--tblr-form-valid-border-color)}.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"],.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"]{--tblr-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%232fb344' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='20 6 9 17 4 12'%3e%3c/polyline%3e%3c/svg%3e");padding-right:5.5rem;background-position:right 1rem center,center right 3rem;background-size:16px 12px,1.8125rem 1.8125rem}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:var(--tblr-form-valid-border-color);box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-success-rgb),.25)}.was-validated .form-control-color:valid,.form-control-color.is-valid{width:5.375rem}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:var(--tblr-form-valid-border-color)}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:var(--tblr-form-valid-color)}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .25rem rgba(var(--tblr-success-rgb),.25)}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:var(--tblr-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):valid,.input-group>.form-control:not(:focus).is-valid,.was-validated .input-group>.form-select:not(:focus):valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.input-group>.form-floating:not(:focus-within).is-valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--tblr-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:var(--tblr-spacer-1) var(--tblr-spacer-3);margin-top:.1rem;font-size:.765625rem;color:#fff;background-color:var(--tblr-danger);border-radius:var(--tblr-border-radius)}.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip,.is-invalid~.invalid-feedback,select.tomselected.is-invalid+div.ts-wrapper~.invalid-feedback,.is-invalid~.invalid-tooltip,select.tomselected.is-invalid+div.ts-wrapper~.invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid,select.tomselected.is-invalid+div.form-control.ts-wrapper{border-color:var(--tblr-form-invalid-border-color);padding-right:2.375rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23d63939' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='18' y1='6' x2='6' y2='18'%3e%3c/line%3e%3cline x1='6' y1='6' x2='18' y2='18'%3e%3c/line%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right 1.53125rem center;background-size:1.8125rem 1.8125rem}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus,select.tomselected.is-invalid+div.form-control.ts-wrapper:focus{border-color:var(--tblr-form-invalid-border-color);box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:2.375rem;background-position:top 1.53125rem right 1.53125rem}.was-validated .form-select:invalid,.form-select.is-invalid,select.tomselected.is-invalid+div.form-select.ts-wrapper{border-color:var(--tblr-form-invalid-border-color)}.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"],.form-select.is-invalid:not([multiple]):not([size]),select.tomselected.is-invalid+div.form-select.ts-wrapper:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],select.tomselected.is-invalid+div.form-select.ts-wrapper:not([multiple])[size="1"]{--tblr-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23d63939' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='18' y1='6' x2='6' y2='18'%3e%3c/line%3e%3cline x1='6' y1='6' x2='18' y2='18'%3e%3c/line%3e%3c/svg%3e");padding-right:5.5rem;background-position:right 1rem center,center right 3rem;background-size:16px 12px,1.8125rem 1.8125rem}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus,select.tomselected.is-invalid+div.form-select.ts-wrapper:focus{border-color:var(--tblr-form-invalid-border-color);box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-danger-rgb),.25)}.was-validated .form-control-color:invalid,.form-control-color.is-invalid,select.tomselected.is-invalid+div.form-control-color.ts-wrapper{width:5.375rem}.was-validated .form-check-input:invalid,.form-check-input.is-invalid,select.tomselected.is-invalid+div.form-check-input.ts-wrapper{border-color:var(--tblr-form-invalid-border-color)}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked,select.tomselected.is-invalid+div.form-check-input.ts-wrapper:checked{background-color:var(--tblr-form-invalid-color)}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus,select.tomselected.is-invalid+div.form-check-input.ts-wrapper:focus{box-shadow:0 0 0 .25rem rgba(var(--tblr-danger-rgb),.25)}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label,select.tomselected.is-invalid+div.form-check-input.ts-wrapper~.form-check-label{color:var(--tblr-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):invalid,.input-group>.form-control:not(:focus).is-invalid,.input-group>select.tomselected.is-invalid+div.form-control.ts-wrapper:not(:focus),.was-validated .input-group>.form-select:not(:focus):invalid,.input-group>.form-select:not(:focus).is-invalid,.input-group>select.tomselected.is-invalid+div.form-select.ts-wrapper:not(:focus),.was-validated .input-group>.form-floating:not(:focus-within):invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>select.tomselected.is-invalid+div.form-floating.ts-wrapper:not(:focus-within){z-index:4}.btn{--tblr-btn-padding-x: .5rem;--tblr-btn-padding-y: .25rem;--tblr-btn-font-family: var(--tblr-body-font-family);--tblr-btn-font-size: .875rem;--tblr-btn-font-weight: var(--tblr-font-weight-medium);--tblr-btn-line-height: 1.25rem;--tblr-btn-color: var(--tblr-body-color);--tblr-btn-bg: transparent;--tblr-btn-border-width: var(--tblr-border-width);--tblr-btn-border-color: transparent;--tblr-btn-border-radius: var(--tblr-border-radius);--tblr-btn-hover-border-color: transparent;--tblr-btn-box-shadow: var(--tblr-shadow-input);--tblr-btn-disabled-opacity: .4;--tblr-btn-focus-box-shadow: 0 0 0 .25rem rgba(var(--tblr-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--tblr-btn-padding-y) var(--tblr-btn-padding-x);font-family:var(--tblr-btn-font-family);font-size:var(--tblr-btn-font-size);font-weight:var(--tblr-btn-font-weight);line-height:var(--tblr-btn-line-height);color:var(--tblr-btn-color);text-align:center;vertical-align:middle;cursor:pointer;user-select:none;border:var(--tblr-btn-border-width) solid var(--tblr-btn-border-color);border-radius:var(--tblr-btn-border-radius);background-color:var(--tblr-btn-bg);box-shadow:var(--tblr-btn-box-shadow);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--tblr-btn-hover-color);text-decoration:none;background-color:var(--tblr-btn-hover-bg);border-color:var(--tblr-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--tblr-btn-color);background-color:var(--tblr-btn-bg);border-color:var(--tblr-btn-border-color)}.btn:focus-visible{color:var(--tblr-btn-hover-color);background-color:var(--tblr-btn-hover-bg);border-color:var(--tblr-btn-hover-border-color);outline:0;box-shadow:var(--tblr-btn-box-shadow),var(--tblr-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--tblr-btn-hover-border-color);outline:0;box-shadow:var(--tblr-btn-box-shadow),var(--tblr-btn-focus-box-shadow)}.btn-check:checked+.btn,:not(.btn-check)+.btn:active,.btn:first-child:active,.btn.active,.btn.show{color:var(--tblr-btn-active-color);background-color:var(--tblr-btn-active-bg);border-color:var(--tblr-btn-active-border-color);box-shadow:var(--tblr-btn-active-shadow)}.btn-check:checked+.btn:focus-visible,:not(.btn-check)+.btn:active:focus-visible,.btn:first-child:active:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible{box-shadow:var(--tblr-btn-active-shadow),var(--tblr-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--tblr-btn-active-shadow),var(--tblr-btn-focus-box-shadow)}.btn:disabled,.btn.disabled,fieldset:disabled .btn{color:var(--tblr-btn-disabled-color);pointer-events:none;background-color:var(--tblr-btn-disabled-bg);border-color:var(--tblr-btn-disabled-border-color);opacity:var(--tblr-btn-disabled-opacity);box-shadow:none}.btn-link{--tblr-btn-font-weight: 400;--tblr-btn-color: var(--tblr-link-color);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-link-hover-color);--tblr-btn-hover-border-color: transparent;--tblr-btn-active-color: var(--tblr-link-hover-color);--tblr-btn-active-border-color: transparent;--tblr-btn-disabled-color: #4b5563;--tblr-btn-disabled-border-color: transparent;--tblr-btn-box-shadow: 0 0 0 #000;--tblr-btn-focus-shadow-rgb: 37, 151, 144;text-decoration:none}.btn-link:hover,.btn-link:focus-visible{text-decoration:underline}.btn-link:focus-visible{color:var(--tblr-btn-color)}.btn-link:hover{color:var(--tblr-btn-hover-color)}.btn-lg,.btn-group-lg>.btn{--tblr-btn-padding-y: .6875rem;--tblr-btn-padding-x: 1.5rem;--tblr-btn-font-size: 1rem;--tblr-btn-border-radius: var(--tblr-border-radius-lg)}.btn-sm,.btn-group-sm>.btn{--tblr-btn-padding-y: .3125rem;--tblr-btn-padding-x: .5rem;--tblr-btn-font-size: .75rem;--tblr-btn-border-radius: var(--tblr-border-radius-sm)}.fade{transition:opacity .15s linear}@media(prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media(prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media(prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropup,.dropend,.dropdown,.dropstart,.dropup-center,.dropdown-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle:after{content:"";display:inline-block;vertical-align:.306em;width:.36em;height:.36em;border-bottom:1px var(--tblr-border-style);border-left:1px var(--tblr-border-style);margin-right:.1em;margin-left:.4em;transform:rotate(-45deg)}.dropdown-menu{--tblr-dropdown-zindex: 1000;--tblr-dropdown-min-width: 11rem;--tblr-dropdown-padding-x: 0;--tblr-dropdown-padding-y: .25rem;--tblr-dropdown-spacer: 1px;--tblr-dropdown-font-size: .875rem;--tblr-dropdown-color: var(--tblr-body-color);--tblr-dropdown-bg: var(--tblr-bg-surface);--tblr-dropdown-border-color: var(--tblr-border-color-translucent);--tblr-dropdown-border-radius: var(--tblr-border-radius);--tblr-dropdown-border-width: var(--tblr-border-width);--tblr-dropdown-inner-border-radius: calc(var(--tblr-border-radius) - var(--tblr-border-width));--tblr-dropdown-divider-bg: var(--tblr-border-color-translucent);--tblr-dropdown-divider-margin-y: var(--tblr-spacer-2);--tblr-dropdown-box-shadow: var(--tblr-shadow-dropdown);--tblr-dropdown-link-color: inherit;--tblr-dropdown-link-hover-color: inherit;--tblr-dropdown-link-hover-bg: rgba(var(--tblr-secondary-rgb), .08);--tblr-dropdown-link-active-color: var(--tblr-primary);--tblr-dropdown-link-active-bg: var(--tblr-active-bg);--tblr-dropdown-link-disabled-color: var(--tblr-tertiary-color);--tblr-dropdown-item-padding-x: .75rem;--tblr-dropdown-item-padding-y: .5rem;--tblr-dropdown-header-color: #4b5563;--tblr-dropdown-header-padding-x: .75rem;--tblr-dropdown-header-padding-y: .25rem;position:absolute;z-index:var(--tblr-dropdown-zindex);display:none;min-width:var(--tblr-dropdown-min-width);padding:var(--tblr-dropdown-padding-y) var(--tblr-dropdown-padding-x);margin:0;font-size:var(--tblr-dropdown-font-size);color:var(--tblr-dropdown-color);text-align:left;list-style:none;background-color:var(--tblr-dropdown-bg);background-clip:padding-box;border:var(--tblr-dropdown-border-width) solid var(--tblr-dropdown-border-color);border-radius:var(--tblr-dropdown-border-radius);box-shadow:var(--tblr-dropdown-box-shadow)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--tblr-dropdown-spacer)}.dropdown-menu-start{--bs-position: start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position: end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media(min-width:576px){.dropdown-menu-sm-start{--bs-position: start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position: end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media(min-width:768px){.dropdown-menu-md-start{--bs-position: start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position: end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media(min-width:992px){.dropdown-menu-lg-start{--bs-position: start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position: end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1200px){.dropdown-menu-xl-start{--bs-position: start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position: end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1400px){.dropdown-menu-xxl-start{--bs-position: start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position: end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--tblr-dropdown-spacer)}.dropup .dropdown-toggle:after{content:"";display:inline-block;vertical-align:.306em;width:.36em;height:.36em;border-bottom:1px var(--tblr-border-style);border-left:1px var(--tblr-border-style);margin-right:.1em;margin-left:.4em;transform:rotate(135deg)}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--tblr-dropdown-spacer)}.dropend .dropdown-toggle:after{content:"";display:inline-block;vertical-align:.306em;width:.36em;height:.36em;border-bottom:1px var(--tblr-border-style);border-left:1px var(--tblr-border-style);margin-right:.1em;margin-left:.4em;transform:rotate(-135deg)}.dropend .dropdown-toggle:after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--tblr-dropdown-spacer)}.dropstart .dropdown-toggle:after{content:"";display:inline-block;vertical-align:.306em;width:.36em;height:.36em;border-bottom:1px var(--tblr-border-style);border-left:1px var(--tblr-border-style);margin-right:.1em;margin-left:.4em;transform:rotate(45deg)}.dropstart .dropdown-toggle:before{vertical-align:0}.dropdown-divider{height:0;margin:var(--tblr-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--tblr-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--tblr-dropdown-item-padding-y) var(--tblr-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--tblr-dropdown-link-color);text-align:inherit;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--tblr-dropdown-item-border-radius, 0)}.dropdown-item:hover,.dropdown-item:focus{color:var(--tblr-dropdown-link-hover-color);text-decoration:none;background-color:var(--tblr-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--tblr-dropdown-link-active-color);text-decoration:none;background-color:var(--tblr-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--tblr-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--tblr-dropdown-header-padding-y) var(--tblr-dropdown-header-padding-x);margin-bottom:0;font-size:.765625rem;color:var(--tblr-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--tblr-dropdown-item-padding-y) var(--tblr-dropdown-item-padding-x);color:var(--tblr-dropdown-link-color)}.dropdown-menu-dark{--tblr-dropdown-color: #d1d5db;--tblr-dropdown-bg: #1f2937;--tblr-dropdown-border-color: var(--tblr-border-color-translucent);--tblr-dropdown-box-shadow: ;--tblr-dropdown-link-color: #d1d5db;--tblr-dropdown-link-hover-color: #ffffff;--tblr-dropdown-divider-bg: var(--tblr-border-color-translucent);--tblr-dropdown-link-hover-bg: rgba(255, 255, 255, .15);--tblr-dropdown-link-active-color: var(--tblr-primary);--tblr-dropdown-link-active-bg: var(--tblr-active-bg);--tblr-dropdown-link-disabled-color: #6b7280;--tblr-dropdown-header-color: #6b7280}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;flex:1 1 auto}.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--tblr-border-radius)}.btn-group>:not(.btn-check:first-child)+.btn,.btn-group>.btn-group:not(:first-child){margin-left:calc(-1 * var(--tblr-border-width))}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn,.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.dropdown-toggle-split:after,.dropup .dropdown-toggle-split:after,.dropend .dropdown-toggle-split:after{margin-left:0}.dropstart .dropdown-toggle-split:before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:1.125rem;padding-left:1.125rem}.btn-group.show .dropdown-toggle{box-shadow:inset 0 3px 5px #00000020}.btn-group.show .dropdown-toggle.btn-link{box-shadow:none}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:calc(-1 * var(--tblr-border-width))}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:nth-child(n+3),.btn-group-vertical>:not(.btn-check)+.btn,.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--tblr-nav-link-padding-x: .75rem;--tblr-nav-link-padding-y: .5rem;--tblr-nav-link-font-weight: ;--tblr-nav-link-color: var(--tblr-gray-500);--tblr-nav-link-hover-color: var(--tblr-link-hover-color);--tblr-nav-link-disabled-color: var(--tblr-disabled-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--tblr-nav-link-padding-y) var(--tblr-nav-link-padding-x);font-size:var(--tblr-nav-link-font-size);font-weight:var(--tblr-nav-link-font-weight);color:var(--tblr-nav-link-color);background:none;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}.nav-link:hover,.nav-link:focus{color:var(--tblr-nav-link-hover-color);text-decoration:none}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.nav-link.disabled,.nav-link:disabled{color:var(--tblr-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--tblr-nav-tabs-border-width: var(--tblr-border-width);--tblr-nav-tabs-border-color: var(--tblr-border-color);--tblr-nav-tabs-border-radius: var(--tblr-border-radius);--tblr-nav-tabs-link-hover-border-color: var(--tblr-border-color) var(--tblr-border-color) var(--tblr-border-color);--tblr-nav-tabs-link-active-color: var(--tblr-body-color);--tblr-nav-tabs-link-active-bg: var(--tblr-body-bg);--tblr-nav-tabs-link-active-border-color: var(--tblr-border-color) var(--tblr-border-color) var(--tblr-border-color);border-bottom:var(--tblr-nav-tabs-border-width) solid var(--tblr-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--tblr-nav-tabs-border-width));border:var(--tblr-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--tblr-nav-tabs-border-radius);border-top-right-radius:var(--tblr-nav-tabs-border-radius)}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{isolation:isolate;border-color:var(--tblr-nav-tabs-link-hover-border-color)}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:var(--tblr-nav-tabs-link-active-color);background-color:var(--tblr-nav-tabs-link-active-bg);border-color:var(--tblr-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--tblr-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--tblr-nav-pills-border-radius: var(--tblr-border-radius);--tblr-nav-pills-link-active-color: var(--tblr-primary);--tblr-nav-pills-link-active-bg: rgba(var(--tblr-secondary-rgb), .15)}.nav-pills .nav-link{border-radius:var(--tblr-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--tblr-nav-pills-link-active-color);background-color:var(--tblr-nav-pills-link-active-bg)}.nav-underline{--tblr-nav-underline-gap: 1rem;--tblr-nav-underline-border-width: .125rem;--tblr-nav-underline-link-active-color: var(--tblr-emphasis-color);gap:var(--tblr-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--tblr-nav-underline-border-width) solid transparent}.nav-underline .nav-link:hover,.nav-underline .nav-link:focus{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:600;color:var(--tblr-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill>.nav-link,.nav-fill .nav-item{flex:1 1 auto;text-align:center}.nav-justified>.nav-link,.nav-justified .nav-item{flex-grow:1;flex-basis:0;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--tblr-navbar-padding-x: 0;--tblr-navbar-padding-y: .25rem;--tblr-navbar-color: var(--tblr-secondary);--tblr-navbar-hover-color: var(--tblr-body-color);--tblr-navbar-disabled-color: var(--tblr-disabled-color);--tblr-navbar-active-color: var(--tblr-body-color);--tblr-navbar-brand-padding-y: .5rem;--tblr-navbar-brand-margin-end: 1rem;--tblr-navbar-brand-font-size: 1.25rem;--tblr-navbar-brand-color: var(--tblr-body-color);--tblr-navbar-brand-hover-color: var(--tblr-body-color);--tblr-navbar-nav-link-padding-x: .75rem;--tblr-navbar-toggler-padding-y: 0;--tblr-navbar-toggler-padding-x: 0;--tblr-navbar-toggler-font-size: 1rem;--tblr-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2831, 41, 55, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--tblr-navbar-toggler-border-color: rgba(var(--tblr-emphasis-color-rgb), .15);--tblr-navbar-toggler-border-radius: var(--tblr-border-radius);--tblr-navbar-toggler-focus-width: 0;--tblr-navbar-toggler-transition: box-shadow .15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--tblr-navbar-padding-y) var(--tblr-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-sm,.navbar>.container-md,.navbar>.container-lg,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--tblr-navbar-brand-padding-y);padding-bottom:var(--tblr-navbar-brand-padding-y);margin-right:var(--tblr-navbar-brand-margin-end);font-size:var(--tblr-navbar-brand-font-size);color:var(--tblr-navbar-brand-color);white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{color:var(--tblr-navbar-brand-hover-color);text-decoration:none}.navbar-nav{--tblr-nav-link-padding-x: 0;--tblr-nav-link-padding-y: .5rem;--tblr-nav-link-font-weight: ;--tblr-nav-link-color: var(--tblr-navbar-color);--tblr-nav-link-hover-color: var(--tblr-navbar-hover-color);--tblr-nav-link-disabled-color: var(--tblr-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--tblr-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--tblr-navbar-color)}.navbar-text a,.navbar-text a:hover,.navbar-text a:focus{color:var(--tblr-navbar-active-color)}.navbar-collapse{flex-grow:1;flex-basis:100%;align-items:center}.navbar-toggler{padding:var(--tblr-navbar-toggler-padding-y) var(--tblr-navbar-toggler-padding-x);font-size:var(--tblr-navbar-toggler-font-size);line-height:1;color:var(--tblr-navbar-color);background-color:transparent;border:var(--tblr-border-width) solid var(--tblr-navbar-toggler-border-color);border-radius:var(--tblr-navbar-toggler-border-radius);transition:var(--tblr-navbar-toggler-transition)}@media(prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--tblr-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--tblr-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--tblr-scroll-height, 75vh);overflow-y:auto}@media(min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--tblr-navbar-nav-link-padding-x);padding-left:var(--tblr-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark],body[data-bs-theme=dark] .navbar[data-bs-theme=light]{--tblr-navbar-color: rgba(255, 255, 255, .7);--tblr-navbar-hover-color: rgba(255, 255, 255, .75);--tblr-navbar-disabled-color: var(--tblr-disabled-color);--tblr-navbar-active-color: #ffffff;--tblr-navbar-brand-color: #ffffff;--tblr-navbar-brand-hover-color: #ffffff;--tblr-navbar-toggler-border-color: rgba(255, 255, 255, .1);--tblr-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.7%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon,body[data-bs-theme=dark] [data-bs-theme=light] .navbar-toggler-icon{--tblr-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.7%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--tblr-card-spacer-y: 1rem;--tblr-card-spacer-x: 1.25rem;--tblr-card-title-spacer-y: 1.25rem;--tblr-card-title-color: ;--tblr-card-subtitle-color: ;--tblr-card-border-width: var(--tblr-border-width);--tblr-card-border-color: var(--tblr-border-color-translucent);--tblr-card-border-radius: var(--tblr-border-radius-lg);--tblr-card-box-shadow: var(--tblr-shadow-card);--tblr-card-inner-border-radius: calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)));--tblr-card-cap-padding-y: 1rem;--tblr-card-cap-padding-x: 1.25rem;--tblr-card-cap-bg: var(--tblr-bg-surface-tertiary);--tblr-card-cap-color: inherit;--tblr-card-height: ;--tblr-card-color: inherit;--tblr-card-bg: var(--tblr-bg-surface);--tblr-card-img-overlay-padding: 1rem;--tblr-card-group-margin: 1.5rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--tblr-card-height);color:var(--tblr-body-color);word-wrap:break-word;background-color:var(--tblr-card-bg);background-clip:border-box;border:var(--tblr-card-border-width) solid var(--tblr-card-border-color);border-radius:var(--tblr-card-border-radius);box-shadow:var(--tblr-card-box-shadow)}.card>hr,.card>.hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--tblr-card-inner-border-radius);border-top-right-radius:var(--tblr-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--tblr-card-inner-border-radius);border-bottom-left-radius:var(--tblr-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--tblr-card-spacer-y) var(--tblr-card-spacer-x);color:var(--tblr-card-color)}.card-title{margin-bottom:var(--tblr-card-title-spacer-y);color:var(--tblr-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--tblr-card-title-spacer-y));margin-bottom:0;color:var(--tblr-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:var(--tblr-card-spacer-x)}.card-header{padding:var(--tblr-card-cap-padding-y) var(--tblr-card-cap-padding-x);margin-bottom:0;color:var(--tblr-card-cap-color);background-color:var(--tblr-card-cap-bg);border-bottom:var(--tblr-card-border-width) solid var(--tblr-card-border-color)}.card-header:first-child{border-radius:var(--tblr-card-inner-border-radius) var(--tblr-card-inner-border-radius) 0 0}.card-footer{padding:var(--tblr-card-cap-padding-y) var(--tblr-card-cap-padding-x);color:var(--tblr-card-cap-color);background-color:var(--tblr-card-cap-bg);border-top:var(--tblr-card-border-width) solid var(--tblr-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--tblr-card-inner-border-radius) var(--tblr-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--tblr-card-cap-padding-x));margin-bottom:calc(-1 * var(--tblr-card-cap-padding-y));margin-left:calc(-.5 * var(--tblr-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--tblr-card-bg);border-bottom-color:var(--tblr-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--tblr-card-cap-padding-x));margin-left:calc(-.5 * var(--tblr-card-cap-padding-x))}.card-img-overlay{position:absolute;inset:0;padding:var(--tblr-card-img-overlay-padding);border-radius:var(--tblr-card-inner-border-radius)}.card-img,.card-img-top,.card-img-bottom{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--tblr-card-inner-border-radius);border-top-right-radius:var(--tblr-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--tblr-card-inner-border-radius);border-bottom-left-radius:var(--tblr-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--tblr-card-group-margin)}@media(min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child)>.card-img-top,.card-group>.card:not(:last-child)>.card-header{border-top-right-radius:0}.card-group>.card:not(:last-child)>.card-img-bottom,.card-group>.card:not(:last-child)>.card-footer{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child)>.card-img-top,.card-group>.card:not(:first-child)>.card-header{border-top-left-radius:0}.card-group>.card:not(:first-child)>.card-img-bottom,.card-group>.card:not(:first-child)>.card-footer{border-bottom-left-radius:0}}.pagination{--tblr-pagination-padding-x: .25rem;--tblr-pagination-padding-y: calc(.25rem + 1px) ;--tblr-pagination-font-size: .875rem;--tblr-pagination-color: var(--tblr-body-color);--tblr-pagination-bg: transparent;--tblr-pagination-border-width: 1px;--tblr-pagination-border-color: transparent;--tblr-pagination-border-radius: var(--tblr-border-radius);--tblr-pagination-hover-color: var(--tblr-link-hover-color);--tblr-pagination-hover-bg: var(--tblr-active-bg);--tblr-pagination-hover-border-color: var(--tblr-pagination-border-color);--tblr-pagination-focus-color: var(--tblr-link-hover-color);--tblr-pagination-focus-bg: var(--tblr-secondary-bg);--tblr-pagination-focus-box-shadow: 0 0 0 .25rem rgba(var(--tblr-primary-rgb), .25);--tblr-pagination-active-color: #ffffff;--tblr-pagination-active-bg: var(--tblr-primary);--tblr-pagination-active-border-color: var(--tblr-primary);--tblr-pagination-disabled-color: var(--tblr-disabled-color);--tblr-pagination-disabled-bg: transparent;--tblr-pagination-disabled-border-color: var(--tblr-pagination-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--tblr-pagination-padding-y) var(--tblr-pagination-padding-x);font-size:var(--tblr-pagination-font-size);color:var(--tblr-pagination-color);background-color:var(--tblr-pagination-bg);border:var(--tblr-pagination-border-width) solid var(--tblr-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--tblr-pagination-hover-color);text-decoration:none;background-color:var(--tblr-pagination-hover-bg);border-color:var(--tblr-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--tblr-pagination-focus-color);background-color:var(--tblr-pagination-focus-bg);outline:0;box-shadow:var(--tblr-pagination-focus-box-shadow)}.page-link.active,.active>.page-link{z-index:3;color:var(--tblr-pagination-active-color);background-color:var(--tblr-pagination-active-bg);border-color:var(--tblr-pagination-active-border-color)}.page-link.disabled,.disabled>.page-link{color:var(--tblr-pagination-disabled-color);pointer-events:none;background-color:var(--tblr-pagination-disabled-bg);border-color:var(--tblr-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item:first-child .page-link{border-top-left-radius:var(--tblr-pagination-border-radius);border-bottom-left-radius:var(--tblr-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--tblr-pagination-border-radius);border-bottom-right-radius:var(--tblr-pagination-border-radius)}.pagination-lg{--tblr-pagination-padding-x: 1.5rem;--tblr-pagination-padding-y: .75rem;--tblr-pagination-font-size: 1.09375rem;--tblr-pagination-border-radius: var(--tblr-border-radius-lg)}.pagination-sm{--tblr-pagination-padding-x: .5rem;--tblr-pagination-padding-y: .25rem;--tblr-pagination-font-size: .765625rem;--tblr-pagination-border-radius: var(--tblr-border-radius-sm)}@keyframes progress-bar-stripes{0%{background-position-x:var(--tblr-progress-height)}}.progress,.progress-stacked{--tblr-progress-height: .5rem;--tblr-progress-font-size: .65625rem;--tblr-progress-bg: var(--tblr-border-color);--tblr-progress-border-radius: var(--tblr-border-radius);--tblr-progress-box-shadow: var(--tblr-box-shadow-inset);--tblr-progress-bar-color: #ffffff;--tblr-progress-bar-bg: var(--tblr-primary);--tblr-progress-bar-transition: width .6s ease;display:flex;height:var(--tblr-progress-height);overflow:hidden;font-size:var(--tblr-progress-font-size);background-color:var(--tblr-progress-bg);border-radius:var(--tblr-progress-border-radius);box-shadow:var(--tblr-progress-box-shadow)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--tblr-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--tblr-progress-bar-bg);transition:var(--tblr-progress-bar-transition)}@media(prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--tblr-progress-height) var(--tblr-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media(prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--tblr-list-group-color: var(--tblr-body-color);--tblr-list-group-bg: inherit;--tblr-list-group-border-color: var(--tblr-border-color);--tblr-list-group-border-width: var(--tblr-border-width);--tblr-list-group-border-radius: var(--tblr-border-radius);--tblr-list-group-item-padding-x: 1.25rem;--tblr-list-group-item-padding-y: 1rem;--tblr-list-group-action-color: inherit;--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: rgba(var(--tblr-secondary-rgb), .08);--tblr-list-group-action-active-color: var(--tblr-body-color);--tblr-list-group-action-active-bg: var(--tblr-secondary-bg);--tblr-list-group-disabled-color: var(--tblr-secondary-color);--tblr-list-group-disabled-bg: inherit;--tblr-list-group-active-color: inherit;--tblr-list-group-active-bg: var(--tblr-active-bg);--tblr-list-group-active-border-color: var(--tblr-border-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--tblr-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item:before{content:counters(section,".") ". ";counter-increment:section}.list-group-item{position:relative;display:block;padding:var(--tblr-list-group-item-padding-y) var(--tblr-list-group-item-padding-x);color:var(--tblr-list-group-color);background-color:var(--tblr-list-group-bg);border:var(--tblr-list-group-border-width) solid var(--tblr-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--tblr-list-group-disabled-color);pointer-events:none;background-color:var(--tblr-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--tblr-list-group-active-color);background-color:var(--tblr-list-group-active-bg);border-color:var(--tblr-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--tblr-list-group-border-width));border-top-width:var(--tblr-list-group-border-width)}.list-group-item-action{width:100%;color:var(--tblr-list-group-action-color);text-align:inherit}.list-group-item-action:not(.active):hover,.list-group-item-action:not(.active):focus{z-index:1;color:var(--tblr-list-group-action-hover-color);text-decoration:none;background-color:var(--tblr-list-group-action-hover-bg)}.list-group-item-action:not(.active):active{color:var(--tblr-list-group-action-active-color);background-color:var(--tblr-list-group-action-active-bg)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}@media(min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}}@media(min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}}@media(min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}}@media(min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}}@media(min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--tblr-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--tblr-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--tblr-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--tblr-list-group-border-width));border-left-width:var(--tblr-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--tblr-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--tblr-list-group-color: var(--tblr-primary-text-emphasis);--tblr-list-group-bg: var(--tblr-primary-bg-subtle);--tblr-list-group-border-color: var(--tblr-primary-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-primary-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-primary-border-subtle);--tblr-list-group-active-color: var(--tblr-primary-bg-subtle);--tblr-list-group-active-bg: var(--tblr-primary-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-primary-text-emphasis)}.list-group-item-secondary{--tblr-list-group-color: var(--tblr-secondary-text-emphasis);--tblr-list-group-bg: var(--tblr-secondary-bg-subtle);--tblr-list-group-border-color: var(--tblr-secondary-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-secondary-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-secondary-border-subtle);--tblr-list-group-active-color: var(--tblr-secondary-bg-subtle);--tblr-list-group-active-bg: var(--tblr-secondary-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-secondary-text-emphasis)}.list-group-item-success{--tblr-list-group-color: var(--tblr-success-text-emphasis);--tblr-list-group-bg: var(--tblr-success-bg-subtle);--tblr-list-group-border-color: var(--tblr-success-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-success-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-success-border-subtle);--tblr-list-group-active-color: var(--tblr-success-bg-subtle);--tblr-list-group-active-bg: var(--tblr-success-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-success-text-emphasis)}.list-group-item-info{--tblr-list-group-color: var(--tblr-info-text-emphasis);--tblr-list-group-bg: var(--tblr-info-bg-subtle);--tblr-list-group-border-color: var(--tblr-info-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-info-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-info-border-subtle);--tblr-list-group-active-color: var(--tblr-info-bg-subtle);--tblr-list-group-active-bg: var(--tblr-info-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-info-text-emphasis)}.list-group-item-warning{--tblr-list-group-color: var(--tblr-warning-text-emphasis);--tblr-list-group-bg: var(--tblr-warning-bg-subtle);--tblr-list-group-border-color: var(--tblr-warning-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-warning-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-warning-border-subtle);--tblr-list-group-active-color: var(--tblr-warning-bg-subtle);--tblr-list-group-active-bg: var(--tblr-warning-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-warning-text-emphasis)}.list-group-item-danger{--tblr-list-group-color: var(--tblr-danger-text-emphasis);--tblr-list-group-bg: var(--tblr-danger-bg-subtle);--tblr-list-group-border-color: var(--tblr-danger-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-danger-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-danger-border-subtle);--tblr-list-group-active-color: var(--tblr-danger-bg-subtle);--tblr-list-group-active-bg: var(--tblr-danger-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-danger-text-emphasis)}.list-group-item-light{--tblr-list-group-color: var(--tblr-light-text-emphasis);--tblr-list-group-bg: var(--tblr-light-bg-subtle);--tblr-list-group-border-color: var(--tblr-light-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-light-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-light-border-subtle);--tblr-list-group-active-color: var(--tblr-light-bg-subtle);--tblr-list-group-active-bg: var(--tblr-light-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-light-text-emphasis)}.list-group-item-dark{--tblr-list-group-color: var(--tblr-dark-text-emphasis);--tblr-list-group-bg: var(--tblr-dark-bg-subtle);--tblr-list-group-border-color: var(--tblr-dark-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-dark-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-dark-border-subtle);--tblr-list-group-active-color: var(--tblr-dark-bg-subtle);--tblr-list-group-active-bg: var(--tblr-dark-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-dark-text-emphasis)}.list-group-item-muted{--tblr-list-group-color: var(--tblr-muted-text-emphasis);--tblr-list-group-bg: var(--tblr-muted-bg-subtle);--tblr-list-group-border-color: var(--tblr-muted-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-muted-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-muted-border-subtle);--tblr-list-group-active-color: var(--tblr-muted-bg-subtle);--tblr-list-group-active-bg: var(--tblr-muted-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-muted-text-emphasis)}.list-group-item-blue{--tblr-list-group-color: var(--tblr-blue-text-emphasis);--tblr-list-group-bg: var(--tblr-blue-bg-subtle);--tblr-list-group-border-color: var(--tblr-blue-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-blue-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-blue-border-subtle);--tblr-list-group-active-color: var(--tblr-blue-bg-subtle);--tblr-list-group-active-bg: var(--tblr-blue-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-blue-text-emphasis)}.list-group-item-azure{--tblr-list-group-color: var(--tblr-azure-text-emphasis);--tblr-list-group-bg: var(--tblr-azure-bg-subtle);--tblr-list-group-border-color: var(--tblr-azure-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-azure-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-azure-border-subtle);--tblr-list-group-active-color: var(--tblr-azure-bg-subtle);--tblr-list-group-active-bg: var(--tblr-azure-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-azure-text-emphasis)}.list-group-item-indigo{--tblr-list-group-color: var(--tblr-indigo-text-emphasis);--tblr-list-group-bg: var(--tblr-indigo-bg-subtle);--tblr-list-group-border-color: var(--tblr-indigo-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-indigo-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-indigo-border-subtle);--tblr-list-group-active-color: var(--tblr-indigo-bg-subtle);--tblr-list-group-active-bg: var(--tblr-indigo-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-indigo-text-emphasis)}.list-group-item-purple{--tblr-list-group-color: var(--tblr-purple-text-emphasis);--tblr-list-group-bg: var(--tblr-purple-bg-subtle);--tblr-list-group-border-color: var(--tblr-purple-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-purple-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-purple-border-subtle);--tblr-list-group-active-color: var(--tblr-purple-bg-subtle);--tblr-list-group-active-bg: var(--tblr-purple-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-purple-text-emphasis)}.list-group-item-pink{--tblr-list-group-color: var(--tblr-pink-text-emphasis);--tblr-list-group-bg: var(--tblr-pink-bg-subtle);--tblr-list-group-border-color: var(--tblr-pink-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-pink-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-pink-border-subtle);--tblr-list-group-active-color: var(--tblr-pink-bg-subtle);--tblr-list-group-active-bg: var(--tblr-pink-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-pink-text-emphasis)}.list-group-item-red{--tblr-list-group-color: var(--tblr-red-text-emphasis);--tblr-list-group-bg: var(--tblr-red-bg-subtle);--tblr-list-group-border-color: var(--tblr-red-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-red-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-red-border-subtle);--tblr-list-group-active-color: var(--tblr-red-bg-subtle);--tblr-list-group-active-bg: var(--tblr-red-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-red-text-emphasis)}.list-group-item-orange{--tblr-list-group-color: var(--tblr-orange-text-emphasis);--tblr-list-group-bg: var(--tblr-orange-bg-subtle);--tblr-list-group-border-color: var(--tblr-orange-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-orange-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-orange-border-subtle);--tblr-list-group-active-color: var(--tblr-orange-bg-subtle);--tblr-list-group-active-bg: var(--tblr-orange-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-orange-text-emphasis)}.list-group-item-yellow{--tblr-list-group-color: var(--tblr-yellow-text-emphasis);--tblr-list-group-bg: var(--tblr-yellow-bg-subtle);--tblr-list-group-border-color: var(--tblr-yellow-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-yellow-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-yellow-border-subtle);--tblr-list-group-active-color: var(--tblr-yellow-bg-subtle);--tblr-list-group-active-bg: var(--tblr-yellow-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-yellow-text-emphasis)}.list-group-item-lime{--tblr-list-group-color: var(--tblr-lime-text-emphasis);--tblr-list-group-bg: var(--tblr-lime-bg-subtle);--tblr-list-group-border-color: var(--tblr-lime-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-lime-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-lime-border-subtle);--tblr-list-group-active-color: var(--tblr-lime-bg-subtle);--tblr-list-group-active-bg: var(--tblr-lime-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-lime-text-emphasis)}.list-group-item-green{--tblr-list-group-color: var(--tblr-green-text-emphasis);--tblr-list-group-bg: var(--tblr-green-bg-subtle);--tblr-list-group-border-color: var(--tblr-green-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-green-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-green-border-subtle);--tblr-list-group-active-color: var(--tblr-green-bg-subtle);--tblr-list-group-active-bg: var(--tblr-green-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-green-text-emphasis)}.list-group-item-teal{--tblr-list-group-color: var(--tblr-teal-text-emphasis);--tblr-list-group-bg: var(--tblr-teal-bg-subtle);--tblr-list-group-border-color: var(--tblr-teal-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-teal-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-teal-border-subtle);--tblr-list-group-active-color: var(--tblr-teal-bg-subtle);--tblr-list-group-active-bg: var(--tblr-teal-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-teal-text-emphasis)}.list-group-item-cyan{--tblr-list-group-color: var(--tblr-cyan-text-emphasis);--tblr-list-group-bg: var(--tblr-cyan-bg-subtle);--tblr-list-group-border-color: var(--tblr-cyan-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-cyan-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-cyan-border-subtle);--tblr-list-group-active-color: var(--tblr-cyan-bg-subtle);--tblr-list-group-active-bg: var(--tblr-cyan-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-cyan-text-emphasis)}.toast{--tblr-toast-zindex: 1090;--tblr-toast-padding-x: .75rem;--tblr-toast-padding-y: .5rem;--tblr-toast-spacing: calc(var(--tblr-page-padding) * 2);--tblr-toast-max-width: 350px;--tblr-toast-font-size: .875rem;--tblr-toast-color: ;--tblr-toast-bg: var(--tblr-bg-surface);--tblr-toast-border-width: var(--tblr-border-width);--tblr-toast-border-color: var(--tblr-border-color);--tblr-toast-border-radius: var(--tblr-border-radius);--tblr-toast-box-shadow: var(--tblr-box-shadow);--tblr-toast-header-color: var(--tblr-gray-500);--tblr-toast-header-bg: rgba(var(--tblr-body-bg-rgb), .85);--tblr-toast-header-border-color: var(--tblr-border-color);width:var(--tblr-toast-max-width);max-width:100%;font-size:var(--tblr-toast-font-size);color:var(--tblr-toast-color);pointer-events:auto;background-color:var(--tblr-toast-bg);background-clip:padding-box;border:var(--tblr-toast-border-width) solid var(--tblr-toast-border-color);box-shadow:var(--tblr-toast-box-shadow);border-radius:var(--tblr-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--tblr-toast-zindex: 1090;position:absolute;z-index:var(--tblr-toast-zindex);width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--tblr-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--tblr-toast-padding-y) var(--tblr-toast-padding-x);color:var(--tblr-toast-header-color);background-color:var(--tblr-toast-header-bg);background-clip:padding-box;border-bottom:var(--tblr-toast-border-width) solid var(--tblr-toast-header-border-color);border-top-left-radius:calc(var(--tblr-toast-border-radius) - var(--tblr-toast-border-width));border-top-right-radius:calc(var(--tblr-toast-border-radius) - var(--tblr-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--tblr-toast-padding-x));margin-left:var(--tblr-toast-padding-x)}.toast-body{padding:var(--tblr-toast-padding-x);word-wrap:break-word}.modal{--tblr-modal-zindex: 1055;--tblr-modal-width: 540px;--tblr-modal-padding: 1.5rem;--tblr-modal-margin: .5rem;--tblr-modal-color: var(--tblr-body-color);--tblr-modal-bg: var(--tblr-bg-surface);--tblr-modal-border-color: transparent;--tblr-modal-border-width: var(--tblr-border-width);--tblr-modal-border-radius: var(--tblr-border-radius-lg);--tblr-modal-box-shadow: var(--tblr-box-shadow-sm);--tblr-modal-inner-border-radius: calc(var(--tblr-modal-border-radius) - 1px);--tblr-modal-header-padding-x: 1.5rem;--tblr-modal-header-padding-y: 1.5rem;--tblr-modal-header-padding: 1.5rem;--tblr-modal-header-border-color: var(--tblr-border-color);--tblr-modal-header-border-width: var(--tblr-border-width);--tblr-modal-title-line-height: 1.4285714286;--tblr-modal-footer-gap: .75rem;--tblr-modal-footer-bg: var(--tblr-bg-surface-tertiary);--tblr-modal-footer-border-color: var(--tblr-border-color);--tblr-modal-footer-border-width: var(--tblr-border-width);position:fixed;top:0;left:0;z-index:var(--tblr-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--tblr-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transform:translateY(-1rem);transition:transform .3s ease-out}@media(prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--tblr-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--tblr-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--tblr-modal-color);pointer-events:auto;background-color:var(--tblr-modal-bg);background-clip:padding-box;border:var(--tblr-modal-border-width) solid var(--tblr-modal-border-color);border-radius:var(--tblr-modal-border-radius);box-shadow:var(--tblr-modal-box-shadow);outline:0}.modal-backdrop{--tblr-backdrop-zindex: 1050;--tblr-backdrop-bg: var(--tblr-gray-800);--tblr-backdrop-opacity: .24;position:fixed;top:0;left:0;z-index:var(--tblr-backdrop-zindex);width:100vw;height:100vh;background-color:var(--tblr-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--tblr-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--tblr-modal-header-padding);border-bottom:var(--tblr-modal-header-border-width) solid var(--tblr-modal-header-border-color);border-top-left-radius:var(--tblr-modal-inner-border-radius);border-top-right-radius:var(--tblr-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--tblr-modal-header-padding-y) * .5) calc(var(--tblr-modal-header-padding-x) * .5);margin-top:calc(-.5 * var(--tblr-modal-header-padding-y));margin-right:calc(-.5 * var(--tblr-modal-header-padding-x));margin-bottom:calc(-.5 * var(--tblr-modal-header-padding-y));margin-left:auto}.modal-title{margin-bottom:0;line-height:var(--tblr-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--tblr-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--tblr-modal-padding) - var(--tblr-modal-footer-gap) * .5);background-color:var(--tblr-modal-footer-bg);border-top:var(--tblr-modal-footer-border-width) solid var(--tblr-modal-footer-border-color);border-bottom-right-radius:var(--tblr-modal-inner-border-radius);border-bottom-left-radius:var(--tblr-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--tblr-modal-footer-gap) * .5)}@media(min-width:576px){.modal{--tblr-modal-margin: 1.75rem;--tblr-modal-box-shadow: var(--tblr-box-shadow)}.modal-dialog{max-width:var(--tblr-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--tblr-modal-width: 380px}}@media(min-width:992px){.modal-lg,.modal-xl{--tblr-modal-width: 720px}}@media(min-width:1200px){.modal-xl{--tblr-modal-width: 1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header,.modal-fullscreen .modal-footer{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media(max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header,.modal-fullscreen-sm-down .modal-footer{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media(max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header,.modal-fullscreen-md-down .modal-footer{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media(max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header,.modal-fullscreen-lg-down .modal-footer{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media(max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header,.modal-fullscreen-xl-down .modal-footer{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media(max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header,.modal-fullscreen-xxl-down .modal-footer{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--tblr-tooltip-zindex: 1080;--tblr-tooltip-max-width: 200px;--tblr-tooltip-padding-x: var(--tblr-spacer-3);--tblr-tooltip-padding-y: var(--tblr-spacer-1);--tblr-tooltip-margin: ;--tblr-tooltip-font-size: .765625rem;--tblr-tooltip-color: var(--tblr-text-inverted);--tblr-tooltip-bg: var(--tblr-bg-surface-inverted);--tblr-tooltip-border-radius: var(--tblr-border-radius);--tblr-tooltip-opacity: .9;--tblr-tooltip-arrow-width: .8rem;--tblr-tooltip-arrow-height: .4rem;z-index:var(--tblr-tooltip-zindex);display:block;margin:var(--tblr-tooltip-margin);font-family:var(--tblr-font-sans-serif);font-style:normal;font-weight:400;line-height:1.4285714286;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--tblr-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--tblr-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--tblr-tooltip-arrow-width);height:var(--tblr-tooltip-arrow-height)}.tooltip .tooltip-arrow:before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow{bottom:calc(-1 * var(--tblr-tooltip-arrow-height))}.bs-tooltip-top .tooltip-arrow:before,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow:before{top:-1px;border-width:var(--tblr-tooltip-arrow-height) calc(var(--tblr-tooltip-arrow-width) * .5) 0;border-top-color:var(--tblr-tooltip-bg)}.bs-tooltip-end .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow{left:calc(-1 * var(--tblr-tooltip-arrow-height));width:var(--tblr-tooltip-arrow-height);height:var(--tblr-tooltip-arrow-width)}.bs-tooltip-end .tooltip-arrow:before,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow:before{right:-1px;border-width:calc(var(--tblr-tooltip-arrow-width) * .5) var(--tblr-tooltip-arrow-height) calc(var(--tblr-tooltip-arrow-width) * .5) 0;border-right-color:var(--tblr-tooltip-bg)}.bs-tooltip-bottom .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow{top:calc(-1 * var(--tblr-tooltip-arrow-height))}.bs-tooltip-bottom .tooltip-arrow:before,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow:before{bottom:-1px;border-width:0 calc(var(--tblr-tooltip-arrow-width) * .5) var(--tblr-tooltip-arrow-height);border-bottom-color:var(--tblr-tooltip-bg)}.bs-tooltip-start .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow{right:calc(-1 * var(--tblr-tooltip-arrow-height));width:var(--tblr-tooltip-arrow-height);height:var(--tblr-tooltip-arrow-width)}.bs-tooltip-start .tooltip-arrow:before,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow:before{left:-1px;border-width:calc(var(--tblr-tooltip-arrow-width) * .5) 0 calc(var(--tblr-tooltip-arrow-width) * .5) var(--tblr-tooltip-arrow-height);border-left-color:var(--tblr-tooltip-bg)}.tooltip-inner{max-width:var(--tblr-tooltip-max-width);padding:var(--tblr-tooltip-padding-y) var(--tblr-tooltip-padding-x);color:var(--tblr-tooltip-color);text-align:center;background-color:var(--tblr-tooltip-bg);border-radius:var(--tblr-tooltip-border-radius)}.popover{--tblr-popover-zindex: 1070;--tblr-popover-max-width: 276px;--tblr-popover-font-size: .765625rem;--tblr-popover-bg: var(--tblr-bg-surface);--tblr-popover-border-width: var(--tblr-border-width);--tblr-popover-border-color: var(--tblr-border-color);--tblr-popover-border-radius: var(--tblr-border-radius-lg);--tblr-popover-inner-border-radius: calc(var(--tblr-border-radius-lg) - var(--tblr-border-width));--tblr-popover-box-shadow: var(--tblr-shadow-lg);--tblr-popover-header-padding-x: 1rem;--tblr-popover-header-padding-y: .5rem;--tblr-popover-header-font-size: .875rem;--tblr-popover-header-color: inherit;--tblr-popover-header-bg: transparent;--tblr-popover-body-padding-x: .5rem;--tblr-popover-body-padding-y: .5rem;--tblr-popover-body-color: inherit;--tblr-popover-arrow-width: 1rem;--tblr-popover-arrow-height: .5rem;--tblr-popover-arrow-border: var(--tblr-popover-border-color);z-index:var(--tblr-popover-zindex);display:block;max-width:var(--tblr-popover-max-width);font-family:var(--tblr-font-sans-serif);font-style:normal;font-weight:400;line-height:1.4285714286;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--tblr-popover-font-size);word-wrap:break-word;background-color:var(--tblr-popover-bg);background-clip:padding-box;border:var(--tblr-popover-border-width) solid var(--tblr-popover-border-color);border-radius:var(--tblr-popover-border-radius);box-shadow:var(--tblr-popover-box-shadow)}.popover .popover-arrow{display:block;width:var(--tblr-popover-arrow-width);height:var(--tblr-popover-arrow-height)}.popover .popover-arrow:before,.popover .popover-arrow:after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-top>.popover-arrow,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow{bottom:calc(-1 * (var(--tblr-popover-arrow-height)) - var(--tblr-popover-border-width))}.bs-popover-top>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow:before,.bs-popover-top>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow:after{border-width:var(--tblr-popover-arrow-height) calc(var(--tblr-popover-arrow-width) * .5) 0}.bs-popover-top>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow:before{bottom:0;border-top-color:var(--tblr-popover-arrow-border)}.bs-popover-top>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow:after{bottom:var(--tblr-popover-border-width);border-top-color:var(--tblr-popover-bg)}.bs-popover-end>.popover-arrow,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow{left:calc(-1 * (var(--tblr-popover-arrow-height)) - var(--tblr-popover-border-width));width:var(--tblr-popover-arrow-height);height:var(--tblr-popover-arrow-width)}.bs-popover-end>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow:before,.bs-popover-end>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow:after{border-width:calc(var(--tblr-popover-arrow-width) * .5) var(--tblr-popover-arrow-height) calc(var(--tblr-popover-arrow-width) * .5) 0}.bs-popover-end>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow:before{left:0;border-right-color:var(--tblr-popover-arrow-border)}.bs-popover-end>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow:after{left:var(--tblr-popover-border-width);border-right-color:var(--tblr-popover-bg)}.bs-popover-bottom>.popover-arrow,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow{top:calc(-1 * (var(--tblr-popover-arrow-height)) - var(--tblr-popover-border-width))}.bs-popover-bottom>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow:before,.bs-popover-bottom>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow:after{border-width:0 calc(var(--tblr-popover-arrow-width) * .5) var(--tblr-popover-arrow-height)}.bs-popover-bottom>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow:before{top:0;border-bottom-color:var(--tblr-popover-arrow-border)}.bs-popover-bottom>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow:after{top:var(--tblr-popover-border-width);border-bottom-color:var(--tblr-popover-bg)}.bs-popover-bottom .popover-header:before,.bs-popover-auto[data-popper-placement^=bottom] .popover-header:before{position:absolute;top:0;left:50%;display:block;width:var(--tblr-popover-arrow-width);margin-left:calc(-.5 * var(--tblr-popover-arrow-width));content:"";border-bottom:var(--tblr-popover-border-width) solid var(--tblr-popover-header-bg)}.bs-popover-start>.popover-arrow,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow{right:calc(-1 * (var(--tblr-popover-arrow-height)) - var(--tblr-popover-border-width));width:var(--tblr-popover-arrow-height);height:var(--tblr-popover-arrow-width)}.bs-popover-start>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow:before,.bs-popover-start>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow:after{border-width:calc(var(--tblr-popover-arrow-width) * .5) 0 calc(var(--tblr-popover-arrow-width) * .5) var(--tblr-popover-arrow-height)}.bs-popover-start>.popover-arrow:before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow:before{right:0;border-left-color:var(--tblr-popover-arrow-border)}.bs-popover-start>.popover-arrow:after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow:after{right:var(--tblr-popover-border-width);border-left-color:var(--tblr-popover-bg)}.popover-header{padding:var(--tblr-popover-header-padding-y) var(--tblr-popover-header-padding-x);margin-bottom:0;font-size:var(--tblr-popover-header-font-size);color:var(--tblr-popover-header-color);background-color:var(--tblr-popover-header-bg);border-bottom:var(--tblr-popover-border-width) solid var(--tblr-popover-border-color);border-top-left-radius:var(--tblr-popover-inner-border-radius);border-top-right-radius:var(--tblr-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--tblr-popover-body-padding-y) var(--tblr-popover-body-padding-x);color:var(--tblr-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner:after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;transition:transform .6s ease-in-out}@media(prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block}.carousel-item-next:not(.carousel-item-start),.active.carousel-item-end{transform:translate(100%)}.carousel-item-prev:not(.carousel-item-end),.active.carousel-item-start{transform:translate(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end{z-index:1;opacity:1}.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{z-index:0;opacity:0;transition:opacity 0s .6s}@media(prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{transition:none}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:none;filter:var(--tblr-carousel-control-icon-filter);border:0;opacity:.5;transition:opacity .15s ease}@media(prefers-reduced-motion:reduce){.carousel-control-prev,.carousel-control-next{transition:none}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:1.5rem;height:1.5rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='15 18 9 12 15 6'%3e%3c/polyline%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='9 18 15 12 9 6'%3e%3c/polyline%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:var(--tblr-carousel-indicator-active-bg);background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media(prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:var(--tblr-carousel-caption-color);text-align:center}.carousel-dark{--tblr-carousel-indicator-active-bg: #000000;--tblr-carousel-caption-color: #000000;--tblr-carousel-control-icon-filter: invert(1) grayscale(100)}:root,[data-bs-theme=light]{--tblr-carousel-indicator-active-bg: #ffffff;--tblr-carousel-caption-color: #ffffff;--tblr-carousel-control-icon-filter: }[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{--tblr-carousel-indicator-active-bg: #000000;--tblr-carousel-caption-color: #000000;--tblr-carousel-control-icon-filter: invert(1) grayscale(100)}.spinner-grow,.spinner-border{display:inline-block;flex-shrink:0;width:var(--tblr-spinner-width);height:var(--tblr-spinner-height);vertical-align:var(--tblr-spinner-vertical-align);border-radius:50%;animation:var(--tblr-spinner-animation-speed) linear infinite var(--tblr-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--tblr-spinner-width: 1.5rem;--tblr-spinner-height: 1.5rem;--tblr-spinner-vertical-align: -.125em;--tblr-spinner-border-width: 2px;--tblr-spinner-animation-speed: .75s;--tblr-spinner-animation-name: spinner-border;border:var(--tblr-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--tblr-spinner-width: 1rem;--tblr-spinner-height: 1rem;--tblr-spinner-border-width: 1px}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--tblr-spinner-width: 1.5rem;--tblr-spinner-height: 1.5rem;--tblr-spinner-vertical-align: -.125em;--tblr-spinner-animation-speed: .75s;--tblr-spinner-animation-name: spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--tblr-spinner-width: 1rem;--tblr-spinner-height: 1rem}@media(prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--tblr-spinner-animation-speed: 1.5s}}.offcanvas,.offcanvas-xxl,.offcanvas-xl,.offcanvas-lg,.offcanvas-md,.offcanvas-sm{--tblr-offcanvas-zindex: 1045;--tblr-offcanvas-width: 400px;--tblr-offcanvas-height: 30vh;--tblr-offcanvas-padding-x: 1.5rem;--tblr-offcanvas-padding-y: 1.5rem;--tblr-offcanvas-color: var(--tblr-body-color);--tblr-offcanvas-bg: var(--tblr-bg-surface);--tblr-offcanvas-border-width: var(--tblr-border-width);--tblr-offcanvas-border-color: var(--tblr-border-color);--tblr-offcanvas-box-shadow: var(--tblr-box-shadow-sm);--tblr-offcanvas-transition: transform .3s ease-in-out;--tblr-offcanvas-title-line-height: 1.4285714286}@media(max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}}@media(max-width:575.98px)and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media(max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.showing,.offcanvas-sm.show:not(.hiding){transform:none}.offcanvas-sm.showing,.offcanvas-sm.hiding,.offcanvas-sm.show{visibility:visible}}@media(min-width:576px){.offcanvas-sm{--tblr-offcanvas-height: auto;--tblr-offcanvas-border-width: 0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}}@media(max-width:767.98px)and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media(max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.showing,.offcanvas-md.show:not(.hiding){transform:none}.offcanvas-md.showing,.offcanvas-md.hiding,.offcanvas-md.show{visibility:visible}}@media(min-width:768px){.offcanvas-md{--tblr-offcanvas-height: auto;--tblr-offcanvas-border-width: 0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}}@media(max-width:991.98px)and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media(max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.showing,.offcanvas-lg.show:not(.hiding){transform:none}.offcanvas-lg.showing,.offcanvas-lg.hiding,.offcanvas-lg.show{visibility:visible}}@media(min-width:992px){.offcanvas-lg{--tblr-offcanvas-height: auto;--tblr-offcanvas-border-width: 0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}}@media(max-width:1199.98px)and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media(max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.showing,.offcanvas-xl.show:not(.hiding){transform:none}.offcanvas-xl.showing,.offcanvas-xl.hiding,.offcanvas-xl.show{visibility:visible}}@media(min-width:1200px){.offcanvas-xl{--tblr-offcanvas-height: auto;--tblr-offcanvas-border-width: 0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}}@media(max-width:1399.98px)and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media(max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.showing,.offcanvas-xxl.show:not(.hiding){transform:none}.offcanvas-xxl.showing,.offcanvas-xxl.hiding,.offcanvas-xxl.show{visibility:visible}}@media(min-width:1400px){.offcanvas-xxl{--tblr-offcanvas-height: auto;--tblr-offcanvas-border-width: 0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--tblr-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--tblr-offcanvas-color);visibility:hidden;background-color:var(--tblr-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--tblr-offcanvas-box-shadow);transition:var(--tblr-offcanvas-transition)}@media(prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--tblr-offcanvas-width);border-right:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--tblr-offcanvas-width);border-left:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translate(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-bottom:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--tblr-offcanvas-height);max-height:100%;border-top:var(--tblr-offcanvas-border-width) solid var(--tblr-offcanvas-border-color);transform:translateY(100%)}.offcanvas.showing,.offcanvas.show:not(.hiding){transform:none}.offcanvas.showing,.offcanvas.hiding,.offcanvas.show{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:var(--tblr-gray-800)}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.24}.offcanvas-header{display:flex;align-items:center;padding:var(--tblr-offcanvas-padding-y) var(--tblr-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--tblr-offcanvas-padding-y) * .5) calc(var(--tblr-offcanvas-padding-x) * .5);margin-top:calc(-.5 * var(--tblr-offcanvas-padding-y));margin-right:calc(-.5 * var(--tblr-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--tblr-offcanvas-padding-y));margin-left:auto}.offcanvas-title{margin-bottom:0;line-height:var(--tblr-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--tblr-offcanvas-padding-y) var(--tblr-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.2}.placeholder.btn:before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.1}}.placeholder-wave{mask-image:linear-gradient(130deg,#000 55%,#000000e6,#000 95%);mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{to{mask-position:-200% 0%}}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{object-fit:contain!important}.object-fit-cover{object-fit:cover!important}.object-fit-fill{object-fit:fill!important}.object-fit-scale{object-fit:scale-down!important}.object-fit-none{object-fit:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:var(--tblr-box-shadow)!important}.shadow-sm{box-shadow:var(--tblr-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--tblr-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--tblr-focus-ring-color: rgba(var(--tblr-primary-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-secondary{--tblr-focus-ring-color: rgba(var(--tblr-secondary-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-success{--tblr-focus-ring-color: rgba(var(--tblr-success-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-info{--tblr-focus-ring-color: rgba(var(--tblr-info-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-warning{--tblr-focus-ring-color: rgba(var(--tblr-warning-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-danger{--tblr-focus-ring-color: rgba(var(--tblr-danger-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-light{--tblr-focus-ring-color: rgba(var(--tblr-light-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-dark{--tblr-focus-ring-color: rgba(var(--tblr-dark-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-muted{--tblr-focus-ring-color: rgba(var(--tblr-muted-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-blue{--tblr-focus-ring-color: rgba(var(--tblr-blue-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-azure{--tblr-focus-ring-color: rgba(var(--tblr-azure-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-indigo{--tblr-focus-ring-color: rgba(var(--tblr-indigo-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-purple{--tblr-focus-ring-color: rgba(var(--tblr-purple-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-pink{--tblr-focus-ring-color: rgba(var(--tblr-pink-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-red{--tblr-focus-ring-color: rgba(var(--tblr-red-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-orange{--tblr-focus-ring-color: rgba(var(--tblr-orange-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-yellow{--tblr-focus-ring-color: rgba(var(--tblr-yellow-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-lime{--tblr-focus-ring-color: rgba(var(--tblr-lime-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-green{--tblr-focus-ring-color: rgba(var(--tblr-green-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-teal{--tblr-focus-ring-color: rgba(var(--tblr-teal-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-cyan{--tblr-focus-ring-color: rgba(var(--tblr-cyan-rgb), var(--tblr-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translate(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-wide{border:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-0{border:0!important}.border-top{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-top-wide{border-top:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-end-wide{border-right:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-bottom-wide{border-bottom:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-start-wide{border-left:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-start-0{border-left:0!important}.border-red{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-red-rgb),var(--tblr-border-opacity))!important}.border-green{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-green-rgb),var(--tblr-border-opacity))!important}.border-primary-subtle{border-color:var(--tblr-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--tblr-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--tblr-success-border-subtle)!important}.border-info-subtle{border-color:var(--tblr-info-border-subtle)!important}.border-warning-subtle{border-color:var(--tblr-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--tblr-danger-border-subtle)!important}.border-light-subtle{border-color:var(--tblr-light-border-subtle)!important}.border-dark-subtle{border-color:var(--tblr-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--tblr-border-opacity: .1}.border-opacity-25{--tblr-border-opacity: .25}.border-opacity-50{--tblr-border-opacity: .5}.border-opacity-75{--tblr-border-opacity: .75}.border-opacity-100{--tblr-border-opacity: 1}.w-25{width:25%!important}.w-33{width:33.33333%!important}.w-50{width:50%!important}.w-66{width:66.66666%!important}.w-75{width:75%!important}.w-100{width:100%!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-33{height:33.33333%!important}.h-50{height:50%!important}.h-66{height:66.66666%!important}.h-75{height:75%!important}.h-100{height:100%!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:2rem!important}.m-6{margin:2.5rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:2rem!important;margin-left:2rem!important}.mx-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:2rem!important}.mt-6{margin-top:2.5rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:2rem!important}.me-6{margin-right:2.5rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:2rem!important}.mb-6{margin-bottom:2.5rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:2rem!important}.ms-6{margin-left:2.5rem!important}.ms-auto{margin-left:auto!important}.m-n1{margin:-.25rem!important}.m-n2{margin:-.5rem!important}.m-n3{margin:-1rem!important}.m-n4{margin:-1.5rem!important}.m-n5{margin:-2rem!important}.m-n6{margin:-2.5rem!important}.mx-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-n1{margin-top:-.25rem!important}.mt-n2{margin-top:-.5rem!important}.mt-n3{margin-top:-1rem!important}.mt-n4{margin-top:-1.5rem!important}.mt-n5{margin-top:-2rem!important}.mt-n6{margin-top:-2.5rem!important}.me-n1{margin-right:-.25rem!important}.me-n2{margin-right:-.5rem!important}.me-n3{margin-right:-1rem!important}.me-n4{margin-right:-1.5rem!important}.me-n5{margin-right:-2rem!important}.me-n6{margin-right:-2.5rem!important}.mb-n1{margin-bottom:-.25rem!important}.mb-n2{margin-bottom:-.5rem!important}.mb-n3{margin-bottom:-1rem!important}.mb-n4{margin-bottom:-1.5rem!important}.mb-n5{margin-bottom:-2rem!important}.mb-n6{margin-bottom:-2.5rem!important}.ms-n1{margin-left:-.25rem!important}.ms-n2{margin-left:-.5rem!important}.ms-n3{margin-left:-1rem!important}.ms-n4{margin-left:-1.5rem!important}.ms-n5{margin-left:-2rem!important}.ms-n6{margin-left:-2.5rem!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:2rem!important}.p-6{padding:2.5rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:2rem!important;padding-left:2rem!important}.px-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:2rem!important}.pt-6{padding-top:2.5rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:2rem!important}.pe-6{padding-right:2.5rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:2rem!important}.pb-6{padding-bottom:2.5rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:2rem!important}.ps-6{padding-left:2.5rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:2rem!important}.gap-6{gap:2.5rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:2rem!important}.row-gap-6{row-gap:2.5rem!important}.column-gap-0{column-gap:0!important}.column-gap-1{column-gap:.25rem!important}.column-gap-2{column-gap:.5rem!important}.column-gap-3{column-gap:1rem!important}.column-gap-4{column-gap:1.5rem!important}.column-gap-5{column-gap:2rem!important}.column-gap-6{column-gap:2.5rem!important}.font-monospace{font-family:var(--tblr-font-monospace)!important}.fs-1{font-size:1.5rem!important}.fs-2{font-size:1.25rem!important}.fs-3{font-size:1rem!important}.fs-4{font-size:.875rem!important}.fs-5{font-size:.75rem!important}.fs-6{font-size:.625rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold,.fw-bold{font-weight:600!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.1428571429!important}.lh-base{line-height:1.4285714286!important}.lh-lg{line-height:1.7142857143!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--tblr-text-opacity: 1;color:rgba(var(--tblr-primary-rgb),var(--tblr-text-opacity))!important}.text-secondary{--tblr-text-opacity: 1;color:rgba(var(--tblr-secondary-rgb),var(--tblr-text-opacity))!important}.text-success{--tblr-text-opacity: 1;color:rgba(var(--tblr-success-rgb),var(--tblr-text-opacity))!important}.text-info{--tblr-text-opacity: 1;color:rgba(var(--tblr-info-rgb),var(--tblr-text-opacity))!important}.text-warning{--tblr-text-opacity: 1;color:rgba(var(--tblr-warning-rgb),var(--tblr-text-opacity))!important}.text-danger{--tblr-text-opacity: 1;color:rgba(var(--tblr-danger-rgb),var(--tblr-text-opacity))!important}.text-light{--tblr-text-opacity: 1;color:rgba(var(--tblr-light-rgb),var(--tblr-text-opacity))!important}.text-dark{--tblr-text-opacity: 1;color:rgba(var(--tblr-dark-rgb),var(--tblr-text-opacity))!important}.text-muted{--tblr-text-opacity: 1;color:var(--tblr-secondary-color)!important}.text-blue{--tblr-text-opacity: 1;color:rgba(var(--tblr-blue-rgb),var(--tblr-text-opacity))!important}.text-azure{--tblr-text-opacity: 1;color:rgba(var(--tblr-azure-rgb),var(--tblr-text-opacity))!important}.text-indigo{--tblr-text-opacity: 1;color:rgba(var(--tblr-indigo-rgb),var(--tblr-text-opacity))!important}.text-purple{--tblr-text-opacity: 1;color:rgba(var(--tblr-purple-rgb),var(--tblr-text-opacity))!important}.text-pink{--tblr-text-opacity: 1;color:rgba(var(--tblr-pink-rgb),var(--tblr-text-opacity))!important}.text-red{--tblr-text-opacity: 1;color:rgba(var(--tblr-red-rgb),var(--tblr-text-opacity))!important}.text-orange{--tblr-text-opacity: 1;color:rgba(var(--tblr-orange-rgb),var(--tblr-text-opacity))!important}.text-yellow{--tblr-text-opacity: 1;color:rgba(var(--tblr-yellow-rgb),var(--tblr-text-opacity))!important}.text-lime{--tblr-text-opacity: 1;color:rgba(var(--tblr-lime-rgb),var(--tblr-text-opacity))!important}.text-green{--tblr-text-opacity: 1;color:rgba(var(--tblr-green-rgb),var(--tblr-text-opacity))!important}.text-teal{--tblr-text-opacity: 1;color:rgba(var(--tblr-teal-rgb),var(--tblr-text-opacity))!important}.text-cyan{--tblr-text-opacity: 1;color:rgba(var(--tblr-cyan-rgb),var(--tblr-text-opacity))!important}.text-black{--tblr-text-opacity: 1;color:rgba(var(--tblr-black-rgb),var(--tblr-text-opacity))!important}.text-white{--tblr-text-opacity: 1;color:rgba(var(--tblr-white-rgb),var(--tblr-text-opacity))!important}.text-body{--tblr-text-opacity: 1;color:rgba(var(--tblr-body-color-rgb),var(--tblr-text-opacity))!important}.text-black-50{--tblr-text-opacity: 1;color:#00000080!important}.text-white-50{--tblr-text-opacity: 1;color:#ffffff80!important}.text-body-secondary{--tblr-text-opacity: 1;color:var(--tblr-secondary-color)!important}.text-body-tertiary{--tblr-text-opacity: 1;color:var(--tblr-tertiary-color)!important}.text-body-emphasis{--tblr-text-opacity: 1;color:var(--tblr-emphasis-color)!important}.text-reset{--tblr-text-opacity: 1;color:inherit!important}.text-opacity-25{--tblr-text-opacity: .25}.text-opacity-50{--tblr-text-opacity: .5}.text-opacity-75{--tblr-text-opacity: .75}.text-opacity-100{--tblr-text-opacity: 1}.text-primary-emphasis{color:var(--tblr-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--tblr-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--tblr-success-text-emphasis)!important}.text-info-emphasis{color:var(--tblr-info-text-emphasis)!important}.text-warning-emphasis{color:var(--tblr-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--tblr-danger-text-emphasis)!important}.text-light-emphasis{color:var(--tblr-light-text-emphasis)!important}.text-dark-emphasis{color:var(--tblr-dark-text-emphasis)!important}.link-opacity-10,.link-opacity-10-hover:hover{--tblr-link-opacity: .1}.link-opacity-25,.link-opacity-25-hover:hover{--tblr-link-opacity: .25}.link-opacity-50,.link-opacity-50-hover:hover{--tblr-link-opacity: .5}.link-opacity-75,.link-opacity-75-hover:hover{--tblr-link-opacity: .75}.link-opacity-100,.link-opacity-100-hover:hover{--tblr-link-opacity: 1}.link-offset-1,.link-offset-1-hover:hover{text-underline-offset:.125em!important}.link-offset-2,.link-offset-2-hover:hover{text-underline-offset:.25em!important}.link-offset-3,.link-offset-3-hover:hover{text-underline-offset:.375em!important}.link-underline-primary{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-primary-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-secondary{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-secondary-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-success{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-success-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-info{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-info-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-warning{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-warning-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-danger{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-danger-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-light{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-light-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-dark{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-dark-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-muted{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-muted-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-blue{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-blue-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-azure{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-azure-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-indigo{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-indigo-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-purple{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-purple-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-pink{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-pink-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-red{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-red-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-orange{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-orange-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-yellow{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-yellow-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-lime{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-lime-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-green{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-green-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-teal{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-teal-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-cyan{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-cyan-rgb),var(--tblr-link-underline-opacity))!important}.link-underline{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-link-color-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-underline-opacity-0,.link-underline-opacity-0-hover:hover{--tblr-link-underline-opacity: 0}.link-underline-opacity-10,.link-underline-opacity-10-hover:hover{--tblr-link-underline-opacity: .1}.link-underline-opacity-25,.link-underline-opacity-25-hover:hover{--tblr-link-underline-opacity: .25}.link-underline-opacity-50,.link-underline-opacity-50-hover:hover{--tblr-link-underline-opacity: .5}.link-underline-opacity-75,.link-underline-opacity-75-hover:hover{--tblr-link-underline-opacity: .75}.link-underline-opacity-100,.link-underline-opacity-100-hover:hover{--tblr-link-underline-opacity: 1}.bg-primary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-primary-rgb),var(--tblr-bg-opacity))!important}.bg-secondary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-secondary-rgb),var(--tblr-bg-opacity))!important}.bg-success{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-success-rgb),var(--tblr-bg-opacity))!important}.bg-info{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-info-rgb),var(--tblr-bg-opacity))!important}.bg-warning{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-warning-rgb),var(--tblr-bg-opacity))!important}.bg-danger{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-danger-rgb),var(--tblr-bg-opacity))!important}.bg-light{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-light-rgb),var(--tblr-bg-opacity))!important}.bg-dark{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-dark-rgb),var(--tblr-bg-opacity))!important}.bg-muted{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-muted-rgb),var(--tblr-bg-opacity))!important}.bg-blue{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-blue-rgb),var(--tblr-bg-opacity))!important}.bg-azure{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-azure-rgb),var(--tblr-bg-opacity))!important}.bg-indigo{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-indigo-rgb),var(--tblr-bg-opacity))!important}.bg-purple{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-purple-rgb),var(--tblr-bg-opacity))!important}.bg-pink{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-pink-rgb),var(--tblr-bg-opacity))!important}.bg-red{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-red-rgb),var(--tblr-bg-opacity))!important}.bg-orange{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-orange-rgb),var(--tblr-bg-opacity))!important}.bg-yellow{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-yellow-rgb),var(--tblr-bg-opacity))!important}.bg-lime{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-lime-rgb),var(--tblr-bg-opacity))!important}.bg-green{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-green-rgb),var(--tblr-bg-opacity))!important}.bg-teal{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-teal-rgb),var(--tblr-bg-opacity))!important}.bg-cyan{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-cyan-rgb),var(--tblr-bg-opacity))!important}.bg-black{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-black-rgb),var(--tblr-bg-opacity))!important}.bg-white{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-white-rgb),var(--tblr-bg-opacity))!important}.bg-body{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-body-bg-rgb),var(--tblr-bg-opacity))!important}.bg-transparent{--tblr-bg-opacity: 1;background-color:transparent!important}.bg-body-secondary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-secondary-bg-rgb),var(--tblr-bg-opacity))!important}.bg-body-tertiary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-tertiary-bg-rgb),var(--tblr-bg-opacity))!important}.bg-opacity-10{--tblr-bg-opacity: .1}.bg-opacity-25{--tblr-bg-opacity: .25}.bg-opacity-50{--tblr-bg-opacity: .5}.bg-opacity-75{--tblr-bg-opacity: .75}.bg-opacity-100{--tblr-bg-opacity: 1}.bg-primary-subtle{background-color:var(--tblr-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--tblr-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--tblr-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--tblr-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--tblr-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--tblr-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--tblr-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--tblr-dark-bg-subtle)!important}.bg-gradient{background-image:var(--tblr-gradient)!important}.user-select-all{user-select:all!important}.user-select-auto{user-select:auto!important}.user-select-none{user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--tblr-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--tblr-border-radius-sm)!important}.rounded-2{border-radius:var(--tblr-border-radius)!important}.rounded-3{border-radius:var(--tblr-border-radius-lg)!important}.rounded-4{border-radius:var(--tblr-border-radius-xl)!important}.rounded-5{border-radius:var(--tblr-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--tblr-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--tblr-border-radius)!important;border-top-right-radius:var(--tblr-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--tblr-border-radius-sm)!important;border-top-right-radius:var(--tblr-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--tblr-border-radius)!important;border-top-right-radius:var(--tblr-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--tblr-border-radius-lg)!important;border-top-right-radius:var(--tblr-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--tblr-border-radius-xl)!important;border-top-right-radius:var(--tblr-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--tblr-border-radius-xxl)!important;border-top-right-radius:var(--tblr-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--tblr-border-radius-pill)!important;border-top-right-radius:var(--tblr-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--tblr-border-radius)!important;border-bottom-right-radius:var(--tblr-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--tblr-border-radius-sm)!important;border-bottom-right-radius:var(--tblr-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--tblr-border-radius)!important;border-bottom-right-radius:var(--tblr-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--tblr-border-radius-lg)!important;border-bottom-right-radius:var(--tblr-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--tblr-border-radius-xl)!important;border-bottom-right-radius:var(--tblr-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--tblr-border-radius-xxl)!important;border-bottom-right-radius:var(--tblr-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--tblr-border-radius-pill)!important;border-bottom-right-radius:var(--tblr-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--tblr-border-radius)!important;border-bottom-left-radius:var(--tblr-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--tblr-border-radius-sm)!important;border-bottom-left-radius:var(--tblr-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--tblr-border-radius)!important;border-bottom-left-radius:var(--tblr-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--tblr-border-radius-lg)!important;border-bottom-left-radius:var(--tblr-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--tblr-border-radius-xl)!important;border-bottom-left-radius:var(--tblr-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--tblr-border-radius-xxl)!important;border-bottom-left-radius:var(--tblr-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--tblr-border-radius-pill)!important;border-bottom-left-radius:var(--tblr-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--tblr-border-radius)!important;border-top-left-radius:var(--tblr-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--tblr-border-radius-sm)!important;border-top-left-radius:var(--tblr-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--tblr-border-radius)!important;border-top-left-radius:var(--tblr-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--tblr-border-radius-lg)!important;border-top-left-radius:var(--tblr-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--tblr-border-radius-xl)!important;border-top-left-radius:var(--tblr-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--tblr-border-radius-xxl)!important;border-top-left-radius:var(--tblr-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--tblr-border-radius-pill)!important;border-top-left-radius:var(--tblr-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}.object-contain{object-fit:contain!important}.object-cover{object-fit:cover!important}.object-fill{object-fit:fill!important}.object-scale-down{object-fit:scale-down!important}.object-none{object-fit:none!important}.cursor-auto{cursor:auto!important}.cursor-pointer{cursor:pointer!important}.cursor-move{cursor:move!important}.cursor-not-allowed{cursor:not-allowed!important}.cursor-zoom-in{cursor:zoom-in!important}.cursor-zoom-out{cursor:zoom-out!important}.cursor-default{cursor:default!important}.cursor-none{cursor:none!important}.cursor-help{cursor:help!important}.cursor-progress{cursor:progress!important}.cursor-wait{cursor:wait!important}.cursor-text{cursor:text!important}.cursor-v-text{cursor:vertical-text!important}.cursor-grab{cursor:grab!important}.cursor-grabbing{cursor:grabbing!important}.cursor-crosshair{cursor:crosshair!important}.border-x{border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important;border-right:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-x-wide{border-left:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important;border-right:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-x-0{border-left:0!important;border-right:0!important}.border-y{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important;border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-y-wide{border-top:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important;border-bottom:2px var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.border-y-0{border-top:0!important;border-bottom:0!important}.columns-2{columns:2!important}.columns-3{columns:3!important}.columns-4{columns:4!important}.bg-pattern-transparent{background:url('data:image/svg+xml;charset=UTF-8,') repeat center/16px 16px!important}.bg-gradient{background:linear-gradient(var(--tblr-gradient-direction, to right),var(--tblr-gradient-stops, var(--tblr-gradient-from, transparent), var(--tblr-gradient-to, transparent))) no-repeat!important}.bg-gradient-to-t{--tblr-gradient-direction: to top !important}.bg-gradient-to-te{--tblr-gradient-direction: to top right !important}.bg-gradient-to-e{--tblr-gradient-direction: to right !important}.bg-gradient-to-be{--tblr-gradient-direction: to bottom right !important}.bg-gradient-to-b{--tblr-gradient-direction: to bottom !important}.bg-gradient-to-bs{--tblr-gradient-direction: to bottom left !important}.bg-gradient-to-s{--tblr-gradient-direction: to left !important}.bg-gradient-to-ts{--tblr-gradient-direction: to top left !important}.table-auto{table-layout:auto!important}.table-fixed{table-layout:fixed!important}@media(min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{object-fit:contain!important}.object-fit-sm-cover{object-fit:cover!important}.object-fit-sm-fill{object-fit:fill!important}.object-fit-sm-scale{object-fit:scale-down!important}.object-fit-sm-none{object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:2rem!important}.m-sm-6{margin:2.5rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:2rem!important;margin-left:2rem!important}.mx-sm-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-sm-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:2rem!important}.mt-sm-6{margin-top:2.5rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:2rem!important}.me-sm-6{margin-right:2.5rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:2rem!important}.mb-sm-6{margin-bottom:2.5rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:2rem!important}.ms-sm-6{margin-left:2.5rem!important}.ms-sm-auto{margin-left:auto!important}.m-sm-n1{margin:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.m-sm-n3{margin:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.m-sm-n5{margin:-2rem!important}.m-sm-n6{margin:-2.5rem!important}.mx-sm-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-sm-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-sm-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-sm-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-sm-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-sm-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-sm-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-sm-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-sm-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-sm-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-sm-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-sm-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-sm-n1{margin-top:-.25rem!important}.mt-sm-n2{margin-top:-.5rem!important}.mt-sm-n3{margin-top:-1rem!important}.mt-sm-n4{margin-top:-1.5rem!important}.mt-sm-n5{margin-top:-2rem!important}.mt-sm-n6{margin-top:-2.5rem!important}.me-sm-n1{margin-right:-.25rem!important}.me-sm-n2{margin-right:-.5rem!important}.me-sm-n3{margin-right:-1rem!important}.me-sm-n4{margin-right:-1.5rem!important}.me-sm-n5{margin-right:-2rem!important}.me-sm-n6{margin-right:-2.5rem!important}.mb-sm-n1{margin-bottom:-.25rem!important}.mb-sm-n2{margin-bottom:-.5rem!important}.mb-sm-n3{margin-bottom:-1rem!important}.mb-sm-n4{margin-bottom:-1.5rem!important}.mb-sm-n5{margin-bottom:-2rem!important}.mb-sm-n6{margin-bottom:-2.5rem!important}.ms-sm-n1{margin-left:-.25rem!important}.ms-sm-n2{margin-left:-.5rem!important}.ms-sm-n3{margin-left:-1rem!important}.ms-sm-n4{margin-left:-1.5rem!important}.ms-sm-n5{margin-left:-2rem!important}.ms-sm-n6{margin-left:-2.5rem!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:2rem!important}.p-sm-6{padding:2.5rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:2rem!important;padding-left:2rem!important}.px-sm-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-sm-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:2rem!important}.pt-sm-6{padding-top:2.5rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:2rem!important}.pe-sm-6{padding-right:2.5rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:2rem!important}.pb-sm-6{padding-bottom:2.5rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:2rem!important}.ps-sm-6{padding-left:2.5rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:2rem!important}.gap-sm-6{gap:2.5rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:2rem!important}.row-gap-sm-6{row-gap:2.5rem!important}.column-gap-sm-0{column-gap:0!important}.column-gap-sm-1{column-gap:.25rem!important}.column-gap-sm-2{column-gap:.5rem!important}.column-gap-sm-3{column-gap:1rem!important}.column-gap-sm-4{column-gap:1.5rem!important}.column-gap-sm-5{column-gap:2rem!important}.column-gap-sm-6{column-gap:2.5rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}.columns-sm-2{columns:2!important}.columns-sm-3{columns:3!important}.columns-sm-4{columns:4!important}}@media(min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{object-fit:contain!important}.object-fit-md-cover{object-fit:cover!important}.object-fit-md-fill{object-fit:fill!important}.object-fit-md-scale{object-fit:scale-down!important}.object-fit-md-none{object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:2rem!important}.m-md-6{margin:2.5rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:2rem!important;margin-left:2rem!important}.mx-md-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-md-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:2rem!important}.mt-md-6{margin-top:2.5rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:2rem!important}.me-md-6{margin-right:2.5rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:2rem!important}.mb-md-6{margin-bottom:2.5rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:2rem!important}.ms-md-6{margin-left:2.5rem!important}.ms-md-auto{margin-left:auto!important}.m-md-n1{margin:-.25rem!important}.m-md-n2{margin:-.5rem!important}.m-md-n3{margin:-1rem!important}.m-md-n4{margin:-1.5rem!important}.m-md-n5{margin:-2rem!important}.m-md-n6{margin:-2.5rem!important}.mx-md-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-md-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-md-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-md-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-md-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-md-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-md-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-md-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-md-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-md-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-md-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-md-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-md-n1{margin-top:-.25rem!important}.mt-md-n2{margin-top:-.5rem!important}.mt-md-n3{margin-top:-1rem!important}.mt-md-n4{margin-top:-1.5rem!important}.mt-md-n5{margin-top:-2rem!important}.mt-md-n6{margin-top:-2.5rem!important}.me-md-n1{margin-right:-.25rem!important}.me-md-n2{margin-right:-.5rem!important}.me-md-n3{margin-right:-1rem!important}.me-md-n4{margin-right:-1.5rem!important}.me-md-n5{margin-right:-2rem!important}.me-md-n6{margin-right:-2.5rem!important}.mb-md-n1{margin-bottom:-.25rem!important}.mb-md-n2{margin-bottom:-.5rem!important}.mb-md-n3{margin-bottom:-1rem!important}.mb-md-n4{margin-bottom:-1.5rem!important}.mb-md-n5{margin-bottom:-2rem!important}.mb-md-n6{margin-bottom:-2.5rem!important}.ms-md-n1{margin-left:-.25rem!important}.ms-md-n2{margin-left:-.5rem!important}.ms-md-n3{margin-left:-1rem!important}.ms-md-n4{margin-left:-1.5rem!important}.ms-md-n5{margin-left:-2rem!important}.ms-md-n6{margin-left:-2.5rem!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:2rem!important}.p-md-6{padding:2.5rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:2rem!important;padding-left:2rem!important}.px-md-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-md-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:2rem!important}.pt-md-6{padding-top:2.5rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:2rem!important}.pe-md-6{padding-right:2.5rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:2rem!important}.pb-md-6{padding-bottom:2.5rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:2rem!important}.ps-md-6{padding-left:2.5rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:2rem!important}.gap-md-6{gap:2.5rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:2rem!important}.row-gap-md-6{row-gap:2.5rem!important}.column-gap-md-0{column-gap:0!important}.column-gap-md-1{column-gap:.25rem!important}.column-gap-md-2{column-gap:.5rem!important}.column-gap-md-3{column-gap:1rem!important}.column-gap-md-4{column-gap:1.5rem!important}.column-gap-md-5{column-gap:2rem!important}.column-gap-md-6{column-gap:2.5rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}.columns-md-2{columns:2!important}.columns-md-3{columns:3!important}.columns-md-4{columns:4!important}}@media(min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{object-fit:contain!important}.object-fit-lg-cover{object-fit:cover!important}.object-fit-lg-fill{object-fit:fill!important}.object-fit-lg-scale{object-fit:scale-down!important}.object-fit-lg-none{object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:2rem!important}.m-lg-6{margin:2.5rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:2rem!important;margin-left:2rem!important}.mx-lg-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-lg-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:2rem!important}.mt-lg-6{margin-top:2.5rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:2rem!important}.me-lg-6{margin-right:2.5rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:2rem!important}.mb-lg-6{margin-bottom:2.5rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:2rem!important}.ms-lg-6{margin-left:2.5rem!important}.ms-lg-auto{margin-left:auto!important}.m-lg-n1{margin:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.m-lg-n3{margin:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.m-lg-n5{margin:-2rem!important}.m-lg-n6{margin:-2.5rem!important}.mx-lg-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-lg-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-lg-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-lg-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-lg-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-lg-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-lg-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-lg-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-lg-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-lg-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-lg-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-lg-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-lg-n1{margin-top:-.25rem!important}.mt-lg-n2{margin-top:-.5rem!important}.mt-lg-n3{margin-top:-1rem!important}.mt-lg-n4{margin-top:-1.5rem!important}.mt-lg-n5{margin-top:-2rem!important}.mt-lg-n6{margin-top:-2.5rem!important}.me-lg-n1{margin-right:-.25rem!important}.me-lg-n2{margin-right:-.5rem!important}.me-lg-n3{margin-right:-1rem!important}.me-lg-n4{margin-right:-1.5rem!important}.me-lg-n5{margin-right:-2rem!important}.me-lg-n6{margin-right:-2.5rem!important}.mb-lg-n1{margin-bottom:-.25rem!important}.mb-lg-n2{margin-bottom:-.5rem!important}.mb-lg-n3{margin-bottom:-1rem!important}.mb-lg-n4{margin-bottom:-1.5rem!important}.mb-lg-n5{margin-bottom:-2rem!important}.mb-lg-n6{margin-bottom:-2.5rem!important}.ms-lg-n1{margin-left:-.25rem!important}.ms-lg-n2{margin-left:-.5rem!important}.ms-lg-n3{margin-left:-1rem!important}.ms-lg-n4{margin-left:-1.5rem!important}.ms-lg-n5{margin-left:-2rem!important}.ms-lg-n6{margin-left:-2.5rem!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:2rem!important}.p-lg-6{padding:2.5rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:2rem!important;padding-left:2rem!important}.px-lg-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-lg-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:2rem!important}.pt-lg-6{padding-top:2.5rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:2rem!important}.pe-lg-6{padding-right:2.5rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:2rem!important}.pb-lg-6{padding-bottom:2.5rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:2rem!important}.ps-lg-6{padding-left:2.5rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:2rem!important}.gap-lg-6{gap:2.5rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:2rem!important}.row-gap-lg-6{row-gap:2.5rem!important}.column-gap-lg-0{column-gap:0!important}.column-gap-lg-1{column-gap:.25rem!important}.column-gap-lg-2{column-gap:.5rem!important}.column-gap-lg-3{column-gap:1rem!important}.column-gap-lg-4{column-gap:1.5rem!important}.column-gap-lg-5{column-gap:2rem!important}.column-gap-lg-6{column-gap:2.5rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}.columns-lg-2{columns:2!important}.columns-lg-3{columns:3!important}.columns-lg-4{columns:4!important}}@media(min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{object-fit:contain!important}.object-fit-xl-cover{object-fit:cover!important}.object-fit-xl-fill{object-fit:fill!important}.object-fit-xl-scale{object-fit:scale-down!important}.object-fit-xl-none{object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:2rem!important}.m-xl-6{margin:2.5rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:2rem!important;margin-left:2rem!important}.mx-xl-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-xl-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:2rem!important}.mt-xl-6{margin-top:2.5rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:2rem!important}.me-xl-6{margin-right:2.5rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:2rem!important}.mb-xl-6{margin-bottom:2.5rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:2rem!important}.ms-xl-6{margin-left:2.5rem!important}.ms-xl-auto{margin-left:auto!important}.m-xl-n1{margin:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.m-xl-n3{margin:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.m-xl-n5{margin:-2rem!important}.m-xl-n6{margin:-2.5rem!important}.mx-xl-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-xl-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-xl-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-xl-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-xl-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-xl-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-xl-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-xl-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-xl-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-xl-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-xl-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-xl-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-xl-n1{margin-top:-.25rem!important}.mt-xl-n2{margin-top:-.5rem!important}.mt-xl-n3{margin-top:-1rem!important}.mt-xl-n4{margin-top:-1.5rem!important}.mt-xl-n5{margin-top:-2rem!important}.mt-xl-n6{margin-top:-2.5rem!important}.me-xl-n1{margin-right:-.25rem!important}.me-xl-n2{margin-right:-.5rem!important}.me-xl-n3{margin-right:-1rem!important}.me-xl-n4{margin-right:-1.5rem!important}.me-xl-n5{margin-right:-2rem!important}.me-xl-n6{margin-right:-2.5rem!important}.mb-xl-n1{margin-bottom:-.25rem!important}.mb-xl-n2{margin-bottom:-.5rem!important}.mb-xl-n3{margin-bottom:-1rem!important}.mb-xl-n4{margin-bottom:-1.5rem!important}.mb-xl-n5{margin-bottom:-2rem!important}.mb-xl-n6{margin-bottom:-2.5rem!important}.ms-xl-n1{margin-left:-.25rem!important}.ms-xl-n2{margin-left:-.5rem!important}.ms-xl-n3{margin-left:-1rem!important}.ms-xl-n4{margin-left:-1.5rem!important}.ms-xl-n5{margin-left:-2rem!important}.ms-xl-n6{margin-left:-2.5rem!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:2rem!important}.p-xl-6{padding:2.5rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:2rem!important;padding-left:2rem!important}.px-xl-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-xl-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:2rem!important}.pt-xl-6{padding-top:2.5rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:2rem!important}.pe-xl-6{padding-right:2.5rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:2rem!important}.pb-xl-6{padding-bottom:2.5rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:2rem!important}.ps-xl-6{padding-left:2.5rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:2rem!important}.gap-xl-6{gap:2.5rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:2rem!important}.row-gap-xl-6{row-gap:2.5rem!important}.column-gap-xl-0{column-gap:0!important}.column-gap-xl-1{column-gap:.25rem!important}.column-gap-xl-2{column-gap:.5rem!important}.column-gap-xl-3{column-gap:1rem!important}.column-gap-xl-4{column-gap:1.5rem!important}.column-gap-xl-5{column-gap:2rem!important}.column-gap-xl-6{column-gap:2.5rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}.columns-xl-2{columns:2!important}.columns-xl-3{columns:3!important}.columns-xl-4{columns:4!important}}@media(min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{object-fit:contain!important}.object-fit-xxl-cover{object-fit:cover!important}.object-fit-xxl-fill{object-fit:fill!important}.object-fit-xxl-scale{object-fit:scale-down!important}.object-fit-xxl-none{object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:2rem!important}.m-xxl-6{margin:2.5rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:2rem!important;margin-left:2rem!important}.mx-xxl-6{margin-right:2.5rem!important;margin-left:2.5rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:2rem!important;margin-bottom:2rem!important}.my-xxl-6{margin-top:2.5rem!important;margin-bottom:2.5rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:2rem!important}.mt-xxl-6{margin-top:2.5rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:2rem!important}.me-xxl-6{margin-right:2.5rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:2rem!important}.mb-xxl-6{margin-bottom:2.5rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:2rem!important}.ms-xxl-6{margin-left:2.5rem!important}.ms-xxl-auto{margin-left:auto!important}.m-xxl-n1{margin:-.25rem!important}.m-xxl-n2{margin:-.5rem!important}.m-xxl-n3{margin:-1rem!important}.m-xxl-n4{margin:-1.5rem!important}.m-xxl-n5{margin:-2rem!important}.m-xxl-n6{margin:-2.5rem!important}.mx-xxl-n1{margin-right:-.25rem!important;margin-left:-.25rem!important}.mx-xxl-n2{margin-right:-.5rem!important;margin-left:-.5rem!important}.mx-xxl-n3{margin-right:-1rem!important;margin-left:-1rem!important}.mx-xxl-n4{margin-right:-1.5rem!important;margin-left:-1.5rem!important}.mx-xxl-n5{margin-right:-2rem!important;margin-left:-2rem!important}.mx-xxl-n6{margin-right:-2.5rem!important;margin-left:-2.5rem!important}.my-xxl-n1{margin-top:-.25rem!important;margin-bottom:-.25rem!important}.my-xxl-n2{margin-top:-.5rem!important;margin-bottom:-.5rem!important}.my-xxl-n3{margin-top:-1rem!important;margin-bottom:-1rem!important}.my-xxl-n4{margin-top:-1.5rem!important;margin-bottom:-1.5rem!important}.my-xxl-n5{margin-top:-2rem!important;margin-bottom:-2rem!important}.my-xxl-n6{margin-top:-2.5rem!important;margin-bottom:-2.5rem!important}.mt-xxl-n1{margin-top:-.25rem!important}.mt-xxl-n2{margin-top:-.5rem!important}.mt-xxl-n3{margin-top:-1rem!important}.mt-xxl-n4{margin-top:-1.5rem!important}.mt-xxl-n5{margin-top:-2rem!important}.mt-xxl-n6{margin-top:-2.5rem!important}.me-xxl-n1{margin-right:-.25rem!important}.me-xxl-n2{margin-right:-.5rem!important}.me-xxl-n3{margin-right:-1rem!important}.me-xxl-n4{margin-right:-1.5rem!important}.me-xxl-n5{margin-right:-2rem!important}.me-xxl-n6{margin-right:-2.5rem!important}.mb-xxl-n1{margin-bottom:-.25rem!important}.mb-xxl-n2{margin-bottom:-.5rem!important}.mb-xxl-n3{margin-bottom:-1rem!important}.mb-xxl-n4{margin-bottom:-1.5rem!important}.mb-xxl-n5{margin-bottom:-2rem!important}.mb-xxl-n6{margin-bottom:-2.5rem!important}.ms-xxl-n1{margin-left:-.25rem!important}.ms-xxl-n2{margin-left:-.5rem!important}.ms-xxl-n3{margin-left:-1rem!important}.ms-xxl-n4{margin-left:-1.5rem!important}.ms-xxl-n5{margin-left:-2rem!important}.ms-xxl-n6{margin-left:-2.5rem!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:2rem!important}.p-xxl-6{padding:2.5rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:2rem!important;padding-left:2rem!important}.px-xxl-6{padding-right:2.5rem!important;padding-left:2.5rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:2rem!important;padding-bottom:2rem!important}.py-xxl-6{padding-top:2.5rem!important;padding-bottom:2.5rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:2rem!important}.pt-xxl-6{padding-top:2.5rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:2rem!important}.pe-xxl-6{padding-right:2.5rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:2rem!important}.pb-xxl-6{padding-bottom:2.5rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:2rem!important}.ps-xxl-6{padding-left:2.5rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:2rem!important}.gap-xxl-6{gap:2.5rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:2rem!important}.row-gap-xxl-6{row-gap:2.5rem!important}.column-gap-xxl-0{column-gap:0!important}.column-gap-xxl-1{column-gap:.25rem!important}.column-gap-xxl-2{column-gap:.5rem!important}.column-gap-xxl-3{column-gap:1rem!important}.column-gap-xxl-4{column-gap:1.5rem!important}.column-gap-xxl-5{column-gap:2rem!important}.column-gap-xxl-6{column-gap:2.5rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}.columns-xxl-2{columns:2!important}.columns-xxl-3{columns:3!important}.columns-xxl-4{columns:4!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}}:root,:host{--tblr-font-monospace: Roboto Mono;--tblr-font-sans-serif: Inter, system-ui, sans-serif;--tblr-font-serif: Georgia, Times New Roman, times, serif;--tblr-font-comic: Comic Sans MS, Comic Sans, Chalkboard SE, Comic Neue, sans-serif, cursive;--tblr-gray-50: #f9fafb;--tblr-gray-100: #f3f4f6;--tblr-gray-200: #e5e7eb;--tblr-gray-300: #d1d5db;--tblr-gray-400: #9ca3af;--tblr-gray-500: #6b7280;--tblr-gray-600: #4b5563;--tblr-gray-700: #374151;--tblr-gray-800: #1f2937;--tblr-gray-900: #111827;--tblr-gray-950: #030712;--tblr-white: #ffffff;--tblr-black: #000000;--tblr-brand: #00857D;--tblr-primary: #00857D;--tblr-primary-rgb: 0, 133, 125;--tblr-primary-fg: var(--tblr-light);--tblr-primary-darken: rgb(0, 119.7, 112.5);--tblr-primary-darken: color-mix(in oklab, var(--tblr-primary), transparent 20%);--tblr-primary-lt: rgb(229.5, 242.8, 242);--tblr-primary-lt: color-mix(in oklab, var(--tblr-primary) 10%, transparent);--tblr-primary-200: color-mix(in oklab, var(--tblr-primary) 20%, transparent);--tblr-primary-lt-rgb: 230, 243, 242;--tblr-secondary: #6b7280;--tblr-secondary-rgb: 107, 114, 128;--tblr-secondary-fg: var(--tblr-light);--tblr-secondary-darken: rgb(96.3, 102.6, 115.2);--tblr-secondary-darken: color-mix(in oklab, var(--tblr-secondary), transparent 20%);--tblr-secondary-lt: rgb(240.2, 240.9, 242.3);--tblr-secondary-lt: color-mix(in oklab, var(--tblr-secondary) 10%, transparent);--tblr-secondary-200: color-mix(in oklab, var(--tblr-secondary) 20%, transparent);--tblr-secondary-lt-rgb: 240, 241, 242;--tblr-success: #2fb344;--tblr-success-rgb: 47, 179, 68;--tblr-success-fg: var(--tblr-light);--tblr-success-darken: rgb(42.3, 161.1, 61.2);--tblr-success-darken: color-mix(in oklab, var(--tblr-success), transparent 20%);--tblr-success-lt: rgb(234.2, 247.4, 236.3);--tblr-success-lt: color-mix(in oklab, var(--tblr-success) 10%, transparent);--tblr-success-200: color-mix(in oklab, var(--tblr-success) 20%, transparent);--tblr-success-lt-rgb: 234, 247, 236;--tblr-info: #4299e1;--tblr-info-rgb: 66, 153, 225;--tblr-info-fg: var(--tblr-light);--tblr-info-darken: rgb(59.4, 137.7, 202.5);--tblr-info-darken: color-mix(in oklab, var(--tblr-info), transparent 20%);--tblr-info-lt: rgb(236.1, 244.8, 252);--tblr-info-lt: color-mix(in oklab, var(--tblr-info) 10%, transparent);--tblr-info-200: color-mix(in oklab, var(--tblr-info) 20%, transparent);--tblr-info-lt-rgb: 236, 245, 252;--tblr-warning: #f59f00;--tblr-warning-rgb: 245, 159, 0;--tblr-warning-fg: var(--tblr-light);--tblr-warning-darken: rgb(220.5, 143.1, 0);--tblr-warning-darken: color-mix(in oklab, var(--tblr-warning), transparent 20%);--tblr-warning-lt: rgb(254, 245.4, 229.5);--tblr-warning-lt: color-mix(in oklab, var(--tblr-warning) 10%, transparent);--tblr-warning-200: color-mix(in oklab, var(--tblr-warning) 20%, transparent);--tblr-warning-lt-rgb: 254, 245, 230;--tblr-danger: #d63939;--tblr-danger-rgb: 214, 57, 57;--tblr-danger-fg: var(--tblr-light);--tblr-danger-darken: rgb(192.6, 51.3, 51.3);--tblr-danger-darken: color-mix(in oklab, var(--tblr-danger), transparent 20%);--tblr-danger-lt: rgb(250.9, 235.2, 235.2);--tblr-danger-lt: color-mix(in oklab, var(--tblr-danger) 10%, transparent);--tblr-danger-200: color-mix(in oklab, var(--tblr-danger) 20%, transparent);--tblr-danger-lt-rgb: 251, 235, 235;--tblr-light: #f9fafb;--tblr-light-rgb: 249, 250, 251;--tblr-light-fg: var(--tblr-dark);--tblr-light-darken: rgb(224.1, 225, 225.9);--tblr-light-darken: color-mix(in oklab, var(--tblr-light), transparent 20%);--tblr-light-lt: rgb(254.4, 254.5, 254.6);--tblr-light-lt: color-mix(in oklab, var(--tblr-light) 10%, transparent);--tblr-light-200: color-mix(in oklab, var(--tblr-light) 20%, transparent);--tblr-light-lt-rgb: 254, 255, 255;--tblr-dark: #1f2937;--tblr-dark-rgb: 31, 41, 55;--tblr-dark-fg: var(--tblr-light);--tblr-dark-darken: rgb(27.9, 36.9, 49.5);--tblr-dark-darken: color-mix(in oklab, var(--tblr-dark), transparent 20%);--tblr-dark-lt: rgb(232.6, 233.6, 235);--tblr-dark-lt: color-mix(in oklab, var(--tblr-dark) 10%, transparent);--tblr-dark-200: color-mix(in oklab, var(--tblr-dark) 20%, transparent);--tblr-dark-lt-rgb: 233, 234, 235;--tblr-muted: #6b7280;--tblr-muted-rgb: 107, 114, 128;--tblr-muted-fg: var(--tblr-light);--tblr-muted-darken: rgb(96.3, 102.6, 115.2);--tblr-muted-darken: color-mix(in oklab, var(--tblr-muted), transparent 20%);--tblr-muted-lt: rgb(240.2, 240.9, 242.3);--tblr-muted-lt: color-mix(in oklab, var(--tblr-muted) 10%, transparent);--tblr-muted-200: color-mix(in oklab, var(--tblr-muted) 20%, transparent);--tblr-muted-lt-rgb: 240, 241, 242;--tblr-blue: #066fd1;--tblr-blue-rgb: 6, 111, 209;--tblr-blue-fg: var(--tblr-light);--tblr-blue-darken: rgb(5.4, 99.9, 188.1);--tblr-blue-darken: color-mix(in oklab, var(--tblr-blue), transparent 20%);--tblr-blue-lt: rgb(230.1, 240.6, 250.4);--tblr-blue-lt: color-mix(in oklab, var(--tblr-blue) 10%, transparent);--tblr-blue-200: color-mix(in oklab, var(--tblr-blue) 20%, transparent);--tblr-blue-lt-rgb: 230, 241, 250;--tblr-azure: #4299e1;--tblr-azure-rgb: 66, 153, 225;--tblr-azure-fg: var(--tblr-light);--tblr-azure-darken: rgb(59.4, 137.7, 202.5);--tblr-azure-darken: color-mix(in oklab, var(--tblr-azure), transparent 20%);--tblr-azure-lt: rgb(236.1, 244.8, 252);--tblr-azure-lt: color-mix(in oklab, var(--tblr-azure) 10%, transparent);--tblr-azure-200: color-mix(in oklab, var(--tblr-azure) 20%, transparent);--tblr-azure-lt-rgb: 236, 245, 252;--tblr-indigo: #4263eb;--tblr-indigo-rgb: 66, 99, 235;--tblr-indigo-fg: var(--tblr-light);--tblr-indigo-darken: rgb(59.4, 89.1, 211.5);--tblr-indigo-darken: color-mix(in oklab, var(--tblr-indigo), transparent 20%);--tblr-indigo-lt: rgb(236.1, 239.4, 253);--tblr-indigo-lt: color-mix(in oklab, var(--tblr-indigo) 10%, transparent);--tblr-indigo-200: color-mix(in oklab, var(--tblr-indigo) 20%, transparent);--tblr-indigo-lt-rgb: 236, 239, 253;--tblr-purple: #ae3ec9;--tblr-purple-rgb: 174, 62, 201;--tblr-purple-fg: var(--tblr-light);--tblr-purple-darken: rgb(156.6, 55.8, 180.9);--tblr-purple-darken: color-mix(in oklab, var(--tblr-purple), transparent 20%);--tblr-purple-lt: rgb(246.9, 235.7, 249.6);--tblr-purple-lt: color-mix(in oklab, var(--tblr-purple) 10%, transparent);--tblr-purple-200: color-mix(in oklab, var(--tblr-purple) 20%, transparent);--tblr-purple-lt-rgb: 247, 236, 250;--tblr-pink: #d6336c;--tblr-pink-rgb: 214, 51, 108;--tblr-pink-fg: var(--tblr-light);--tblr-pink-darken: rgb(192.6, 45.9, 97.2);--tblr-pink-darken: color-mix(in oklab, var(--tblr-pink), transparent 20%);--tblr-pink-lt: rgb(250.9, 234.6, 240.3);--tblr-pink-lt: color-mix(in oklab, var(--tblr-pink) 10%, transparent);--tblr-pink-200: color-mix(in oklab, var(--tblr-pink) 20%, transparent);--tblr-pink-lt-rgb: 251, 235, 240;--tblr-red: #d63939;--tblr-red-rgb: 214, 57, 57;--tblr-red-fg: var(--tblr-light);--tblr-red-darken: rgb(192.6, 51.3, 51.3);--tblr-red-darken: color-mix(in oklab, var(--tblr-red), transparent 20%);--tblr-red-lt: rgb(250.9, 235.2, 235.2);--tblr-red-lt: color-mix(in oklab, var(--tblr-red) 10%, transparent);--tblr-red-200: color-mix(in oklab, var(--tblr-red) 20%, transparent);--tblr-red-lt-rgb: 251, 235, 235;--tblr-orange: #f76707;--tblr-orange-rgb: 247, 103, 7;--tblr-orange-fg: var(--tblr-light);--tblr-orange-darken: rgb(222.3, 92.7, 6.3);--tblr-orange-darken: color-mix(in oklab, var(--tblr-orange), transparent 20%);--tblr-orange-lt: rgb(254.2, 239.8, 230.2);--tblr-orange-lt: color-mix(in oklab, var(--tblr-orange) 10%, transparent);--tblr-orange-200: color-mix(in oklab, var(--tblr-orange) 20%, transparent);--tblr-orange-lt-rgb: 254, 240, 230;--tblr-yellow: #f59f00;--tblr-yellow-rgb: 245, 159, 0;--tblr-yellow-fg: var(--tblr-light);--tblr-yellow-darken: rgb(220.5, 143.1, 0);--tblr-yellow-darken: color-mix(in oklab, var(--tblr-yellow), transparent 20%);--tblr-yellow-lt: rgb(254, 245.4, 229.5);--tblr-yellow-lt: color-mix(in oklab, var(--tblr-yellow) 10%, transparent);--tblr-yellow-200: color-mix(in oklab, var(--tblr-yellow) 20%, transparent);--tblr-yellow-lt-rgb: 254, 245, 230;--tblr-lime: #74b816;--tblr-lime-rgb: 116, 184, 22;--tblr-lime-fg: var(--tblr-light);--tblr-lime-darken: rgb(104.4, 165.6, 19.8);--tblr-lime-darken: color-mix(in oklab, var(--tblr-lime), transparent 20%);--tblr-lime-lt: rgb(241.1, 247.9, 231.7);--tblr-lime-lt: color-mix(in oklab, var(--tblr-lime) 10%, transparent);--tblr-lime-200: color-mix(in oklab, var(--tblr-lime) 20%, transparent);--tblr-lime-lt-rgb: 241, 248, 232;--tblr-green: #2fb344;--tblr-green-rgb: 47, 179, 68;--tblr-green-fg: var(--tblr-light);--tblr-green-darken: rgb(42.3, 161.1, 61.2);--tblr-green-darken: color-mix(in oklab, var(--tblr-green), transparent 20%);--tblr-green-lt: rgb(234.2, 247.4, 236.3);--tblr-green-lt: color-mix(in oklab, var(--tblr-green) 10%, transparent);--tblr-green-200: color-mix(in oklab, var(--tblr-green) 20%, transparent);--tblr-green-lt-rgb: 234, 247, 236;--tblr-teal: #0ca678;--tblr-teal-rgb: 12, 166, 120;--tblr-teal-fg: var(--tblr-light);--tblr-teal-darken: rgb(10.8, 149.4, 108);--tblr-teal-darken: color-mix(in oklab, var(--tblr-teal), transparent 20%);--tblr-teal-lt: rgb(230.7, 246.1, 241.5);--tblr-teal-lt: color-mix(in oklab, var(--tblr-teal) 10%, transparent);--tblr-teal-200: color-mix(in oklab, var(--tblr-teal) 20%, transparent);--tblr-teal-lt-rgb: 231, 246, 242;--tblr-cyan: #17a2b8;--tblr-cyan-rgb: 23, 162, 184;--tblr-cyan-fg: var(--tblr-light);--tblr-cyan-darken: rgb(20.7, 145.8, 165.6);--tblr-cyan-darken: color-mix(in oklab, var(--tblr-cyan), transparent 20%);--tblr-cyan-lt: rgb(231.8, 245.7, 247.9);--tblr-cyan-lt: color-mix(in oklab, var(--tblr-cyan) 10%, transparent);--tblr-cyan-200: color-mix(in oklab, var(--tblr-cyan) 20%, transparent);--tblr-cyan-lt-rgb: 232, 246, 248;--tblr-x: #000000;--tblr-x-rgb: 0, 0, 0;--tblr-x-fg: var(--tblr-light);--tblr-x-darken: black;--tblr-x-darken: color-mix(in oklab, var(--tblr-x), transparent 20%);--tblr-x-lt: rgb(229.5, 229.5, 229.5);--tblr-x-lt: color-mix(in oklab, var(--tblr-x) 10%, transparent);--tblr-x-200: color-mix(in oklab, var(--tblr-x) 20%, transparent);--tblr-x-lt-rgb: 230, 230, 230;--tblr-facebook: #1877f2;--tblr-facebook-rgb: 24, 119, 242;--tblr-facebook-fg: var(--tblr-light);--tblr-facebook-darken: rgb(21.6, 107.1, 217.8);--tblr-facebook-darken: color-mix(in oklab, var(--tblr-facebook), transparent 20%);--tblr-facebook-lt: rgb(231.9, 241.4, 253.7);--tblr-facebook-lt: color-mix(in oklab, var(--tblr-facebook) 10%, transparent);--tblr-facebook-200: color-mix(in oklab, var(--tblr-facebook) 20%, transparent);--tblr-facebook-lt-rgb: 232, 241, 254;--tblr-twitter: #1da1f2;--tblr-twitter-rgb: 29, 161, 242;--tblr-twitter-fg: var(--tblr-light);--tblr-twitter-darken: rgb(26.1, 144.9, 217.8);--tblr-twitter-darken: color-mix(in oklab, var(--tblr-twitter), transparent 20%);--tblr-twitter-lt: rgb(232.4, 245.6, 253.7);--tblr-twitter-lt: color-mix(in oklab, var(--tblr-twitter) 10%, transparent);--tblr-twitter-200: color-mix(in oklab, var(--tblr-twitter) 20%, transparent);--tblr-twitter-lt-rgb: 232, 246, 254;--tblr-linkedin: #0a66c2;--tblr-linkedin-rgb: 10, 102, 194;--tblr-linkedin-fg: var(--tblr-light);--tblr-linkedin-darken: rgb(9, 91.8, 174.6);--tblr-linkedin-darken: color-mix(in oklab, var(--tblr-linkedin), transparent 20%);--tblr-linkedin-lt: rgb(230.5, 239.7, 248.9);--tblr-linkedin-lt: color-mix(in oklab, var(--tblr-linkedin) 10%, transparent);--tblr-linkedin-200: color-mix(in oklab, var(--tblr-linkedin) 20%, transparent);--tblr-linkedin-lt-rgb: 231, 240, 249;--tblr-google: #dc4e41;--tblr-google-rgb: 220, 78, 65;--tblr-google-fg: var(--tblr-light);--tblr-google-darken: rgb(198, 70.2, 58.5);--tblr-google-darken: color-mix(in oklab, var(--tblr-google), transparent 20%);--tblr-google-lt: rgb(251.5, 237.3, 236);--tblr-google-lt: color-mix(in oklab, var(--tblr-google) 10%, transparent);--tblr-google-200: color-mix(in oklab, var(--tblr-google) 20%, transparent);--tblr-google-lt-rgb: 252, 237, 236;--tblr-youtube: #ff0000;--tblr-youtube-rgb: 255, 0, 0;--tblr-youtube-fg: var(--tblr-light);--tblr-youtube-darken: rgb(229.5, 0, 0);--tblr-youtube-darken: color-mix(in oklab, var(--tblr-youtube), transparent 20%);--tblr-youtube-lt: rgb(255, 229.5, 229.5);--tblr-youtube-lt: color-mix(in oklab, var(--tblr-youtube) 10%, transparent);--tblr-youtube-200: color-mix(in oklab, var(--tblr-youtube) 20%, transparent);--tblr-youtube-lt-rgb: 255, 230, 230;--tblr-vimeo: #1ab7ea;--tblr-vimeo-rgb: 26, 183, 234;--tblr-vimeo-fg: var(--tblr-light);--tblr-vimeo-darken: rgb(23.4, 164.7, 210.6);--tblr-vimeo-darken: color-mix(in oklab, var(--tblr-vimeo), transparent 20%);--tblr-vimeo-lt: rgb(232.1, 247.8, 252.9);--tblr-vimeo-lt: color-mix(in oklab, var(--tblr-vimeo) 10%, transparent);--tblr-vimeo-200: color-mix(in oklab, var(--tblr-vimeo) 20%, transparent);--tblr-vimeo-lt-rgb: 232, 248, 253;--tblr-dribbble: #ea4c89;--tblr-dribbble-rgb: 234, 76, 137;--tblr-dribbble-fg: var(--tblr-light);--tblr-dribbble-darken: rgb(210.6, 68.4, 123.3);--tblr-dribbble-darken: color-mix(in oklab, var(--tblr-dribbble), transparent 20%);--tblr-dribbble-lt: rgb(252.9, 237.1, 243.2);--tblr-dribbble-lt: color-mix(in oklab, var(--tblr-dribbble) 10%, transparent);--tblr-dribbble-200: color-mix(in oklab, var(--tblr-dribbble) 20%, transparent);--tblr-dribbble-lt-rgb: 253, 237, 243;--tblr-github: #181717;--tblr-github-rgb: 24, 23, 23;--tblr-github-fg: var(--tblr-light);--tblr-github-darken: rgb(21.6, 20.7, 20.7);--tblr-github-darken: color-mix(in oklab, var(--tblr-github), transparent 20%);--tblr-github-lt: rgb(231.9, 231.8, 231.8);--tblr-github-lt: color-mix(in oklab, var(--tblr-github) 10%, transparent);--tblr-github-200: color-mix(in oklab, var(--tblr-github) 20%, transparent);--tblr-github-lt-rgb: 232, 232, 232;--tblr-instagram: #e4405f;--tblr-instagram-rgb: 228, 64, 95;--tblr-instagram-fg: var(--tblr-light);--tblr-instagram-darken: rgb(205.2, 57.6, 85.5);--tblr-instagram-darken: color-mix(in oklab, var(--tblr-instagram), transparent 20%);--tblr-instagram-lt: rgb(252.3, 235.9, 239);--tblr-instagram-lt: color-mix(in oklab, var(--tblr-instagram) 10%, transparent);--tblr-instagram-200: color-mix(in oklab, var(--tblr-instagram) 20%, transparent);--tblr-instagram-lt-rgb: 252, 236, 239;--tblr-pinterest: #bd081c;--tblr-pinterest-rgb: 189, 8, 28;--tblr-pinterest-fg: var(--tblr-light);--tblr-pinterest-darken: rgb(170.1, 7.2, 25.2);--tblr-pinterest-darken: color-mix(in oklab, var(--tblr-pinterest), transparent 20%);--tblr-pinterest-lt: rgb(248.4, 230.3, 232.3);--tblr-pinterest-lt: color-mix(in oklab, var(--tblr-pinterest) 10%, transparent);--tblr-pinterest-200: color-mix(in oklab, var(--tblr-pinterest) 20%, transparent);--tblr-pinterest-lt-rgb: 248, 230, 232;--tblr-vk: #6383a8;--tblr-vk-rgb: 99, 131, 168;--tblr-vk-fg: var(--tblr-light);--tblr-vk-darken: rgb(89.1, 117.9, 151.2);--tblr-vk-darken: color-mix(in oklab, var(--tblr-vk), transparent 20%);--tblr-vk-lt: rgb(239.4, 242.6, 246.3);--tblr-vk-lt: color-mix(in oklab, var(--tblr-vk) 10%, transparent);--tblr-vk-200: color-mix(in oklab, var(--tblr-vk) 20%, transparent);--tblr-vk-lt-rgb: 239, 243, 246;--tblr-rss: #ffa500;--tblr-rss-rgb: 255, 165, 0;--tblr-rss-fg: var(--tblr-light);--tblr-rss-darken: rgb(229.5, 148.5, 0);--tblr-rss-darken: color-mix(in oklab, var(--tblr-rss), transparent 20%);--tblr-rss-lt: rgb(255, 246, 229.5);--tblr-rss-lt: color-mix(in oklab, var(--tblr-rss) 10%, transparent);--tblr-rss-200: color-mix(in oklab, var(--tblr-rss) 20%, transparent);--tblr-rss-lt-rgb: 255, 246, 230;--tblr-flickr: #0063dc;--tblr-flickr-rgb: 0, 99, 220;--tblr-flickr-fg: var(--tblr-light);--tblr-flickr-darken: rgb(0, 89.1, 198);--tblr-flickr-darken: color-mix(in oklab, var(--tblr-flickr), transparent 20%);--tblr-flickr-lt: rgb(229.5, 239.4, 251.5);--tblr-flickr-lt: color-mix(in oklab, var(--tblr-flickr) 10%, transparent);--tblr-flickr-200: color-mix(in oklab, var(--tblr-flickr) 20%, transparent);--tblr-flickr-lt-rgb: 230, 239, 252;--tblr-bitbucket: #0052cc;--tblr-bitbucket-rgb: 0, 82, 204;--tblr-bitbucket-fg: var(--tblr-light);--tblr-bitbucket-darken: rgb(0, 73.8, 183.6);--tblr-bitbucket-darken: color-mix(in oklab, var(--tblr-bitbucket), transparent 20%);--tblr-bitbucket-lt: rgb(229.5, 237.7, 249.9);--tblr-bitbucket-lt: color-mix(in oklab, var(--tblr-bitbucket) 10%, transparent);--tblr-bitbucket-200: color-mix(in oklab, var(--tblr-bitbucket) 20%, transparent);--tblr-bitbucket-lt-rgb: 230, 238, 250;--tblr-tabler: #066fd1;--tblr-tabler-rgb: 6, 111, 209;--tblr-tabler-fg: var(--tblr-light);--tblr-tabler-darken: rgb(5.4, 99.9, 188.1);--tblr-tabler-darken: color-mix(in oklab, var(--tblr-tabler), transparent 20%);--tblr-tabler-lt: rgb(230.1, 240.6, 250.4);--tblr-tabler-lt: color-mix(in oklab, var(--tblr-tabler) 10%, transparent);--tblr-tabler-200: color-mix(in oklab, var(--tblr-tabler) 20%, transparent);--tblr-tabler-lt-rgb: 230, 241, 250;--tblr-gray-50-fg: var(--tblr-body-color);--tblr-gray-100-fg: var(--tblr-body-color);--tblr-gray-200-fg: var(--tblr-body-color);--tblr-gray-300-fg: var(--tblr-body-color);--tblr-gray-400-fg: var(--tblr-white);--tblr-gray-500-fg: var(--tblr-white);--tblr-gray-600-fg: var(--tblr-white);--tblr-gray-700-fg: var(--tblr-white);--tblr-gray-800-fg: var(--tblr-white);--tblr-gray-900-fg: var(--tblr-white);--tblr-gray-950-fg: var(--tblr-white);--tblr-spacer-0: 0;--tblr-spacer-1: .25rem;--tblr-spacer-2: .5rem;--tblr-spacer-3: 1rem;--tblr-spacer-4: 1.5rem;--tblr-spacer-5: 2rem;--tblr-spacer-6: 2.5rem;--tblr-font-weight-light: 300;--tblr-font-weight-normal: 400;--tblr-font-weight-medium: 500;--tblr-font-weight-bold: 600;--tblr-font-weight-black: 700;--tblr-font-weight-headings: var(--tblr-font-weight-bold);--tblr-font-size-h1: 1.5rem;--tblr-font-size-h2: 1.25rem;--tblr-font-size-h3: 1rem;--tblr-font-size-h4: .875rem;--tblr-font-size-h5: .75rem;--tblr-font-size-h6: .625rem;--tblr-line-height-h1: 2rem;--tblr-line-height-h2: 1.75rem;--tblr-line-height-h3: 1.5rem;--tblr-line-height-h4: 1.25rem;--tblr-line-height-h5: 1rem;--tblr-line-height-h6: 1rem;--tblr-shadow: rgba(var(--tblr-body-color-rgb), .04) 0 2px 4px 0;--tblr-shadow-border: inset 0 0 0 1px var(--tblr-border-color-translucent);--tblr-shadow-transparent: 0 0 0 0 transparent;--tblr-shadow-input: 0 1px 1px rgba(var(--tblr-body-color-rgb), .06);--tblr-shadow-card: 0 0 4px rgba(var(--tblr-body-color-rgb), .04);--tblr-shadow-card-hover: rgba(var(--tblr-body-color-rgb), .16) 0 2px 16px 0;--tblr-shadow-dropdown: 0 16px 24px 2px rgba(0, 0, 0, .07), 0 6px 30px 5px rgba(0, 0, 0, .06), 0 8px 10px -5px rgba(0, 0, 0, .1);--tblr-border-radius-scale: 1;--tblr-border-radius-0: calc(0 * var(--tblr-border-radius-scale, 1));--tblr-border-radius-sm: calc(4px * var(--tblr-border-radius-scale, 1));--tblr-border-radius-md: calc(6px * var(--tblr-border-radius-scale, 1));--tblr-border-radius-lg: calc(8px * var(--tblr-border-radius-scale, 1));--tblr-border-radius-pill: calc(100rem * var(--tblr-border-radius-scale, 1));--tblr-border-radius: var(--tblr-border-radius-md);--tblr-backdrop-opacity: 24%;--tblr-backdrop-bg: var(--tblr-bg-surface-dark);--tblr-backdrop-bg-dark: color-mix(in srgb, var(--tblr-color-dark), transparent var(--tblr-backdrop-opacity));--tblr-backdrop-bg-light: color-mix(in srgb, var(--tblr-color-light), transparent var(--tblr-backdrop-opacity));--tblr-backdrop-blur: 4px;--tblr-backdrop-filter: blur(var(--tblr-backdrop-blur))}:root,:host{font-size:16px;height:100%}@media(min-width:992px){:root,:host{margin-left:calc(100vw - 100%);margin-right:0}}:root,:host,[data-bs-theme=light]{color-scheme:light;--tblr-spacer: var(--tblr-spacer-2);--tblr-bg-surface: var(--tblr-bg-surface-primary);--tblr-bg-surface-primary: var(--tblr-white);--tblr-bg-surface-secondary: var(--tblr-gray-50);--tblr-bg-surface-tertiary: var(--tblr-gray-50);--tblr-bg-surface-dark: var(--tblr-gray-900);--tblr-bg-surface-inverted: var(--tblr-gray-900);--tblr-bg-forms: var(--tblr-bg-surface);--tblr-text-inverted: var(--tblr-gray-100);--tblr-body-color: var(--tblr-gray-700);--tblr-body-bg: var(--tblr-bg-surface-secondary);--tblr-link-color: var(--tblr-primary);--tblr-link-hover-color: color-mix(in srgb, var(--tblr-primary), #000 20%);--tblr-secondary: var(--tblr-gray-500);--tblr-tertiary: var(--tblr-gray-400);--tblr-border-color: #e5e7eb;--tblr-border-color-translucent: rgba(4, 32, 69, .1);--tblr-border-dark-color: #9ca3af;--tblr-border-dark-color-translucent: rgba(4, 32, 69, .27);--tblr-border-active-color: rgb(169.16, 173.22, 181.34);--tblr-icon-color: var(--tblr-gray-400);--tblr-active-bg: rgba(var(--tblr-primary-rgb), .04);--tblr-disabled-bg: var(--tblr-bg-surface-secondary);--tblr-disabled-color: color-mix(in srgb, var(--tblr-body-color) 40%, transparent);--tblr-code-color: light-dark(var(--tblr-gray-600), var(--tblr-gray-400));--tblr-code-bg: light-dark(var(--tblr-gray-100), var(--tblr-gray-900));--tblr-dark-mode-border-color: rgb(45.7069767442, 60.4511627907, 81.0930232558);--tblr-dark-mode-border-color-translucent: rgba(72, 110, 149, .14);--tblr-dark-mode-border-active-color: rgb(53.0604651163, 70.176744186, 94.1395348837);--tblr-dark-mode-border-dark-color: rgb(38.3534883721, 50.7255813953, 68.0465116279);--tblr-page-padding: var(--tblr-spacer-3);--tblr-page-padding-y: var(--tblr-spacer-4)}@media(max-width:991.98px){:root,:host,[data-bs-theme=light]{--tblr-page-padding: var(--tblr-spacer-2)}}@keyframes pulse{0%{transform:scale(1)}14%{transform:scale(1.25)}28%{transform:scale(1)}42%{transform:scale(1.25)}70%{transform:scale(1)}}@keyframes tada{0%{transform:scaleZ(1)}10%,5%{transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-5deg)}15%,25%,35%,45%{transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,5deg)}20%,30%,40%{transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-5deg)}50%{transform:scaleZ(1)}}@keyframes rotate-360{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes blink{0%{opacity:0}50%{opacity:1}to{opacity:0}}@keyframes shake{0%{transform:scaleX(1)}20%{transform:scale3d(.9,.9,.9) rotate(-5deg)}50%,70%,90%{transform:scale3d(1.25,1.25,1.25) rotate(5deg)}60%,80%{transform:scale3d(1.25,1.25,1.25) rotate(-5deg)}to{transform:scaleX(1)}}body{letter-spacing:0;touch-action:manipulation;text-rendering:optimizeLegibility;font-feature-settings:"liga" 0,"cv03","cv04","cv11";position:relative;min-height:100%;height:100%;padding:0;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media print{body{background:transparent}}*{scrollbar-color:color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent) transparent}*::-webkit-scrollbar{width:1rem;height:1rem;transition:background .3s}@media(prefers-reduced-motion:reduce){*::-webkit-scrollbar{transition:none}}*::-webkit-scrollbar-thumb{border-radius:1rem;border:5px solid transparent;box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent)}*::-webkit-scrollbar-track{background:transparent}*:hover::-webkit-scrollbar-thumb{box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 40%,transparent)}*::-webkit-scrollbar-corner{background:transparent}.layout-fluid .container,.layout-fluid [class^=container-],.layout-fluid [class*=" container-"]{max-width:100%}.layout-boxed{--tblr-theme-boxed-border-radius: 0;--tblr-theme-boxed-width: 1320px}@media(min-width:768px){.layout-boxed{background:#1f2937 linear-gradient(to right,rgba(255,255,255,.1),transparent) fixed;padding:1rem;--tblr-theme-boxed-border-radius: 6px}}.layout-boxed .page{margin:0 auto;max-width:var(--tblr-theme-boxed-width);border-radius:var(--tblr-theme-boxed-border-radius);color:var(--tblr-body-color)}@media(min-width:768px){.layout-boxed .page{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);background:var(--tblr-body-bg)}}.layout-boxed .page>.navbar:first-child{border-top-left-radius:var(--tblr-theme-boxed-border-radius);border-top-right-radius:var(--tblr-theme-boxed-border-radius)}.navbar{--tblr-navbar-bg: var(--tblr-bg-surface);--tblr-navbar-border-width: var(--tblr-border-width);--tblr-navbar-active-border-color: #00857D;--tblr-navbar-active-bg: rgba(0, 0, 0, .2);--tblr-navbar-border-color: var(--tblr-border-color);--tblr-navbar-hover-color: var(--tblr-body-color);align-items:stretch;min-height:3.5rem;box-shadow:inset 0 calc(-1 * var(--tblr-navbar-border-width)) 0 0 var(--tblr-navbar-border-color);background:var(--tblr-navbar-bg);color:var(--tblr-navbar-color)}.navbar-collapse .navbar{flex-grow:1}.navbar.collapsing{min-height:0}.navbar .dropdown-menu{position:absolute;z-index:1030}.navbar .navbar-nav{min-height:3rem}.navbar .navbar-nav .nav-link{position:relative;min-width:2.5rem;min-height:2.5rem;justify-content:center;border-radius:var(--tblr-border-radius)}.navbar .navbar-nav .nav-link .badge{position:absolute;top:.375rem;right:.375rem;transform:translate(50%,-50%)}@media(max-width:575.98px){.navbar-expand-sm .navbar-collapse{flex-direction:column}.navbar-expand-sm .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand-sm .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand-sm .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand-sm .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand-sm .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand-sm .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand-sm .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:576px){.navbar-expand-sm .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand-sm .nav-item.active{position:relative}.navbar-expand-sm .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand-sm .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand-sm.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-sm.navbar-vertical.navbar-right,.navbar-expand-sm.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-sm.navbar-vertical~.navbar,.navbar-expand-sm.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand-sm.navbar-vertical.navbar-right~.navbar,.navbar-expand-sm.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand-sm.navbar-vertical.navbar-end~.navbar,.navbar-expand-sm.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}}@media(max-width:767.98px){.navbar-expand-md .navbar-collapse{flex-direction:column}.navbar-expand-md .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand-md .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand-md .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand-md .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand-md .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand-md .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand-md .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:768px){.navbar-expand-md .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand-md .nav-item.active{position:relative}.navbar-expand-md .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand-md .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand-md.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-md.navbar-vertical.navbar-right,.navbar-expand-md.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-md.navbar-vertical~.navbar,.navbar-expand-md.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand-md.navbar-vertical.navbar-right~.navbar,.navbar-expand-md.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand-md.navbar-vertical.navbar-end~.navbar,.navbar-expand-md.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}}@media(max-width:991.98px){.navbar-expand-lg .navbar-collapse{flex-direction:column}.navbar-expand-lg .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand-lg .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand-lg .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand-lg .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand-lg .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand-lg .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand-lg .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:992px){.navbar-expand-lg .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand-lg .nav-item.active{position:relative}.navbar-expand-lg .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand-lg .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand-lg.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-lg.navbar-vertical.navbar-right,.navbar-expand-lg.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-lg.navbar-vertical~.navbar,.navbar-expand-lg.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand-lg.navbar-vertical.navbar-right~.navbar,.navbar-expand-lg.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand-lg.navbar-vertical.navbar-end~.navbar,.navbar-expand-lg.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}}@media(max-width:1199.98px){.navbar-expand-xl .navbar-collapse{flex-direction:column}.navbar-expand-xl .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand-xl .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand-xl .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand-xl .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand-xl .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand-xl .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand-xl .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:1200px){.navbar-expand-xl .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand-xl .nav-item.active{position:relative}.navbar-expand-xl .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand-xl .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand-xl.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-xl.navbar-vertical.navbar-right,.navbar-expand-xl.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-xl.navbar-vertical~.navbar,.navbar-expand-xl.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand-xl.navbar-vertical.navbar-right~.navbar,.navbar-expand-xl.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand-xl.navbar-vertical.navbar-end~.navbar,.navbar-expand-xl.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}}@media(max-width:1399.98px){.navbar-expand-xxl .navbar-collapse{flex-direction:column}.navbar-expand-xxl .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand-xxl .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand-xxl .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand-xxl .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand-xxl .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand-xxl .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand-xxl .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:1400px){.navbar-expand-xxl .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand-xxl .nav-item.active{position:relative}.navbar-expand-xxl .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand-xxl .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand-xxl.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-xxl.navbar-vertical.navbar-right,.navbar-expand-xxl.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand-xxl.navbar-vertical~.navbar,.navbar-expand-xxl.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand-xxl.navbar-vertical.navbar-right~.navbar,.navbar-expand-xxl.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand-xxl.navbar-vertical.navbar-end~.navbar,.navbar-expand-xxl.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}}.navbar-expand .navbar-collapse{flex-direction:column}.navbar-expand .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-expand .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-expand .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-expand .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-expand .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-expand .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-expand .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-expand .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-expand .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}.navbar-expand .navbar-collapse{width:auto;flex:1 1 auto}.navbar-expand .nav-item.active{position:relative}.navbar-expand .nav-item.active .nav-link{color:var(--tblr-navbar-active-color)}.navbar-expand .nav-item.active:after{content:"";position:absolute;left:0;right:0;bottom:-.25rem;border:0 var(--tblr-border-style) var(--tblr-navbar-active-border-color);border-bottom-width:2px}.navbar-expand.navbar-vertical{box-shadow:inset calc(-1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand.navbar-vertical.navbar-right,.navbar-expand.navbar-vertical.navbar-end{box-shadow:inset calc(1 * var(--tblr-navbar-border-width)) 0 0 0 var(--tblr-navbar-border-color)}.navbar-expand.navbar-vertical~.navbar,.navbar-expand.navbar-vertical~.page-wrapper{margin-left:18rem}.navbar-expand.navbar-vertical.navbar-right~.navbar,.navbar-expand.navbar-vertical.navbar-right~.page-wrapper,.navbar-expand.navbar-vertical.navbar-end~.navbar,.navbar-expand.navbar-vertical.navbar-end~.page-wrapper{margin-left:0;margin-right:18rem}.navbar-brand{display:inline-flex;align-items:center;font-weight:var(--tblr-font-weight-bold);margin:0;line-height:1;gap:.5rem}.navbar-brand-image{height:2rem;width:auto}.navbar-toggler{border:0;width:2rem;height:2rem;position:relative;display:flex;align-items:center;justify-content:center}.navbar-toggler-icon{height:2px;width:1.25em;background:currentColor;border-radius:10px;transition:top .2s .2s,bottom .2s .2s,transform .2s,opacity 0s .2s}@media(prefers-reduced-motion:reduce){.navbar-toggler-icon{transition:none}}.navbar-toggler-icon{position:relative}.navbar-toggler-icon:before,.navbar-toggler-icon:after{content:"";display:block;height:inherit;width:inherit;border-radius:inherit;background:inherit;position:absolute;left:0;transition:inherit}@media(prefers-reduced-motion:reduce){.navbar-toggler-icon:before,.navbar-toggler-icon:after{transition:none}}.navbar-toggler-icon:before{top:-.45em}.navbar-toggler-icon:after{bottom:-.45em}.navbar-toggler[aria-expanded=true] .navbar-toggler-icon{transform:rotate(45deg);transition:top .3s,bottom .3s,transform .3s .3s,opacity 0s .3s}@media(prefers-reduced-motion:reduce){.navbar-toggler[aria-expanded=true] .navbar-toggler-icon{transition:none}}.navbar-toggler[aria-expanded=true] .navbar-toggler-icon:before{top:0;transform:rotate(-90deg)}.navbar-toggler[aria-expanded=true] .navbar-toggler-icon:after{bottom:0;opacity:0}.navbar-transparent{--tblr-navbar-border-color: transparent !important;background:transparent!important}.navbar-nav{--tblr-nav-link-hover-bg: color-mix(in srgb, var(--tblr-nav-link-color) 4%, transparent);margin:0;padding:0;align-items:stretch}.navbar-nav .nav-item{display:flex;flex-direction:column;justify-content:center}.navbar-side{margin:0;display:flex;flex-direction:row;align-items:center;justify-content:space-around}@media(min-width:576px){.navbar-vertical.navbar-expand-sm{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s;overflow-y:scroll;padding:0}}@media(min-width:576px)and (prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand-sm{transition:none}}@media(min-width:576px){.navbar-vertical.navbar-expand-sm.navbar-right,.navbar-vertical.navbar-expand-sm.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand-sm .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand-sm .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand-sm .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand-sm .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand-sm>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand-sm~.page{padding-left:18rem}.navbar-vertical.navbar-expand-sm~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand-sm.navbar-right~.page,.navbar-vertical.navbar-expand-sm.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand-sm .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand-sm .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand-sm .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand-sm .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand-sm .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand-sm .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:768px){.navbar-vertical.navbar-expand-md{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s;overflow-y:scroll;padding:0}}@media(min-width:768px)and (prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand-md{transition:none}}@media(min-width:768px){.navbar-vertical.navbar-expand-md.navbar-right,.navbar-vertical.navbar-expand-md.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand-md .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand-md .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand-md .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand-md .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand-md>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand-md~.page{padding-left:18rem}.navbar-vertical.navbar-expand-md~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand-md.navbar-right~.page,.navbar-vertical.navbar-expand-md.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand-md .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand-md .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand-md .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand-md .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand-md .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand-md .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:992px){.navbar-vertical.navbar-expand-lg{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s;overflow-y:scroll;padding:0}}@media(min-width:992px)and (prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand-lg{transition:none}}@media(min-width:992px){.navbar-vertical.navbar-expand-lg.navbar-right,.navbar-vertical.navbar-expand-lg.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand-lg .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand-lg .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand-lg .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand-lg .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand-lg>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand-lg~.page{padding-left:18rem}.navbar-vertical.navbar-expand-lg~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand-lg.navbar-right~.page,.navbar-vertical.navbar-expand-lg.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand-lg .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand-lg .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand-lg .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand-lg .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand-lg .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:1200px){.navbar-vertical.navbar-expand-xl{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s;overflow-y:scroll;padding:0}}@media(min-width:1200px)and (prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand-xl{transition:none}}@media(min-width:1200px){.navbar-vertical.navbar-expand-xl.navbar-right,.navbar-vertical.navbar-expand-xl.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand-xl .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand-xl .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand-xl .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand-xl .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand-xl>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand-xl~.page{padding-left:18rem}.navbar-vertical.navbar-expand-xl~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand-xl.navbar-right~.page,.navbar-vertical.navbar-expand-xl.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand-xl .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand-xl .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand-xl .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand-xl .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand-xl .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand-xl .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}@media(min-width:1400px){.navbar-vertical.navbar-expand-xxl{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s;overflow-y:scroll;padding:0}}@media(min-width:1400px)and (prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand-xxl{transition:none}}@media(min-width:1400px){.navbar-vertical.navbar-expand-xxl.navbar-right,.navbar-vertical.navbar-expand-xxl.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand-xxl .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand-xxl .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand-xxl .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand-xxl .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand-xxl>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand-xxl~.page{padding-left:18rem}.navbar-vertical.navbar-expand-xxl~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand-xxl.navbar-right~.page,.navbar-vertical.navbar-expand-xxl.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand-xxl .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand-xxl .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand-xxl .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand-xxl .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand-xxl .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand-xxl .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}}.navbar-vertical.navbar-expand{width:18rem;position:fixed;top:0;left:0;bottom:0;z-index:1030;align-items:flex-start;transition:transform .3s}@media(prefers-reduced-motion:reduce){.navbar-vertical.navbar-expand{transition:none}}.navbar-vertical.navbar-expand{overflow-y:scroll;padding:0}.navbar-vertical.navbar-expand.navbar-right,.navbar-vertical.navbar-expand.navbar-end{left:auto;right:0}.navbar-vertical.navbar-expand .navbar-brand{padding:.75rem 0;justify-content:center}.navbar-vertical.navbar-expand .navbar-collapse{align-items:stretch}.navbar-vertical.navbar-expand .navbar-nav{flex-direction:column;flex-grow:1;min-height:auto}.navbar-vertical.navbar-expand .navbar-nav .nav-link{padding-top:.5rem;padding-bottom:.5rem}.navbar-vertical.navbar-expand>[class^=container]{flex-direction:column;align-items:stretch;min-height:100%;justify-content:flex-start;padding:0}.navbar-vertical.navbar-expand~.page{padding-left:18rem}.navbar-vertical.navbar-expand~.page [class^=container]{padding-left:1.5rem;padding-right:1.5rem}.navbar-vertical.navbar-expand.navbar-right~.page,.navbar-vertical.navbar-expand.navbar-end~.page{padding-left:0;padding-right:18rem}.navbar-vertical.navbar-expand .navbar-collapse{flex-direction:column}.navbar-vertical.navbar-expand .navbar-collapse [class^=container]{flex-direction:column;align-items:stretch;padding:0}.navbar-vertical.navbar-expand .navbar-collapse .navbar-nav{margin-left:0;margin-right:0}.navbar-vertical.navbar-expand .navbar-collapse .navbar-nav .nav-link{padding:.5rem calc(calc(var(--tblr-page-padding) * 2) / 2);justify-content:flex-start}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu-columns{flex-direction:column}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu{padding:0;background:transparent;position:static;color:inherit;box-shadow:none;border:none;min-width:0;margin:0}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item{min-width:0;display:flex;width:auto;padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 1.75rem);color:inherit}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item.disabled{color:var(--tblr-disabled-color);pointer-events:none;background-color:transparent}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item.active,.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-item:active{background:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 3.25rem)}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-menu .dropdown-menu .dropdown-menu .dropdown-item{padding-left:calc(calc(calc(var(--tblr-page-padding) * 2) / 2) + 4.75rem)}.navbar-vertical.navbar-expand .navbar-collapse .dropdown-toggle:after{margin-left:auto}.navbar-vertical.navbar-expand .navbar-collapse .nav-item.active:after{border-bottom-width:0;border-left-width:3px;right:auto;top:0;bottom:0}.navbar-overlap:after{content:"";height:9rem;position:absolute;top:100%;left:0;right:0;background:inherit;z-index:-1;box-shadow:inherit}.page{display:flex;flex-direction:column;position:relative;min-height:100%}.page-center{justify-content:center}.page-wrapper{flex:1;display:flex;flex-direction:column}@media print{.page-wrapper{margin:0!important}}.page-wrapper-full .page-body:first-child{margin:0;border-top:0}.page-body{margin-top:var(--tblr-page-padding-y);margin-bottom:var(--tblr-page-padding-y);display:flex;flex-direction:column;flex:1}.page-body-card{background:var(--tblr-bg-surface);border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);padding:var(--tblr-page-padding) 0;margin-bottom:0;flex:1}.page-body~.page-body-card{margin-top:0}.page-cover{background:no-repeat center/cover;min-height:9rem}@media(min-width:768px){.page-cover{min-height:12rem}}@media(min-width:992px){.page-cover{min-height:15rem}}.page-cover-overlay{position:relative}.page-cover-overlay:after{content:"";position:absolute;inset:0;background-image:linear-gradient(180deg,#0000,#0009)}.page-header{display:flex;flex-wrap:wrap;min-height:2.25rem;flex-direction:column;justify-content:center;max-width:100%}.page-wrapper .page-header{margin:var(--tblr-page-padding-y) 0 0}.page-header-border{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);padding:var(--tblr-page-padding-y) 0;margin:0!important;background-color:var(--tblr-bg-surface)}.page-pretitle{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary)}.page-title{margin:0;font-size:var(--tblr-font-size-h2);line-height:var(--tblr-line-height-h2);font-weight:var(--tblr-font-weight-headings);color:inherit;display:flex;align-items:center}.page-title svg{width:1.5rem;height:1.5rem;margin-right:.25rem}.page-title-lg{font-size:1.5rem;line-height:2rem}.page-subtitle{margin-top:.25rem;color:var(--tblr-secondary)}.page-cover{--tblr-page-cover-blur: 20px;--tblr-page-cover-padding: 1rem;min-height:6rem;padding:var(--tblr-page-cover-padding) 0;position:relative;overflow:hidden}.page-cover-img{position:absolute;top:calc(-2 * var(--tblr-page-cover-blur, 0));left:calc(-2 * var(--tblr-page-cover-blur, 0));right:calc(-2 * var(--tblr-page-cover-blur, 0));bottom:calc(-2 * var(--tblr-page-cover-blur, 0));pointer-events:none;filter:blur(var(--tblr-page-cover-blur));object-fit:cover;background-size:cover;background-position:center;z-index:-1}.page-tabs{margin-top:.5rem;position:relative}.page-header-tabs .nav-bordered{border:0}.page-header-tabs+.page-body-card{margin-top:0}.footer{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);background-color:var(--tblr-bg-surface);padding:2rem 0;color:var(--tblr-gray-500);margin-top:auto}.footer-transparent{background-color:transparent;border-top:0}:root:not(.theme-dark):not([data-bs-theme=dark]) .hide-theme-light{display:none!important}:root:not(.theme-dark):not([data-bs-theme=dark]) .img-dark{display:none!important}:root.theme-dark .hide-theme-dark,:root[data-bs-theme=dark] .hide-theme-dark,body[data-bs-theme=dark] [data-bs-theme=light]:root .hide-theme-dark{display:none!important}:root.theme-dark .img-light,:root[data-bs-theme=dark] .img-light,body[data-bs-theme=dark] [data-bs-theme=light]:root .img-light{display:none!important}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{color-scheme:dark;--tblr-body-color: var(--tblr-gray-200);--tblr-secondary: var(--tblr-gray-400);--tblr-body-bg: var(--tblr-gray-900);--tblr-emphasis-color: #ffffff;--tblr-emphasis-color-rgb: 255, 255, 255;--tblr-bg-forms: var(--tblr-gray-900);--tblr-bg-surface: var(--tblr-gray-800);--tblr-bg-surface-inverted: var(--tblr-gray-100);--tblr-bg-surface-secondary: var(--tblr-gray-900);--tblr-bg-surface-tertiary: var(--tblr-gray-800);--tblr-text-inverted: var(--tblr-gray-800);--tblr-link-color: var(--tblr-primary);--tblr-link-hover-color: color-mix(in srgb, var(--tblr-primary), black 20%);--tblr-active-bg: rgb(34.676744186, 45.8627906977, 61.523255814);--tblr-disabled-color: color-mix(in srgb, var(--tblr-body-color) 40%, transparent);--tblr-border-color: var(--tblr-gray-700);--tblr-border-color-translucent: var( --tblr-dark-mode-border-color-translucent );--tblr-border-dark-color: var(--tblr-dark-mode-border-dark-color);--tblr-border-active-color: var( --tblr-dark-mode-border-active-color );--tblr-btn-color: rgb(27.323255814, 36.1372093023, 48.476744186)}[data-bs-theme=dark] .navbar-brand-autodark .navbar-brand-image{filter:brightness(0) invert(1)}.accordion{--tblr-accordion-color: var(--tblr-body-color);--tblr-accordion-border-color: var(--tblr-border-color);--tblr-accordion-border-radius: var(--tblr-border-radius);--tblr-accordion-inner-border-radius: calc(var(--tblr-border-radius) - (var(--tblr-border-width)));--tblr-accordion-padding-x: 1.25rem;--tblr-accordion-gap: 0;--tblr-accordion-active-color: inherit;--tblr-accordion-btn-color: var(--tblr-accordion-color);--tblr-accordion-btn-bg: transparent;--tblr-accordion-btn-toggle-width: 1.25rem;--tblr-accordion-btn-padding-x: var(--tblr-accordion-padding-x);--tblr-accordion-btn-padding-y: 1rem;--tblr-accordion-btn-font-weight: var(--tblr-font-weight-medium);--tblr-accordion-body-padding-x: var(--tblr-accordion-padding-x);--tblr-accordion-body-padding-y: 1rem;display:flex;flex-direction:column;gap:var(--tblr-accordion-gap)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--tblr-accordion-btn-padding-y) var(--tblr-accordion-padding-x);color:inherit;text-align:inherit;background-color:transparent;border:0;font-size:inherit;font-weight:var(--tblr-accordion-btn-font-weight);gap:.75rem}.accordion-button:not(.collapsed){border-bottom-color:transparent;box-shadow:none;color:var(--tblr-accordion-active-color)}.accordion-header{margin:0;position:relative;display:flex;gap:1rem;align-items:center;width:100%;color:var(--tblr-accordion-btn-color);text-align:left;background-color:transparent;border:0;overflow-anchor:none;transition:transform .3s}.accordion-header:hover{z-index:2}.accordion-header:focus{z-index:3;outline:0;box-shadow:var(--tblr-accordion-btn-focus-box-shadow)}.accordion-header:focus:not(:focus-visible){outline:none;box-shadow:none}.accordion-button-icon{color:var(--tblr-secondary)}.accordion-button-toggle{display:flex;line-height:1;transition:.3s transform;margin-left:auto;margin-right:0;color:var(--tblr-secondary);width:var(--tblr-accordion-btn-toggle-width);height:var(--tblr-accordion-btn-toggle-width)}.accordion-button:not(.collapsed) .accordion-button-toggle{transform:rotate(-180deg);color:var(--tblr-accordion-active-color)}.accordion-button-toggle path{transition:.3s opacity}.accordion-button:not(.collapsed) .accordion-button-toggle-plus path:first-child{opacity:0}.accordion-item{color:var(--tblr-accordion-color);border:var(--tblr-border-width) solid var(--tblr-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--tblr-accordion-border-radius);border-top-right-radius:var(--tblr-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header{border-top-left-radius:var(--tblr-accordion-inner-border-radius);border-top-right-radius:var(--tblr-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--tblr-accordion-border-radius);border-bottom-left-radius:var(--tblr-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header.collapsed{border-bottom-right-radius:var(--tblr-accordion-inner-border-radius);border-bottom-left-radius:var(--tblr-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--tblr-accordion-border-radius);border-bottom-left-radius:var(--tblr-accordion-border-radius)}.accordion-body{color:var(--tblr-secondary);padding:0 var(--tblr-accordion-body-padding-x) var(--tblr-accordion-body-padding-y)}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush>.accordion-item:first-child{border-top:0}.accordion-flush>.accordion-item:last-child{border-bottom:0}.accordion-flush>.accordion-item>.accordion-collapse,.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}.accordion-tabs{--tblr-accordion-gap: .75rem}.accordion-tabs>.accordion-item{border:var(--tblr-border-width) solid var(--tblr-accordion-border-color);border-radius:var(--tblr-accordion-border-radius)}.accordion-inverted .accordion-button-toggle{order:-1;margin-left:0}.alert{--tblr-alert-color: var(--tblr-body-color);--tblr-alert-bg: color-mix(in srgb, var(--tblr-alert-color) 10%, transparent);--tblr-alert-padding-x: 1rem;--tblr-alert-padding-y: .75rem;--tblr-alert-margin-bottom: 1rem;--tblr-alert-border-color: color-mix(in srgb, var(--tblr-alert-color) 20%, transparent);--tblr-alert-border: var(--tblr-border-width) solid var(--tblr-alert-border-color);--tblr-alert-border-radius: var(--tblr-border-radius);--tblr-alert-link-color: inherit;--tblr-alert-heading-font-weight: var(--tblr-font-weight-medium);position:relative;padding:var(--tblr-alert-padding-y) var(--tblr-alert-padding-x);margin-bottom:var(--tblr-alert-margin-bottom);background-color:color-mix(in srgb,var(--tblr-alert-bg),var(--tblr-bg-surface));border-radius:var(--tblr-alert-border-radius);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-alert-border-color);display:flex;flex-direction:row;gap:1rem}.alert-heading{color:inherit;margin-bottom:.25rem;font-weight:var(--tblr-alert-heading-font-weight)}.alert-description{color:var(--tblr-secondary)}.alert-icon{color:var(--tblr-alert-color);width:1.25rem!important;height:1.25rem!important}.alert-action{color:var(--tblr-alert-color);text-decoration:underline}.alert-action:hover{text-decoration:none}.alert-list{margin:0}.alert-link{font-weight:var(--tblr-font-weight-bold);color:var(--tblr-alert-link-color)}.alert-link,.alert-link:hover{color:var(--tblr-alert-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:calc(var(--tblr-alert-padding-x) / 2 - 1px);right:calc(var(--tblr-alert-padding-y) / 2 - 1px);z-index:1;padding:calc(var(--tblr-alert-padding-y) * 1.25) var(--tblr-alert-padding-x)}.alert-important{border-color:var(--tblr-alert-color);background-color:var(--tblr-alert-color);color:var(--tblr-white)}.alert-important .alert-description,.alert-important .alert-icon{color:inherit}.alert-minor{background:transparent;border-color:var(--tblr-border-color)}.alert-primary{--tblr-alert-color: var(--tblr-primary)}.alert-secondary{--tblr-alert-color: var(--tblr-secondary)}.alert-success{--tblr-alert-color: var(--tblr-success)}.alert-info{--tblr-alert-color: var(--tblr-info)}.alert-warning{--tblr-alert-color: var(--tblr-warning)}.alert-danger{--tblr-alert-color: var(--tblr-danger)}.alert-light{--tblr-alert-color: var(--tblr-light)}.alert-dark{--tblr-alert-color: var(--tblr-dark)}.alert-muted{--tblr-alert-color: var(--tblr-muted)}.alert-blue{--tblr-alert-color: var(--tblr-blue)}.alert-azure{--tblr-alert-color: var(--tblr-azure)}.alert-indigo{--tblr-alert-color: var(--tblr-indigo)}.alert-purple{--tblr-alert-color: var(--tblr-purple)}.alert-pink{--tblr-alert-color: var(--tblr-pink)}.alert-red{--tblr-alert-color: var(--tblr-red)}.alert-orange{--tblr-alert-color: var(--tblr-orange)}.alert-yellow{--tblr-alert-color: var(--tblr-yellow)}.alert-lime{--tblr-alert-color: var(--tblr-lime)}.alert-green{--tblr-alert-color: var(--tblr-green)}.alert-teal{--tblr-alert-color: var(--tblr-teal)}.alert-cyan{--tblr-alert-color: var(--tblr-cyan)}.avatar{--tblr-avatar-size: var(--tblr-avatar-list-size, 2.5rem);--tblr-avatar-status-size: .75rem;--tblr-avatar-bg: var(--tblr-bg-surface-secondary);--tblr-avatar-box-shadow-color: var(--tblr-border-color-translucent);--tblr-avatar-box-shadow: inset 0 0 0 1px var(--tblr-avatar-box-shadow-color);--tblr-avatar-font-size: 1rem;--tblr-avatar-icon-size: 1.5rem;--tblr-avatar-brand-size: 1.25rem;position:relative;width:var(--tblr-avatar-size);height:var(--tblr-avatar-size);font-size:var(--tblr-avatar-font-size);font-weight:var(--tblr-font-weight-medium);line-height:1;display:inline-flex;align-items:center;justify-content:center;color:var(--tblr-secondary);text-align:center;text-transform:uppercase;vertical-align:bottom;user-select:none;background:var(--tblr-avatar-bg) no-repeat center/cover;border-radius:var(--tblr-border-radius);box-shadow:var(--tblr-avatar-box-shadow);transition:color .3s,background-color .3s,box-shadow .3s}.avatar .icon{width:var(--tblr-avatar-icon-size);height:var(--tblr-avatar-icon-size)}.avatar .badge{position:absolute;right:0;bottom:0;border-radius:100rem;box-shadow:0 0 0 calc(var(--tblr-avatar-status-size) / 4) var(--tblr-bg-surface)}a.avatar{cursor:pointer}a.avatar:hover{color:var(--tblr-primary);--tblr-avatar-box-shadow-color: var(--tblr-primary)}.avatar-rounded{border-radius:100rem}.avatar-xxs{--tblr-avatar-size: 1rem;--tblr-avatar-status-size: .25rem;--tblr-avatar-font-size: .5rem;--tblr-avatar-icon-size: .5rem;--tblr-avatar-brand-size: .5rem}.avatar-xxs .badge:empty{width:.25rem;height:.25rem}.avatar-xs{--tblr-avatar-size: 1.25rem;--tblr-avatar-status-size: .375rem;--tblr-avatar-font-size: .625rem;--tblr-avatar-icon-size: .75rem;--tblr-avatar-brand-size: .75rem}.avatar-xs .badge:empty{width:.375rem;height:.375rem}.avatar-sm{--tblr-avatar-size: 2rem;--tblr-avatar-status-size: .5rem;--tblr-avatar-font-size: .75rem;--tblr-avatar-icon-size: 1.5rem;--tblr-avatar-brand-size: 1rem}.avatar-sm .badge:empty{width:.5rem;height:.5rem}.avatar-md{--tblr-avatar-size: 2.5rem;--tblr-avatar-status-size: .75rem;--tblr-avatar-font-size: .875rem;--tblr-avatar-icon-size: 1.5rem;--tblr-avatar-brand-size: 1.25rem}.avatar-md .badge:empty{width:.75rem;height:.75rem}.avatar-lg{--tblr-avatar-size: 3rem;--tblr-avatar-status-size: .75rem;--tblr-avatar-font-size: 1.25rem;--tblr-avatar-icon-size: 2rem;--tblr-avatar-brand-size: 1.25rem}.avatar-lg .badge:empty{width:.75rem;height:.75rem}.avatar-xl{--tblr-avatar-size: 5rem;--tblr-avatar-status-size: 1rem;--tblr-avatar-font-size: 2rem;--tblr-avatar-icon-size: 3rem;--tblr-avatar-brand-size: 1.25rem}.avatar-xl .badge:empty{width:1rem;height:1rem}.avatar-2xl{--tblr-avatar-size: 7rem;--tblr-avatar-status-size: 1rem;--tblr-avatar-font-size: 3rem;--tblr-avatar-icon-size: 5rem;--tblr-avatar-brand-size: 2rem}.avatar-2xl .badge:empty{width:1rem;height:1rem}.avatar-list{--tblr-avatar-list-size: 2.5rem;--tblr-list-gap: .5rem;display:flex;flex-wrap:wrap;gap:var(--tblr-list-gap)}.avatar-list a.avatar:hover{z-index:1}.avatar-list-stacked{display:block;--tblr-list-gap: 0}.avatar-list-stacked .avatar{margin-right:calc(-.5 * var(--tblr-avatar-size))!important;box-shadow:var(--tblr-avatar-box-shadow),0 0 0 2px var(--tblr-card-bg, var(--tblr-bg-surface))}.avatar-list-xxs{--tblr-avatar-list-size: 1rem}.avatar-list-xs{--tblr-avatar-list-size: 1.25rem}.avatar-list-sm{--tblr-avatar-list-size: 2rem}.avatar-list-md{--tblr-avatar-list-size: 2.5rem}.avatar-list-lg{--tblr-avatar-list-size: 3rem}.avatar-list-xl{--tblr-avatar-list-size: 5rem}.avatar-list-2xl{--tblr-avatar-list-size: 7rem}.avatar-upload{border:var(--tblr-border-width) dashed var(--tblr-border-color);background:var(--tblr-bg-forms);box-shadow:none;flex-direction:column;transition:color .3s,background-color .3s}@media(prefers-reduced-motion:reduce){.avatar-upload{transition:none}}.avatar-upload svg{width:1.5rem;height:1.5rem;stroke-width:1}.avatar-upload:hover{border-color:var(--tblr-primary);color:var(--tblr-primary);text-decoration:none}.avatar-upload-text{font-size:.625rem;line-height:1;margin-top:.25rem}.avatar-cover{margin-top:calc(-.5 * var(--tblr-avatar-size));box-shadow:0 0 0 .25rem var(--tblr-card-bg, var(--tblr-body-bg))}.avatar-brand{width:var(--tblr-avatar-brand-size);height:var(--tblr-avatar-brand-size);position:absolute;right:-2px;bottom:-2px;z-index:1000;background:var(--tblr-bg-surface);border-radius:var(--tblr-border-radius);border:1px solid var(--tblr-border-color)}.badge{--tblr-badge-padding-x: .5em;--tblr-badge-padding-y: .25em;--tblr-badge-font-size: .85714285em;--tblr-badge-font-weight: var(--tblr-font-weight-medium);--tblr-badge-color: var(--tblr-secondary);--tblr-badge-border-radius: var(--tblr-border-radius);--tblr-badge-icon-size: 1em;--tblr-badge-line-height: 1;display:inline-flex;padding:var(--tblr-badge-padding-y) var(--tblr-badge-padding-x);font-weight:var(--tblr-badge-font-weight);font-size:var(--tblr-badge-font-size);color:var(--tblr-badge-color);text-align:center;white-space:nowrap;justify-content:center;align-items:center;gap:.25rem;background:var(--tblr-bg-surface-secondary);overflow:hidden;user-select:none;border:var(--tblr-border-width) var(--tblr-border-style) transparent;border-radius:var(--tblr-badge-border-radius);min-width:calc(1em + var(--tblr-badge-padding-y) * 2 + 2px);letter-spacing:.04em;vertical-align:bottom;line-height:var(--tblr-badge-line-height)}a.badge{background:var(--tblr-bg-surface-secondary)}.badge .icon{width:1em;height:1em;font-size:var(--tblr-badge-icon-size);stroke-width:2}.badge:empty,.badge-dot{display:inline-block;width:10px;height:10px;min-width:0;min-height:auto;padding:0;border-radius:100rem;vertical-align:baseline}.badge-outline{background-color:transparent;border:var(--tblr-border-width) var(--tblr-border-style) currentColor}.badge-pill{border-radius:100rem}.badges-list{--tblr-list-gap: .5rem;display:flex;flex-wrap:wrap;gap:var(--tblr-list-gap)}.badge-notification{position:absolute!important;top:0!important;right:0!important;transform:translate(50%,-50%);z-index:1}.badge-blink{animation:blink 2s infinite}.badge-sm{--tblr-badge-font-size: .71428571em;--tblr-badge-icon-size: 1em;--tblr-badge-padding-y: 2px;--tblr-badge-padding-x: .25rem}.badge-lg{--tblr-badge-font-size: 1em;--tblr-badge-icon-size: 1em;--tblr-badge-padding-y: .25rem;--tblr-badge-padding-x: .5rem}.badge-icononly{--tblr-badge-padding-x: 0}.breadcrumb{--tblr-breadcrumb-padding-x: 0;--tblr-breadcrumb-padding-y: 0;--tblr-breadcrumb-margin-bottom: 1rem;--tblr-breadcrumb-font-size: ;--tblr-breadcrumb-bg: ;--tblr-breadcrumb-border-radius: ;--tblr-breadcrumb-divider-color: var(--tblr-gray-500);--tblr-breadcrumb-item-padding-x: .5rem;--tblr-breadcrumb-item-active-color: inherit;--tblr-breadcrumb-item-active-font-weight: var(--tblr-font-weight-bold);--tblr-breadcrumb-item-disabled-color: var(--tblr-disabled-color);--tblr-breadcrumb-link-color: var(--tblr-link-color);display:flex;flex-wrap:wrap;font-size:var(--tblr-breadcrumb-font-size);list-style:none;background-color:var(--tblr-breadcrumb-bg);border-radius:var(--tblr-breadcrumb-border-radius);padding:0;margin:0;background:transparent}.breadcrumb a{color:var(--tblr-breadcrumb-link-color)}.breadcrumb a:hover{text-decoration:underline}.breadcrumb-muted{--tblr-breadcrumb-link-color: var(--tblr-secondary)}.breadcrumb-item.active{color:var(--tblr-breadcrumb-item-active-color);font-weight:var(--tblr-breadcrumb-item-active-font-weight)}.breadcrumb-item.active a{color:inherit;pointer-events:none}.breadcrumb-item.disabled{color:var(--tblr-breadcrumb-item-disabled-color)}.breadcrumb-item.disabled:before{color:inherit}.breadcrumb-item.disabled a{color:inherit;pointer-events:none}.breadcrumb-item+.breadcrumb-item{padding-left:var(--tblr-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item:before{float:left;padding-right:var(--tblr-breadcrumb-item-padding-x);color:var(--tblr-breadcrumb-divider-color);content:var(--tblr-breadcrumb-divider, "/")}.breadcrumb-dots{--tblr-breadcrumb-divider: "\b7"}.breadcrumb-arrows{--tblr-breadcrumb-divider: "\203a"}.breadcrumb-bullets{--tblr-breadcrumb-divider: "\2022"}.btn{--tblr-btn-icon-size: 1.25rem;--tblr-btn-icon-color: inherit;--tblr-btn-bg: var(--tblr-bg-surface);--tblr-btn-color: var(--tblr-body-color);--tblr-btn-border-color: var(--tblr-border-color);--tblr-btn-hover-bg: var(--tblr-btn-bg);--tblr-btn-hover-border-color: var(--tblr-border-active-color);--tblr-btn-active-color: var(--tblr-primary);--tblr-btn-active-bg: rgba(var(--tblr-primary-rgb), .04);--tblr-btn-active-border-color: var(--tblr-primary);display:inline-flex;align-items:center;justify-content:center;white-space:nowrap;box-shadow:var(--tblr-btn-box-shadow);position:relative;min-width:calc(var(--tblr-btn-line-height) * 1 + var(--tblr-btn-padding-y) * 2 + var(--tblr-btn-border-width) * 2);min-height:calc(var(--tblr-btn-line-height) * 1 + var(--tblr-btn-padding-y) * 2 + var(--tblr-btn-border-width) * 2)}.btn .icon{width:var(--tblr-btn-icon-size);height:var(--tblr-btn-icon-size);min-width:var(--tblr-btn-icon-size);font-size:var(--tblr-btn-icon-size);margin:0 calc(var(--tblr-btn-padding-x) / 2) 0 calc(var(--tblr-btn-padding-x) / -4);vertical-align:bottom;color:var(--tblr-btn-icon-color)}.btn .avatar{width:var(--tblr-btn-icon-size);height:var(--tblr-btn-icon-size);margin:0 calc(var(--tblr-btn-padding-x) / 2) 0 calc(var(--tblr-btn-padding-x) / -4)}.btn .icon-right,.btn .icon-end{margin:0 calc(var(--tblr-btn-padding-x) / -4) 0 calc(var(--tblr-btn-padding-x) / 2)}.btn .badge{top:auto}.btn-check+.btn:hover{color:var(--tblr-btn-hover-color);background-color:var(--tblr-btn-hover-bg);border-color:var(--tblr-btn-hover-border-color)}.btn-link{color:#009f95;background-color:transparent;border-color:transparent;box-shadow:none}.btn-link .icon{color:inherit}.btn-link:hover{color:#006a64;border-color:transparent}.btn-primary{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-primary-fg, #ffffff);--tblr-btn-bg: var(--tblr-primary);--tblr-btn-hover-color: var(--tblr-primary-fg);--tblr-btn-hover-bg: var(--tblr-primary-darken);--tblr-btn-active-color: var(--tblr-primary-fg);--tblr-btn-active-bg: var(--tblr-primary-darken);--tblr-btn-disabled-bg: var(--tblr-primary);--tblr-btn-disabled-color: var(--tblr-primary-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-primary,.btn-outline.btn-primary{--tblr-btn-color: var(--tblr-primary);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-primary);--tblr-btn-hover-color: var(--tblr-primary-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-primary);--tblr-btn-active-color: var(--tblr-primary-fg);--tblr-btn-active-bg: var(--tblr-primary);--tblr-btn-active-border-color: var(--tblr-primary);--tblr-btn-disabled-color: var(--tblr-primary);--tblr-btn-disabled-border-color: var(--tblr-primary)}.btn-ghost-primary,.btn-ghost.btn-primary{--tblr-btn-color: var(--tblr-primary);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-primary-fg);--tblr-btn-hover-bg: var(--tblr-primary);--tblr-btn-hover-border-color: var(--tblr-primary);--tblr-btn-active-color: var(--tblr-primary-fg);--tblr-btn-active-bg: var(--tblr-primary);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-primary);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-secondary,.btn-grey,.btn-gray{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-secondary-fg, #ffffff);--tblr-btn-bg: var(--tblr-secondary);--tblr-btn-hover-color: var(--tblr-secondary-fg);--tblr-btn-hover-bg: var(--tblr-secondary-darken);--tblr-btn-active-color: var(--tblr-secondary-fg);--tblr-btn-active-bg: var(--tblr-secondary-darken);--tblr-btn-disabled-bg: var(--tblr-secondary);--tblr-btn-disabled-color: var(--tblr-secondary-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-secondary,.btn-outline.btn-secondary,.btn-outline.btn-grey,.btn-outline.btn-gray{--tblr-btn-color: var(--tblr-secondary);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-secondary);--tblr-btn-hover-color: var(--tblr-secondary-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-secondary);--tblr-btn-active-color: var(--tblr-secondary-fg);--tblr-btn-active-bg: var(--tblr-secondary);--tblr-btn-active-border-color: var(--tblr-secondary);--tblr-btn-disabled-color: var(--tblr-secondary);--tblr-btn-disabled-border-color: var(--tblr-secondary)}.btn-ghost-secondary,.btn-ghost.btn-secondary,.btn-ghost.btn-grey,.btn-ghost.btn-gray{--tblr-btn-color: var(--tblr-secondary);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-secondary-fg);--tblr-btn-hover-bg: var(--tblr-secondary);--tblr-btn-hover-border-color: var(--tblr-secondary);--tblr-btn-active-color: var(--tblr-secondary-fg);--tblr-btn-active-bg: var(--tblr-secondary);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-secondary);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-success{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-success-fg, #ffffff);--tblr-btn-bg: var(--tblr-success);--tblr-btn-hover-color: var(--tblr-success-fg);--tblr-btn-hover-bg: var(--tblr-success-darken);--tblr-btn-active-color: var(--tblr-success-fg);--tblr-btn-active-bg: var(--tblr-success-darken);--tblr-btn-disabled-bg: var(--tblr-success);--tblr-btn-disabled-color: var(--tblr-success-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-success,.btn-outline.btn-success{--tblr-btn-color: var(--tblr-success);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-success);--tblr-btn-hover-color: var(--tblr-success-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-success);--tblr-btn-active-color: var(--tblr-success-fg);--tblr-btn-active-bg: var(--tblr-success);--tblr-btn-active-border-color: var(--tblr-success);--tblr-btn-disabled-color: var(--tblr-success);--tblr-btn-disabled-border-color: var(--tblr-success)}.btn-ghost-success,.btn-ghost.btn-success{--tblr-btn-color: var(--tblr-success);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-success-fg);--tblr-btn-hover-bg: var(--tblr-success);--tblr-btn-hover-border-color: var(--tblr-success);--tblr-btn-active-color: var(--tblr-success-fg);--tblr-btn-active-bg: var(--tblr-success);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-success);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-info{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-info-fg, #ffffff);--tblr-btn-bg: var(--tblr-info);--tblr-btn-hover-color: var(--tblr-info-fg);--tblr-btn-hover-bg: var(--tblr-info-darken);--tblr-btn-active-color: var(--tblr-info-fg);--tblr-btn-active-bg: var(--tblr-info-darken);--tblr-btn-disabled-bg: var(--tblr-info);--tblr-btn-disabled-color: var(--tblr-info-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-info,.btn-outline.btn-info{--tblr-btn-color: var(--tblr-info);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-info);--tblr-btn-hover-color: var(--tblr-info-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-info);--tblr-btn-active-color: var(--tblr-info-fg);--tblr-btn-active-bg: var(--tblr-info);--tblr-btn-active-border-color: var(--tblr-info);--tblr-btn-disabled-color: var(--tblr-info);--tblr-btn-disabled-border-color: var(--tblr-info)}.btn-ghost-info,.btn-ghost.btn-info{--tblr-btn-color: var(--tblr-info);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-info-fg);--tblr-btn-hover-bg: var(--tblr-info);--tblr-btn-hover-border-color: var(--tblr-info);--tblr-btn-active-color: var(--tblr-info-fg);--tblr-btn-active-bg: var(--tblr-info);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-info);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-warning{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-warning-fg, #ffffff);--tblr-btn-bg: var(--tblr-warning);--tblr-btn-hover-color: var(--tblr-warning-fg);--tblr-btn-hover-bg: var(--tblr-warning-darken);--tblr-btn-active-color: var(--tblr-warning-fg);--tblr-btn-active-bg: var(--tblr-warning-darken);--tblr-btn-disabled-bg: var(--tblr-warning);--tblr-btn-disabled-color: var(--tblr-warning-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-warning,.btn-outline.btn-warning{--tblr-btn-color: var(--tblr-warning);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-warning);--tblr-btn-hover-color: var(--tblr-warning-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-warning);--tblr-btn-active-color: var(--tblr-warning-fg);--tblr-btn-active-bg: var(--tblr-warning);--tblr-btn-active-border-color: var(--tblr-warning);--tblr-btn-disabled-color: var(--tblr-warning);--tblr-btn-disabled-border-color: var(--tblr-warning)}.btn-ghost-warning,.btn-ghost.btn-warning{--tblr-btn-color: var(--tblr-warning);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-warning-fg);--tblr-btn-hover-bg: var(--tblr-warning);--tblr-btn-hover-border-color: var(--tblr-warning);--tblr-btn-active-color: var(--tblr-warning-fg);--tblr-btn-active-bg: var(--tblr-warning);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-warning);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-danger{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-danger-fg, #ffffff);--tblr-btn-bg: var(--tblr-danger);--tblr-btn-hover-color: var(--tblr-danger-fg);--tblr-btn-hover-bg: var(--tblr-danger-darken);--tblr-btn-active-color: var(--tblr-danger-fg);--tblr-btn-active-bg: var(--tblr-danger-darken);--tblr-btn-disabled-bg: var(--tblr-danger);--tblr-btn-disabled-color: var(--tblr-danger-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-danger,.btn-outline.btn-danger{--tblr-btn-color: var(--tblr-danger);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-danger);--tblr-btn-hover-color: var(--tblr-danger-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-danger);--tblr-btn-active-color: var(--tblr-danger-fg);--tblr-btn-active-bg: var(--tblr-danger);--tblr-btn-active-border-color: var(--tblr-danger);--tblr-btn-disabled-color: var(--tblr-danger);--tblr-btn-disabled-border-color: var(--tblr-danger)}.btn-ghost-danger,.btn-ghost.btn-danger{--tblr-btn-color: var(--tblr-danger);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-danger-fg);--tblr-btn-hover-bg: var(--tblr-danger);--tblr-btn-hover-border-color: var(--tblr-danger);--tblr-btn-active-color: var(--tblr-danger-fg);--tblr-btn-active-bg: var(--tblr-danger);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-danger);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-light,.btn-white{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-light-fg, #ffffff);--tblr-btn-bg: var(--tblr-light);--tblr-btn-hover-color: var(--tblr-light-fg);--tblr-btn-hover-bg: var(--tblr-light-darken);--tblr-btn-active-color: var(--tblr-light-fg);--tblr-btn-active-bg: var(--tblr-light-darken);--tblr-btn-disabled-bg: var(--tblr-light);--tblr-btn-disabled-color: var(--tblr-light-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-light,.btn-outline.btn-light,.btn-outline.btn-white{--tblr-btn-color: var(--tblr-light);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-light);--tblr-btn-hover-color: var(--tblr-light-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-light);--tblr-btn-active-color: var(--tblr-light-fg);--tblr-btn-active-bg: var(--tblr-light);--tblr-btn-active-border-color: var(--tblr-light);--tblr-btn-disabled-color: var(--tblr-light);--tblr-btn-disabled-border-color: var(--tblr-light)}.btn-ghost-light,.btn-ghost.btn-light,.btn-ghost.btn-white{--tblr-btn-color: var(--tblr-light);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-light-fg);--tblr-btn-hover-bg: var(--tblr-light);--tblr-btn-hover-border-color: var(--tblr-light);--tblr-btn-active-color: var(--tblr-light-fg);--tblr-btn-active-bg: var(--tblr-light);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-light);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-dark,.btn-black{--tblr-btn-border-color: var(--tblr-dark-mode-border-color);--tblr-btn-hover-border-color: var(--tblr-dark-mode-border-active-color);--tblr-btn-active-border-color: var(--tblr-dark-mode-border-active-color);--tblr-btn-color: var(--tblr-dark-fg, #ffffff);--tblr-btn-bg: var(--tblr-dark);--tblr-btn-hover-color: var(--tblr-dark-fg);--tblr-btn-hover-bg: var(--tblr-dark-darken);--tblr-btn-active-color: var(--tblr-dark-fg);--tblr-btn-active-bg: var(--tblr-dark-darken);--tblr-btn-disabled-bg: var(--tblr-dark);--tblr-btn-disabled-color: var(--tblr-dark-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-dark,.btn-outline.btn-dark,.btn-outline.btn-black{--tblr-btn-color: var(--tblr-dark);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-dark);--tblr-btn-hover-color: var(--tblr-dark-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-dark);--tblr-btn-active-color: var(--tblr-dark-fg);--tblr-btn-active-bg: var(--tblr-dark);--tblr-btn-active-border-color: var(--tblr-dark);--tblr-btn-disabled-color: var(--tblr-dark);--tblr-btn-disabled-border-color: var(--tblr-dark)}.btn-ghost-dark,.btn-ghost.btn-dark,.btn-ghost.btn-black{--tblr-btn-color: var(--tblr-dark);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-dark-fg);--tblr-btn-hover-bg: var(--tblr-dark);--tblr-btn-hover-border-color: var(--tblr-dark);--tblr-btn-active-color: var(--tblr-dark-fg);--tblr-btn-active-bg: var(--tblr-dark);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-dark);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-muted{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-muted-fg, #ffffff);--tblr-btn-bg: var(--tblr-muted);--tblr-btn-hover-color: var(--tblr-muted-fg);--tblr-btn-hover-bg: var(--tblr-muted-darken);--tblr-btn-active-color: var(--tblr-muted-fg);--tblr-btn-active-bg: var(--tblr-muted-darken);--tblr-btn-disabled-bg: var(--tblr-muted);--tblr-btn-disabled-color: var(--tblr-muted-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-muted,.btn-outline.btn-muted{--tblr-btn-color: var(--tblr-muted);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-muted);--tblr-btn-hover-color: var(--tblr-muted-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-muted);--tblr-btn-active-color: var(--tblr-muted-fg);--tblr-btn-active-bg: var(--tblr-muted);--tblr-btn-active-border-color: var(--tblr-muted);--tblr-btn-disabled-color: var(--tblr-muted);--tblr-btn-disabled-border-color: var(--tblr-muted)}.btn-ghost-muted,.btn-ghost.btn-muted{--tblr-btn-color: var(--tblr-muted);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-muted-fg);--tblr-btn-hover-bg: var(--tblr-muted);--tblr-btn-hover-border-color: var(--tblr-muted);--tblr-btn-active-color: var(--tblr-muted-fg);--tblr-btn-active-bg: var(--tblr-muted);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-muted);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-blue{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-blue-fg, #ffffff);--tblr-btn-bg: var(--tblr-blue);--tblr-btn-hover-color: var(--tblr-blue-fg);--tblr-btn-hover-bg: var(--tblr-blue-darken);--tblr-btn-active-color: var(--tblr-blue-fg);--tblr-btn-active-bg: var(--tblr-blue-darken);--tblr-btn-disabled-bg: var(--tblr-blue);--tblr-btn-disabled-color: var(--tblr-blue-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-blue,.btn-outline.btn-blue{--tblr-btn-color: var(--tblr-blue);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-blue);--tblr-btn-hover-color: var(--tblr-blue-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-blue);--tblr-btn-active-color: var(--tblr-blue-fg);--tblr-btn-active-bg: var(--tblr-blue);--tblr-btn-active-border-color: var(--tblr-blue);--tblr-btn-disabled-color: var(--tblr-blue);--tblr-btn-disabled-border-color: var(--tblr-blue)}.btn-ghost-blue,.btn-ghost.btn-blue{--tblr-btn-color: var(--tblr-blue);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-blue-fg);--tblr-btn-hover-bg: var(--tblr-blue);--tblr-btn-hover-border-color: var(--tblr-blue);--tblr-btn-active-color: var(--tblr-blue-fg);--tblr-btn-active-bg: var(--tblr-blue);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-blue);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-azure{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-azure-fg, #ffffff);--tblr-btn-bg: var(--tblr-azure);--tblr-btn-hover-color: var(--tblr-azure-fg);--tblr-btn-hover-bg: var(--tblr-azure-darken);--tblr-btn-active-color: var(--tblr-azure-fg);--tblr-btn-active-bg: var(--tblr-azure-darken);--tblr-btn-disabled-bg: var(--tblr-azure);--tblr-btn-disabled-color: var(--tblr-azure-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-azure,.btn-outline.btn-azure{--tblr-btn-color: var(--tblr-azure);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-azure);--tblr-btn-hover-color: var(--tblr-azure-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-azure);--tblr-btn-active-color: var(--tblr-azure-fg);--tblr-btn-active-bg: var(--tblr-azure);--tblr-btn-active-border-color: var(--tblr-azure);--tblr-btn-disabled-color: var(--tblr-azure);--tblr-btn-disabled-border-color: var(--tblr-azure)}.btn-ghost-azure,.btn-ghost.btn-azure{--tblr-btn-color: var(--tblr-azure);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-azure-fg);--tblr-btn-hover-bg: var(--tblr-azure);--tblr-btn-hover-border-color: var(--tblr-azure);--tblr-btn-active-color: var(--tblr-azure-fg);--tblr-btn-active-bg: var(--tblr-azure);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-azure);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-indigo{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-indigo-fg, #ffffff);--tblr-btn-bg: var(--tblr-indigo);--tblr-btn-hover-color: var(--tblr-indigo-fg);--tblr-btn-hover-bg: var(--tblr-indigo-darken);--tblr-btn-active-color: var(--tblr-indigo-fg);--tblr-btn-active-bg: var(--tblr-indigo-darken);--tblr-btn-disabled-bg: var(--tblr-indigo);--tblr-btn-disabled-color: var(--tblr-indigo-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-indigo,.btn-outline.btn-indigo{--tblr-btn-color: var(--tblr-indigo);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-indigo);--tblr-btn-hover-color: var(--tblr-indigo-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-indigo);--tblr-btn-active-color: var(--tblr-indigo-fg);--tblr-btn-active-bg: var(--tblr-indigo);--tblr-btn-active-border-color: var(--tblr-indigo);--tblr-btn-disabled-color: var(--tblr-indigo);--tblr-btn-disabled-border-color: var(--tblr-indigo)}.btn-ghost-indigo,.btn-ghost.btn-indigo{--tblr-btn-color: var(--tblr-indigo);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-indigo-fg);--tblr-btn-hover-bg: var(--tblr-indigo);--tblr-btn-hover-border-color: var(--tblr-indigo);--tblr-btn-active-color: var(--tblr-indigo-fg);--tblr-btn-active-bg: var(--tblr-indigo);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-indigo);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-purple{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-purple-fg, #ffffff);--tblr-btn-bg: var(--tblr-purple);--tblr-btn-hover-color: var(--tblr-purple-fg);--tblr-btn-hover-bg: var(--tblr-purple-darken);--tblr-btn-active-color: var(--tblr-purple-fg);--tblr-btn-active-bg: var(--tblr-purple-darken);--tblr-btn-disabled-bg: var(--tblr-purple);--tblr-btn-disabled-color: var(--tblr-purple-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-purple,.btn-outline.btn-purple{--tblr-btn-color: var(--tblr-purple);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-purple);--tblr-btn-hover-color: var(--tblr-purple-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-purple);--tblr-btn-active-color: var(--tblr-purple-fg);--tblr-btn-active-bg: var(--tblr-purple);--tblr-btn-active-border-color: var(--tblr-purple);--tblr-btn-disabled-color: var(--tblr-purple);--tblr-btn-disabled-border-color: var(--tblr-purple)}.btn-ghost-purple,.btn-ghost.btn-purple{--tblr-btn-color: var(--tblr-purple);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-purple-fg);--tblr-btn-hover-bg: var(--tblr-purple);--tblr-btn-hover-border-color: var(--tblr-purple);--tblr-btn-active-color: var(--tblr-purple-fg);--tblr-btn-active-bg: var(--tblr-purple);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-purple);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-pink{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-pink-fg, #ffffff);--tblr-btn-bg: var(--tblr-pink);--tblr-btn-hover-color: var(--tblr-pink-fg);--tblr-btn-hover-bg: var(--tblr-pink-darken);--tblr-btn-active-color: var(--tblr-pink-fg);--tblr-btn-active-bg: var(--tblr-pink-darken);--tblr-btn-disabled-bg: var(--tblr-pink);--tblr-btn-disabled-color: var(--tblr-pink-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-pink,.btn-outline.btn-pink{--tblr-btn-color: var(--tblr-pink);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-pink);--tblr-btn-hover-color: var(--tblr-pink-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-pink);--tblr-btn-active-color: var(--tblr-pink-fg);--tblr-btn-active-bg: var(--tblr-pink);--tblr-btn-active-border-color: var(--tblr-pink);--tblr-btn-disabled-color: var(--tblr-pink);--tblr-btn-disabled-border-color: var(--tblr-pink)}.btn-ghost-pink,.btn-ghost.btn-pink{--tblr-btn-color: var(--tblr-pink);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-pink-fg);--tblr-btn-hover-bg: var(--tblr-pink);--tblr-btn-hover-border-color: var(--tblr-pink);--tblr-btn-active-color: var(--tblr-pink-fg);--tblr-btn-active-bg: var(--tblr-pink);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-pink);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-red{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-red-fg, #ffffff);--tblr-btn-bg: var(--tblr-red);--tblr-btn-hover-color: var(--tblr-red-fg);--tblr-btn-hover-bg: var(--tblr-red-darken);--tblr-btn-active-color: var(--tblr-red-fg);--tblr-btn-active-bg: var(--tblr-red-darken);--tblr-btn-disabled-bg: var(--tblr-red);--tblr-btn-disabled-color: var(--tblr-red-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-red,.btn-outline.btn-red{--tblr-btn-color: var(--tblr-red);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-red);--tblr-btn-hover-color: var(--tblr-red-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-red);--tblr-btn-active-color: var(--tblr-red-fg);--tblr-btn-active-bg: var(--tblr-red);--tblr-btn-active-border-color: var(--tblr-red);--tblr-btn-disabled-color: var(--tblr-red);--tblr-btn-disabled-border-color: var(--tblr-red)}.btn-ghost-red,.btn-ghost.btn-red{--tblr-btn-color: var(--tblr-red);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-red-fg);--tblr-btn-hover-bg: var(--tblr-red);--tblr-btn-hover-border-color: var(--tblr-red);--tblr-btn-active-color: var(--tblr-red-fg);--tblr-btn-active-bg: var(--tblr-red);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-red);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-orange{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-orange-fg, #ffffff);--tblr-btn-bg: var(--tblr-orange);--tblr-btn-hover-color: var(--tblr-orange-fg);--tblr-btn-hover-bg: var(--tblr-orange-darken);--tblr-btn-active-color: var(--tblr-orange-fg);--tblr-btn-active-bg: var(--tblr-orange-darken);--tblr-btn-disabled-bg: var(--tblr-orange);--tblr-btn-disabled-color: var(--tblr-orange-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-orange,.btn-outline.btn-orange{--tblr-btn-color: var(--tblr-orange);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-orange);--tblr-btn-hover-color: var(--tblr-orange-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-orange);--tblr-btn-active-color: var(--tblr-orange-fg);--tblr-btn-active-bg: var(--tblr-orange);--tblr-btn-active-border-color: var(--tblr-orange);--tblr-btn-disabled-color: var(--tblr-orange);--tblr-btn-disabled-border-color: var(--tblr-orange)}.btn-ghost-orange,.btn-ghost.btn-orange{--tblr-btn-color: var(--tblr-orange);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-orange-fg);--tblr-btn-hover-bg: var(--tblr-orange);--tblr-btn-hover-border-color: var(--tblr-orange);--tblr-btn-active-color: var(--tblr-orange-fg);--tblr-btn-active-bg: var(--tblr-orange);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-orange);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-yellow{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-yellow-fg, #ffffff);--tblr-btn-bg: var(--tblr-yellow);--tblr-btn-hover-color: var(--tblr-yellow-fg);--tblr-btn-hover-bg: var(--tblr-yellow-darken);--tblr-btn-active-color: var(--tblr-yellow-fg);--tblr-btn-active-bg: var(--tblr-yellow-darken);--tblr-btn-disabled-bg: var(--tblr-yellow);--tblr-btn-disabled-color: var(--tblr-yellow-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-yellow,.btn-outline.btn-yellow{--tblr-btn-color: var(--tblr-yellow);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-yellow);--tblr-btn-hover-color: var(--tblr-yellow-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-yellow);--tblr-btn-active-color: var(--tblr-yellow-fg);--tblr-btn-active-bg: var(--tblr-yellow);--tblr-btn-active-border-color: var(--tblr-yellow);--tblr-btn-disabled-color: var(--tblr-yellow);--tblr-btn-disabled-border-color: var(--tblr-yellow)}.btn-ghost-yellow,.btn-ghost.btn-yellow{--tblr-btn-color: var(--tblr-yellow);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-yellow-fg);--tblr-btn-hover-bg: var(--tblr-yellow);--tblr-btn-hover-border-color: var(--tblr-yellow);--tblr-btn-active-color: var(--tblr-yellow-fg);--tblr-btn-active-bg: var(--tblr-yellow);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-yellow);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-lime{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-lime-fg, #ffffff);--tblr-btn-bg: var(--tblr-lime);--tblr-btn-hover-color: var(--tblr-lime-fg);--tblr-btn-hover-bg: var(--tblr-lime-darken);--tblr-btn-active-color: var(--tblr-lime-fg);--tblr-btn-active-bg: var(--tblr-lime-darken);--tblr-btn-disabled-bg: var(--tblr-lime);--tblr-btn-disabled-color: var(--tblr-lime-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-lime,.btn-outline.btn-lime{--tblr-btn-color: var(--tblr-lime);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-lime);--tblr-btn-hover-color: var(--tblr-lime-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-lime);--tblr-btn-active-color: var(--tblr-lime-fg);--tblr-btn-active-bg: var(--tblr-lime);--tblr-btn-active-border-color: var(--tblr-lime);--tblr-btn-disabled-color: var(--tblr-lime);--tblr-btn-disabled-border-color: var(--tblr-lime)}.btn-ghost-lime,.btn-ghost.btn-lime{--tblr-btn-color: var(--tblr-lime);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-lime-fg);--tblr-btn-hover-bg: var(--tblr-lime);--tblr-btn-hover-border-color: var(--tblr-lime);--tblr-btn-active-color: var(--tblr-lime-fg);--tblr-btn-active-bg: var(--tblr-lime);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-lime);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-green{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-green-fg, #ffffff);--tblr-btn-bg: var(--tblr-green);--tblr-btn-hover-color: var(--tblr-green-fg);--tblr-btn-hover-bg: var(--tblr-green-darken);--tblr-btn-active-color: var(--tblr-green-fg);--tblr-btn-active-bg: var(--tblr-green-darken);--tblr-btn-disabled-bg: var(--tblr-green);--tblr-btn-disabled-color: var(--tblr-green-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-green,.btn-outline.btn-green{--tblr-btn-color: var(--tblr-green);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-green);--tblr-btn-hover-color: var(--tblr-green-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-green);--tblr-btn-active-color: var(--tblr-green-fg);--tblr-btn-active-bg: var(--tblr-green);--tblr-btn-active-border-color: var(--tblr-green);--tblr-btn-disabled-color: var(--tblr-green);--tblr-btn-disabled-border-color: var(--tblr-green)}.btn-ghost-green,.btn-ghost.btn-green{--tblr-btn-color: var(--tblr-green);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-green-fg);--tblr-btn-hover-bg: var(--tblr-green);--tblr-btn-hover-border-color: var(--tblr-green);--tblr-btn-active-color: var(--tblr-green-fg);--tblr-btn-active-bg: var(--tblr-green);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-green);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-teal{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-teal-fg, #ffffff);--tblr-btn-bg: var(--tblr-teal);--tblr-btn-hover-color: var(--tblr-teal-fg);--tblr-btn-hover-bg: var(--tblr-teal-darken);--tblr-btn-active-color: var(--tblr-teal-fg);--tblr-btn-active-bg: var(--tblr-teal-darken);--tblr-btn-disabled-bg: var(--tblr-teal);--tblr-btn-disabled-color: var(--tblr-teal-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-teal,.btn-outline.btn-teal{--tblr-btn-color: var(--tblr-teal);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-teal);--tblr-btn-hover-color: var(--tblr-teal-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-teal);--tblr-btn-active-color: var(--tblr-teal-fg);--tblr-btn-active-bg: var(--tblr-teal);--tblr-btn-active-border-color: var(--tblr-teal);--tblr-btn-disabled-color: var(--tblr-teal);--tblr-btn-disabled-border-color: var(--tblr-teal)}.btn-ghost-teal,.btn-ghost.btn-teal{--tblr-btn-color: var(--tblr-teal);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-teal-fg);--tblr-btn-hover-bg: var(--tblr-teal);--tblr-btn-hover-border-color: var(--tblr-teal);--tblr-btn-active-color: var(--tblr-teal-fg);--tblr-btn-active-bg: var(--tblr-teal);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-teal);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-cyan{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-cyan-fg, #ffffff);--tblr-btn-bg: var(--tblr-cyan);--tblr-btn-hover-color: var(--tblr-cyan-fg);--tblr-btn-hover-bg: var(--tblr-cyan-darken);--tblr-btn-active-color: var(--tblr-cyan-fg);--tblr-btn-active-bg: var(--tblr-cyan-darken);--tblr-btn-disabled-bg: var(--tblr-cyan);--tblr-btn-disabled-color: var(--tblr-cyan-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-cyan,.btn-outline.btn-cyan{--tblr-btn-color: var(--tblr-cyan);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-cyan);--tblr-btn-hover-color: var(--tblr-cyan-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-cyan);--tblr-btn-active-color: var(--tblr-cyan-fg);--tblr-btn-active-bg: var(--tblr-cyan);--tblr-btn-active-border-color: var(--tblr-cyan);--tblr-btn-disabled-color: var(--tblr-cyan);--tblr-btn-disabled-border-color: var(--tblr-cyan)}.btn-ghost-cyan,.btn-ghost.btn-cyan{--tblr-btn-color: var(--tblr-cyan);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-cyan-fg);--tblr-btn-hover-bg: var(--tblr-cyan);--tblr-btn-hover-border-color: var(--tblr-cyan);--tblr-btn-active-color: var(--tblr-cyan-fg);--tblr-btn-active-bg: var(--tblr-cyan);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-cyan);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-x{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-x-fg, #ffffff);--tblr-btn-bg: var(--tblr-x);--tblr-btn-hover-color: var(--tblr-x-fg);--tblr-btn-hover-bg: var(--tblr-x-darken);--tblr-btn-active-color: var(--tblr-x-fg);--tblr-btn-active-bg: var(--tblr-x-darken);--tblr-btn-disabled-bg: var(--tblr-x);--tblr-btn-disabled-color: var(--tblr-x-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-x,.btn-outline.btn-x{--tblr-btn-color: var(--tblr-x);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-x);--tblr-btn-hover-color: var(--tblr-x-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-x);--tblr-btn-active-color: var(--tblr-x-fg);--tblr-btn-active-bg: var(--tblr-x);--tblr-btn-active-border-color: var(--tblr-x);--tblr-btn-disabled-color: var(--tblr-x);--tblr-btn-disabled-border-color: var(--tblr-x)}.btn-ghost-x,.btn-ghost.btn-x{--tblr-btn-color: var(--tblr-x);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-x-fg);--tblr-btn-hover-bg: var(--tblr-x);--tblr-btn-hover-border-color: var(--tblr-x);--tblr-btn-active-color: var(--tblr-x-fg);--tblr-btn-active-bg: var(--tblr-x);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-x);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-facebook{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-facebook-fg, #ffffff);--tblr-btn-bg: var(--tblr-facebook);--tblr-btn-hover-color: var(--tblr-facebook-fg);--tblr-btn-hover-bg: var(--tblr-facebook-darken);--tblr-btn-active-color: var(--tblr-facebook-fg);--tblr-btn-active-bg: var(--tblr-facebook-darken);--tblr-btn-disabled-bg: var(--tblr-facebook);--tblr-btn-disabled-color: var(--tblr-facebook-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-facebook,.btn-outline.btn-facebook{--tblr-btn-color: var(--tblr-facebook);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-facebook);--tblr-btn-hover-color: var(--tblr-facebook-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-facebook);--tblr-btn-active-color: var(--tblr-facebook-fg);--tblr-btn-active-bg: var(--tblr-facebook);--tblr-btn-active-border-color: var(--tblr-facebook);--tblr-btn-disabled-color: var(--tblr-facebook);--tblr-btn-disabled-border-color: var(--tblr-facebook)}.btn-ghost-facebook,.btn-ghost.btn-facebook{--tblr-btn-color: var(--tblr-facebook);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-facebook-fg);--tblr-btn-hover-bg: var(--tblr-facebook);--tblr-btn-hover-border-color: var(--tblr-facebook);--tblr-btn-active-color: var(--tblr-facebook-fg);--tblr-btn-active-bg: var(--tblr-facebook);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-facebook);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-twitter{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-twitter-fg, #ffffff);--tblr-btn-bg: var(--tblr-twitter);--tblr-btn-hover-color: var(--tblr-twitter-fg);--tblr-btn-hover-bg: var(--tblr-twitter-darken);--tblr-btn-active-color: var(--tblr-twitter-fg);--tblr-btn-active-bg: var(--tblr-twitter-darken);--tblr-btn-disabled-bg: var(--tblr-twitter);--tblr-btn-disabled-color: var(--tblr-twitter-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-twitter,.btn-outline.btn-twitter{--tblr-btn-color: var(--tblr-twitter);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-twitter);--tblr-btn-hover-color: var(--tblr-twitter-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-twitter);--tblr-btn-active-color: var(--tblr-twitter-fg);--tblr-btn-active-bg: var(--tblr-twitter);--tblr-btn-active-border-color: var(--tblr-twitter);--tblr-btn-disabled-color: var(--tblr-twitter);--tblr-btn-disabled-border-color: var(--tblr-twitter)}.btn-ghost-twitter,.btn-ghost.btn-twitter{--tblr-btn-color: var(--tblr-twitter);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-twitter-fg);--tblr-btn-hover-bg: var(--tblr-twitter);--tblr-btn-hover-border-color: var(--tblr-twitter);--tblr-btn-active-color: var(--tblr-twitter-fg);--tblr-btn-active-bg: var(--tblr-twitter);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-twitter);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-linkedin{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-linkedin-fg, #ffffff);--tblr-btn-bg: var(--tblr-linkedin);--tblr-btn-hover-color: var(--tblr-linkedin-fg);--tblr-btn-hover-bg: var(--tblr-linkedin-darken);--tblr-btn-active-color: var(--tblr-linkedin-fg);--tblr-btn-active-bg: var(--tblr-linkedin-darken);--tblr-btn-disabled-bg: var(--tblr-linkedin);--tblr-btn-disabled-color: var(--tblr-linkedin-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-linkedin,.btn-outline.btn-linkedin{--tblr-btn-color: var(--tblr-linkedin);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-linkedin);--tblr-btn-hover-color: var(--tblr-linkedin-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-linkedin);--tblr-btn-active-color: var(--tblr-linkedin-fg);--tblr-btn-active-bg: var(--tblr-linkedin);--tblr-btn-active-border-color: var(--tblr-linkedin);--tblr-btn-disabled-color: var(--tblr-linkedin);--tblr-btn-disabled-border-color: var(--tblr-linkedin)}.btn-ghost-linkedin,.btn-ghost.btn-linkedin{--tblr-btn-color: var(--tblr-linkedin);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-linkedin-fg);--tblr-btn-hover-bg: var(--tblr-linkedin);--tblr-btn-hover-border-color: var(--tblr-linkedin);--tblr-btn-active-color: var(--tblr-linkedin-fg);--tblr-btn-active-bg: var(--tblr-linkedin);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-linkedin);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-google{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-google-fg, #ffffff);--tblr-btn-bg: var(--tblr-google);--tblr-btn-hover-color: var(--tblr-google-fg);--tblr-btn-hover-bg: var(--tblr-google-darken);--tblr-btn-active-color: var(--tblr-google-fg);--tblr-btn-active-bg: var(--tblr-google-darken);--tblr-btn-disabled-bg: var(--tblr-google);--tblr-btn-disabled-color: var(--tblr-google-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-google,.btn-outline.btn-google{--tblr-btn-color: var(--tblr-google);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-google);--tblr-btn-hover-color: var(--tblr-google-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-google);--tblr-btn-active-color: var(--tblr-google-fg);--tblr-btn-active-bg: var(--tblr-google);--tblr-btn-active-border-color: var(--tblr-google);--tblr-btn-disabled-color: var(--tblr-google);--tblr-btn-disabled-border-color: var(--tblr-google)}.btn-ghost-google,.btn-ghost.btn-google{--tblr-btn-color: var(--tblr-google);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-google-fg);--tblr-btn-hover-bg: var(--tblr-google);--tblr-btn-hover-border-color: var(--tblr-google);--tblr-btn-active-color: var(--tblr-google-fg);--tblr-btn-active-bg: var(--tblr-google);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-google);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-youtube{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-youtube-fg, #ffffff);--tblr-btn-bg: var(--tblr-youtube);--tblr-btn-hover-color: var(--tblr-youtube-fg);--tblr-btn-hover-bg: var(--tblr-youtube-darken);--tblr-btn-active-color: var(--tblr-youtube-fg);--tblr-btn-active-bg: var(--tblr-youtube-darken);--tblr-btn-disabled-bg: var(--tblr-youtube);--tblr-btn-disabled-color: var(--tblr-youtube-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-youtube,.btn-outline.btn-youtube{--tblr-btn-color: var(--tblr-youtube);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-youtube);--tblr-btn-hover-color: var(--tblr-youtube-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-youtube);--tblr-btn-active-color: var(--tblr-youtube-fg);--tblr-btn-active-bg: var(--tblr-youtube);--tblr-btn-active-border-color: var(--tblr-youtube);--tblr-btn-disabled-color: var(--tblr-youtube);--tblr-btn-disabled-border-color: var(--tblr-youtube)}.btn-ghost-youtube,.btn-ghost.btn-youtube{--tblr-btn-color: var(--tblr-youtube);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-youtube-fg);--tblr-btn-hover-bg: var(--tblr-youtube);--tblr-btn-hover-border-color: var(--tblr-youtube);--tblr-btn-active-color: var(--tblr-youtube-fg);--tblr-btn-active-bg: var(--tblr-youtube);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-youtube);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-vimeo{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-vimeo-fg, #ffffff);--tblr-btn-bg: var(--tblr-vimeo);--tblr-btn-hover-color: var(--tblr-vimeo-fg);--tblr-btn-hover-bg: var(--tblr-vimeo-darken);--tblr-btn-active-color: var(--tblr-vimeo-fg);--tblr-btn-active-bg: var(--tblr-vimeo-darken);--tblr-btn-disabled-bg: var(--tblr-vimeo);--tblr-btn-disabled-color: var(--tblr-vimeo-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-vimeo,.btn-outline.btn-vimeo{--tblr-btn-color: var(--tblr-vimeo);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-vimeo);--tblr-btn-hover-color: var(--tblr-vimeo-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-vimeo);--tblr-btn-active-color: var(--tblr-vimeo-fg);--tblr-btn-active-bg: var(--tblr-vimeo);--tblr-btn-active-border-color: var(--tblr-vimeo);--tblr-btn-disabled-color: var(--tblr-vimeo);--tblr-btn-disabled-border-color: var(--tblr-vimeo)}.btn-ghost-vimeo,.btn-ghost.btn-vimeo{--tblr-btn-color: var(--tblr-vimeo);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-vimeo-fg);--tblr-btn-hover-bg: var(--tblr-vimeo);--tblr-btn-hover-border-color: var(--tblr-vimeo);--tblr-btn-active-color: var(--tblr-vimeo-fg);--tblr-btn-active-bg: var(--tblr-vimeo);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-vimeo);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-dribbble{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-dribbble-fg, #ffffff);--tblr-btn-bg: var(--tblr-dribbble);--tblr-btn-hover-color: var(--tblr-dribbble-fg);--tblr-btn-hover-bg: var(--tblr-dribbble-darken);--tblr-btn-active-color: var(--tblr-dribbble-fg);--tblr-btn-active-bg: var(--tblr-dribbble-darken);--tblr-btn-disabled-bg: var(--tblr-dribbble);--tblr-btn-disabled-color: var(--tblr-dribbble-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-dribbble,.btn-outline.btn-dribbble{--tblr-btn-color: var(--tblr-dribbble);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-dribbble);--tblr-btn-hover-color: var(--tblr-dribbble-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-dribbble);--tblr-btn-active-color: var(--tblr-dribbble-fg);--tblr-btn-active-bg: var(--tblr-dribbble);--tblr-btn-active-border-color: var(--tblr-dribbble);--tblr-btn-disabled-color: var(--tblr-dribbble);--tblr-btn-disabled-border-color: var(--tblr-dribbble)}.btn-ghost-dribbble,.btn-ghost.btn-dribbble{--tblr-btn-color: var(--tblr-dribbble);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-dribbble-fg);--tblr-btn-hover-bg: var(--tblr-dribbble);--tblr-btn-hover-border-color: var(--tblr-dribbble);--tblr-btn-active-color: var(--tblr-dribbble-fg);--tblr-btn-active-bg: var(--tblr-dribbble);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-dribbble);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-github{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-github-fg, #ffffff);--tblr-btn-bg: var(--tblr-github);--tblr-btn-hover-color: var(--tblr-github-fg);--tblr-btn-hover-bg: var(--tblr-github-darken);--tblr-btn-active-color: var(--tblr-github-fg);--tblr-btn-active-bg: var(--tblr-github-darken);--tblr-btn-disabled-bg: var(--tblr-github);--tblr-btn-disabled-color: var(--tblr-github-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-github,.btn-outline.btn-github{--tblr-btn-color: var(--tblr-github);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-github);--tblr-btn-hover-color: var(--tblr-github-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-github);--tblr-btn-active-color: var(--tblr-github-fg);--tblr-btn-active-bg: var(--tblr-github);--tblr-btn-active-border-color: var(--tblr-github);--tblr-btn-disabled-color: var(--tblr-github);--tblr-btn-disabled-border-color: var(--tblr-github)}.btn-ghost-github,.btn-ghost.btn-github{--tblr-btn-color: var(--tblr-github);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-github-fg);--tblr-btn-hover-bg: var(--tblr-github);--tblr-btn-hover-border-color: var(--tblr-github);--tblr-btn-active-color: var(--tblr-github-fg);--tblr-btn-active-bg: var(--tblr-github);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-github);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-instagram{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-instagram-fg, #ffffff);--tblr-btn-bg: var(--tblr-instagram);--tblr-btn-hover-color: var(--tblr-instagram-fg);--tblr-btn-hover-bg: var(--tblr-instagram-darken);--tblr-btn-active-color: var(--tblr-instagram-fg);--tblr-btn-active-bg: var(--tblr-instagram-darken);--tblr-btn-disabled-bg: var(--tblr-instagram);--tblr-btn-disabled-color: var(--tblr-instagram-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-instagram,.btn-outline.btn-instagram{--tblr-btn-color: var(--tblr-instagram);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-instagram);--tblr-btn-hover-color: var(--tblr-instagram-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-instagram);--tblr-btn-active-color: var(--tblr-instagram-fg);--tblr-btn-active-bg: var(--tblr-instagram);--tblr-btn-active-border-color: var(--tblr-instagram);--tblr-btn-disabled-color: var(--tblr-instagram);--tblr-btn-disabled-border-color: var(--tblr-instagram)}.btn-ghost-instagram,.btn-ghost.btn-instagram{--tblr-btn-color: var(--tblr-instagram);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-instagram-fg);--tblr-btn-hover-bg: var(--tblr-instagram);--tblr-btn-hover-border-color: var(--tblr-instagram);--tblr-btn-active-color: var(--tblr-instagram-fg);--tblr-btn-active-bg: var(--tblr-instagram);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-instagram);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-pinterest{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-pinterest-fg, #ffffff);--tblr-btn-bg: var(--tblr-pinterest);--tblr-btn-hover-color: var(--tblr-pinterest-fg);--tblr-btn-hover-bg: var(--tblr-pinterest-darken);--tblr-btn-active-color: var(--tblr-pinterest-fg);--tblr-btn-active-bg: var(--tblr-pinterest-darken);--tblr-btn-disabled-bg: var(--tblr-pinterest);--tblr-btn-disabled-color: var(--tblr-pinterest-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-pinterest,.btn-outline.btn-pinterest{--tblr-btn-color: var(--tblr-pinterest);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-pinterest);--tblr-btn-hover-color: var(--tblr-pinterest-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-pinterest);--tblr-btn-active-color: var(--tblr-pinterest-fg);--tblr-btn-active-bg: var(--tblr-pinterest);--tblr-btn-active-border-color: var(--tblr-pinterest);--tblr-btn-disabled-color: var(--tblr-pinterest);--tblr-btn-disabled-border-color: var(--tblr-pinterest)}.btn-ghost-pinterest,.btn-ghost.btn-pinterest{--tblr-btn-color: var(--tblr-pinterest);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-pinterest-fg);--tblr-btn-hover-bg: var(--tblr-pinterest);--tblr-btn-hover-border-color: var(--tblr-pinterest);--tblr-btn-active-color: var(--tblr-pinterest-fg);--tblr-btn-active-bg: var(--tblr-pinterest);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-pinterest);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-vk{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-vk-fg, #ffffff);--tblr-btn-bg: var(--tblr-vk);--tblr-btn-hover-color: var(--tblr-vk-fg);--tblr-btn-hover-bg: var(--tblr-vk-darken);--tblr-btn-active-color: var(--tblr-vk-fg);--tblr-btn-active-bg: var(--tblr-vk-darken);--tblr-btn-disabled-bg: var(--tblr-vk);--tblr-btn-disabled-color: var(--tblr-vk-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-vk,.btn-outline.btn-vk{--tblr-btn-color: var(--tblr-vk);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-vk);--tblr-btn-hover-color: var(--tblr-vk-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-vk);--tblr-btn-active-color: var(--tblr-vk-fg);--tblr-btn-active-bg: var(--tblr-vk);--tblr-btn-active-border-color: var(--tblr-vk);--tblr-btn-disabled-color: var(--tblr-vk);--tblr-btn-disabled-border-color: var(--tblr-vk)}.btn-ghost-vk,.btn-ghost.btn-vk{--tblr-btn-color: var(--tblr-vk);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-vk-fg);--tblr-btn-hover-bg: var(--tblr-vk);--tblr-btn-hover-border-color: var(--tblr-vk);--tblr-btn-active-color: var(--tblr-vk-fg);--tblr-btn-active-bg: var(--tblr-vk);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-vk);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-rss{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-rss-fg, #ffffff);--tblr-btn-bg: var(--tblr-rss);--tblr-btn-hover-color: var(--tblr-rss-fg);--tblr-btn-hover-bg: var(--tblr-rss-darken);--tblr-btn-active-color: var(--tblr-rss-fg);--tblr-btn-active-bg: var(--tblr-rss-darken);--tblr-btn-disabled-bg: var(--tblr-rss);--tblr-btn-disabled-color: var(--tblr-rss-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-rss,.btn-outline.btn-rss{--tblr-btn-color: var(--tblr-rss);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-rss);--tblr-btn-hover-color: var(--tblr-rss-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-rss);--tblr-btn-active-color: var(--tblr-rss-fg);--tblr-btn-active-bg: var(--tblr-rss);--tblr-btn-active-border-color: var(--tblr-rss);--tblr-btn-disabled-color: var(--tblr-rss);--tblr-btn-disabled-border-color: var(--tblr-rss)}.btn-ghost-rss,.btn-ghost.btn-rss{--tblr-btn-color: var(--tblr-rss);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-rss-fg);--tblr-btn-hover-bg: var(--tblr-rss);--tblr-btn-hover-border-color: var(--tblr-rss);--tblr-btn-active-color: var(--tblr-rss-fg);--tblr-btn-active-bg: var(--tblr-rss);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-rss);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-flickr{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-flickr-fg, #ffffff);--tblr-btn-bg: var(--tblr-flickr);--tblr-btn-hover-color: var(--tblr-flickr-fg);--tblr-btn-hover-bg: var(--tblr-flickr-darken);--tblr-btn-active-color: var(--tblr-flickr-fg);--tblr-btn-active-bg: var(--tblr-flickr-darken);--tblr-btn-disabled-bg: var(--tblr-flickr);--tblr-btn-disabled-color: var(--tblr-flickr-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-flickr,.btn-outline.btn-flickr{--tblr-btn-color: var(--tblr-flickr);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-flickr);--tblr-btn-hover-color: var(--tblr-flickr-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-flickr);--tblr-btn-active-color: var(--tblr-flickr-fg);--tblr-btn-active-bg: var(--tblr-flickr);--tblr-btn-active-border-color: var(--tblr-flickr);--tblr-btn-disabled-color: var(--tblr-flickr);--tblr-btn-disabled-border-color: var(--tblr-flickr)}.btn-ghost-flickr,.btn-ghost.btn-flickr{--tblr-btn-color: var(--tblr-flickr);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-flickr-fg);--tblr-btn-hover-bg: var(--tblr-flickr);--tblr-btn-hover-border-color: var(--tblr-flickr);--tblr-btn-active-color: var(--tblr-flickr-fg);--tblr-btn-active-bg: var(--tblr-flickr);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-flickr);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-bitbucket{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-bitbucket-fg, #ffffff);--tblr-btn-bg: var(--tblr-bitbucket);--tblr-btn-hover-color: var(--tblr-bitbucket-fg);--tblr-btn-hover-bg: var(--tblr-bitbucket-darken);--tblr-btn-active-color: var(--tblr-bitbucket-fg);--tblr-btn-active-bg: var(--tblr-bitbucket-darken);--tblr-btn-disabled-bg: var(--tblr-bitbucket);--tblr-btn-disabled-color: var(--tblr-bitbucket-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-bitbucket,.btn-outline.btn-bitbucket{--tblr-btn-color: var(--tblr-bitbucket);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-bitbucket);--tblr-btn-hover-color: var(--tblr-bitbucket-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-bitbucket);--tblr-btn-active-color: var(--tblr-bitbucket-fg);--tblr-btn-active-bg: var(--tblr-bitbucket);--tblr-btn-active-border-color: var(--tblr-bitbucket);--tblr-btn-disabled-color: var(--tblr-bitbucket);--tblr-btn-disabled-border-color: var(--tblr-bitbucket)}.btn-ghost-bitbucket,.btn-ghost.btn-bitbucket{--tblr-btn-color: var(--tblr-bitbucket);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-bitbucket-fg);--tblr-btn-hover-bg: var(--tblr-bitbucket);--tblr-btn-hover-border-color: var(--tblr-bitbucket);--tblr-btn-active-color: var(--tblr-bitbucket-fg);--tblr-btn-active-bg: var(--tblr-bitbucket);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-bitbucket);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-tabler{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-tabler-fg, #ffffff);--tblr-btn-bg: var(--tblr-tabler);--tblr-btn-hover-color: var(--tblr-tabler-fg);--tblr-btn-hover-bg: var(--tblr-tabler-darken);--tblr-btn-active-color: var(--tblr-tabler-fg);--tblr-btn-active-bg: var(--tblr-tabler-darken);--tblr-btn-disabled-bg: var(--tblr-tabler);--tblr-btn-disabled-color: var(--tblr-tabler-fg);--tblr-btn-box-shadow: var(--tblr-shadow-input)}.btn-outline-tabler,.btn-outline.btn-tabler{--tblr-btn-color: var(--tblr-tabler);--tblr-btn-bg: transparent;--tblr-btn-border-color: var(--tblr-tabler);--tblr-btn-hover-color: var(--tblr-tabler-fg);--tblr-btn-hover-border-color: transparent;--tblr-btn-hover-bg: var(--tblr-tabler);--tblr-btn-active-color: var(--tblr-tabler-fg);--tblr-btn-active-bg: var(--tblr-tabler);--tblr-btn-active-border-color: var(--tblr-tabler);--tblr-btn-disabled-color: var(--tblr-tabler);--tblr-btn-disabled-border-color: var(--tblr-tabler)}.btn-ghost-tabler,.btn-ghost.btn-tabler{--tblr-btn-color: var(--tblr-tabler);--tblr-btn-bg: transparent;--tblr-btn-border-color: transparent;--tblr-btn-hover-color: var(--tblr-tabler-fg);--tblr-btn-hover-bg: var(--tblr-tabler);--tblr-btn-hover-border-color: var(--tblr-tabler);--tblr-btn-active-color: var(--tblr-tabler-fg);--tblr-btn-active-bg: var(--tblr-tabler);--tblr-btn-active-border-color: transparent;--tblr-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);--tblr-btn-disabled-color: var(--tblr-tabler);--tblr-btn-disabled-bg: transparent;--tblr-btn-disabled-border-color: transparent;--tblr-gradient: none;--tblr-btn-box-shadow: none}.btn-sm,.btn-group-sm>.btn{--tblr-btn-line-height: 1.3333333333;--tblr-btn-icon-size: 1rem}.btn-lg,.btn-group-lg>.btn{--tblr-btn-line-height: 1.5rem;--tblr-btn-icon-size: 1.5rem}.btn-xl,.btn-group-xl>.btn{--tblr-btn-line-height: 2;--tblr-btn-icon-size: 2rem;--tblr-btn-padding-y: .6875rem;--tblr-btn-padding-x: 2rem;--tblr-btn-font-size: 1.5rem}.btn-pill{padding-right:1.5em;padding-left:1.5em;border-radius:10rem}.btn-pill[class*=btn-icon]{padding:.375rem 15px}.btn-square{border-radius:0}.btn-icon,.btn-action{padding-left:0;padding-right:0}.btn-icon .icon,.btn-action .icon{margin:calc(-1 * var(--tblr-btn-padding-x))}.btn-list{--tblr-list-gap: .5rem;display:flex;flex-wrap:wrap;gap:var(--tblr-list-gap)}.btn-floating{position:fixed;z-index:1030;bottom:1rem;left:1rem;box-shadow:var(--tblr-shadow-dropdown)}.btn-loading{position:relative;color:transparent!important;text-shadow:none!important;pointer-events:none}.btn-loading>*{opacity:0}.btn-loading:after{content:"";display:inline-block;vertical-align:text-bottom;border:2px var(--tblr-border-style) currentColor;border-right-color:transparent;border-radius:100rem;color:var(--tblr-btn-color);position:absolute;width:var(--tblr-btn-icon-size);height:var(--tblr-btn-icon-size);left:calc(50% - var(--tblr-btn-icon-size) / 2);top:calc(50% - var(--tblr-btn-icon-size) / 2);animation:spinner-border .75s linear infinite}.btn-action{--tblr-border-color: transparent;color:var(--tblr-secondary);border-radius:var(--tblr-border-radius);background:transparent;box-shadow:none}.btn-action:after{content:none}.btn-action:focus{outline:none;box-shadow:none}.btn-action:hover,.btn-action.show{color:var(--tblr-body-color);background:var(--tblr-active-bg);border-color:transparent}.btn-action.show{color:var(--tblr-primary)}.btn-actions{display:flex}.btn-animate-icon .icon{transition:transform .3s ease}.btn-animate-icon:hover .icon,.btn-animate-icon:focus-visible .icon{transform:translate(4px)}.btn-animate-icon.btn-animate-icon-rotate:hover .icon,.btn-animate-icon.btn-animate-icon-rotate:focus-visible .icon{transform:rotate(90deg)}.btn-animate-icon.btn-animate-icon-move-start:hover .icon,.btn-animate-icon.btn-animate-icon-move-start:focus-visible .icon{transform:translate(-4px)}.btn-animate-icon.btn-animate-icon-pulse:hover .icon,.btn-animate-icon.btn-animate-icon-pulse:focus-visible .icon{transform:none;animation:pulse .9s}.btn-animate-icon.btn-animate-icon-shake:hover .icon,.btn-animate-icon.btn-animate-icon-shake:focus-visible .icon{transform:none;animation:shake .9s}.btn-animate-icon.btn-animate-icon-tada:hover .icon,.btn-animate-icon.btn-animate-icon-tada:focus-visible .icon{transform:none;animation:tada .9s}.btn-group,.btn-group-vertical{box-shadow:var(--tblr-shadow-input)}.btn-group>.btn-check:checked+.btn,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:5}.btn-group>.btn-check:focus+.btn,.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus{z-index:1}.calendar{display:block;font-size:.765625rem;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius)}.calendar-nav{display:flex;align-items:center}.calendar-title{flex:1;text-align:center}.calendar-body,.calendar-header{display:flex;flex-wrap:wrap;justify-content:flex-start;padding:.5rem 0}.calendar-header{color:var(--tblr-secondary)}.calendar-date{flex:0 0 14.2857142857%;max-width:14.2857142857%;padding:.2rem;text-align:center;border:0}.calendar-date.prev-month,.calendar-date.next-month{opacity:.25}.calendar-date .date-item{position:relative;display:inline-block;width:1.4rem;height:1.4rem;line-height:1.4rem;color:#66758c;text-align:center;text-decoration:none;white-space:nowrap;vertical-align:middle;cursor:pointer;background:0 0;border:var(--tblr-border-width) var(--tblr-border-style) transparent;border-radius:100rem;outline:0;transition:background .3s,border .3s,box-shadow .32s,color .3s}@media(prefers-reduced-motion:reduce){.calendar-date .date-item{transition:none}}.calendar-date .date-item:hover{color:var(--tblr-primary);text-decoration:none;background:#fefeff;border-color:var(--tblr-border-color)}.calendar-date .date-today{color:var(--tblr-primary);border-color:var(--tblr-border-color)}.calendar-range{position:relative}.calendar-range:before{position:absolute;top:50%;right:0;left:0;height:1.4rem;content:"";background:rgba(var(--tblr-primary-rgb),.1);transform:translateY(-50%)}.calendar-range.range-start .date-item,.calendar-range.range-end .date-item{color:#fff;background:var(--tblr-primary);border-color:var(--tblr-primary)}.calendar-range.range-start:before{left:50%}.calendar-range.range-end:before{right:50%}.carousel-indicators-vertical{left:auto;top:0;margin:0 1rem 0 0;flex-direction:column}.carousel-indicators-vertical [data-bs-target]{margin:3px 0;width:3px;height:30px;border:0;border-left:10px var(--tblr-border-style) transparent;border-right:10px var(--tblr-border-style) transparent}.carousel-indicators-dot [data-bs-target]{width:.5rem;height:.5rem;border-radius:100rem;border:10px var(--tblr-border-style) transparent;margin:0}.carousel-indicators-thumb [data-bs-target]{width:2rem;height:auto;background:no-repeat center/cover;border:0;border-radius:var(--tblr-border-radius);box-shadow:rgba(var(--tblr-body-color-rgb),.04) 0 2px 4px;margin:0 3px;opacity:.75}@media(min-width:992px){.carousel-indicators-thumb [data-bs-target]{width:4rem}}.carousel-indicators-thumb [data-bs-target]:before{content:"";padding-top:var(--tblr-aspect-ratio, 100%);display:block}.carousel-indicators-thumb.carousel-indicators-vertical [data-bs-target]{margin:3px 0}.carousel-caption-background{background:red;position:absolute;left:0;right:0;bottom:0;height:90%;background:linear-gradient(0deg,#1f2937e6,#1f293700)}.card{transition:transform .3s ease-out,opacity .3s ease-out,box-shadow .3s ease-out}@media(prefers-reduced-motion:reduce){.card{transition:none}}@media print{.card{border:none;box-shadow:none}}a.card{color:inherit}a.card:hover{text-decoration:none;box-shadow:rgba(var(--tblr-body-color-rgb),.16) 0 2px 16px}.card .card{box-shadow:none}.card-borderless,.card-borderless .card-header,.card-borderless .card-footer{border-color:transparent}.card-stamp{--tblr-stamp-size: 7rem;position:absolute;top:0;right:0;width:calc(var(--tblr-stamp-size) * 1);height:calc(var(--tblr-stamp-size) * 1);max-height:100%;border-top-right-radius:6px;opacity:.2;overflow:hidden;pointer-events:none}.card-stamp-lg{--tblr-stamp-size: 13rem}.card-stamp-icon{background:var(--tblr-secondary);color:var(--tblr-card-bg, var(--tblr-bg-surface));display:flex;align-items:center;justify-content:center;border-radius:100rem;width:calc(var(--tblr-stamp-size) * 1);height:calc(var(--tblr-stamp-size) * 1);position:relative;top:calc(var(--tblr-stamp-size) * -.25);right:calc(var(--tblr-stamp-size) * -.25);font-size:calc(var(--tblr-stamp-size) * .75);transform:rotate(10deg)}.card-stamp-icon .icon{stroke-width:2;width:calc(var(--tblr-stamp-size) * .75);height:calc(var(--tblr-stamp-size) * .75)}.card-img,.card-img-start{border-top-left-radius:calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)));border-bottom-left-radius:calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)))}.card-img,.card-img-end{border-top-right-radius:calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)));border-bottom-right-radius:calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)))}.card-img-overlay{display:flex;flex-direction:column;justify-content:flex-end}.card-img-overlay-dark{background-image:linear-gradient(180deg,#0000,#0009)}.card-inactive{pointer-events:none;box-shadow:none}.card-inactive .card-body{opacity:.64}.card-active{--tblr-card-border-color: var(--tblr-primary);--tblr-card-bg: var(--tblr-active-bg)}.card-btn{display:flex;align-items:center;justify-content:center;padding:1rem 1.25rem;text-align:center;transition:background .3s}@media(prefers-reduced-motion:reduce){.card-btn{transition:none}}.card-btn{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);flex:1;color:inherit;font-weight:var(--tblr-font-weight-medium)}.card-btn:hover{text-decoration:none;background:rgba(var(--tblr-primary-rgb),.04)}.card-btn+.card-btn{border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.card-stacked{--tblr-card-stacked-offset: .25rem;position:relative}.card-stacked:after{position:absolute;top:calc(-1 * var(--tblr-card-stacked-offset));right:var(--tblr-card-stacked-offset);left:var(--tblr-card-stacked-offset);height:var(--tblr-card-stacked-offset);content:"";background:var(--tblr-card-bg, var(--tblr-bg-surface));border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-card-border-color);border-radius:var(--tblr-card-border-radius) var(--tblr-card-border-radius) 0 0}.card-cover{position:relative;padding:1rem 1.25rem;background:#666 no-repeat center/cover}.card-cover:before{position:absolute;inset:0;content:"";background:#1f29377a}.card-cover:first-child,.card-cover:first-child:before{border-radius:6px 6px 0 0}.card-cover-blurred:before{backdrop-filter:blur(2px)}.card-actions{margin:-.5rem -.5rem -.5rem auto;padding-left:.5rem}.card-actions a{text-decoration:none}.card-header{color:inherit;display:flex;align-items:center;background:transparent}.card-header:first-child{border-radius:var(--tblr-card-border-radius) var(--tblr-card-border-radius) 0 0}.card-header-light{border-bottom-color:transparent;background:var(--tblr-bg-surface-tertiary)}.card-header-tabs{background:var(--tblr-bg-surface-tertiary);flex:1;margin:calc(var(--tblr-card-cap-padding-y) * -1) calc(var(--tblr-card-cap-padding-x) * -1) calc(var(--tblr-card-cap-padding-y) * -1);padding:calc(var(--tblr-card-cap-padding-y) * .5) calc(var(--tblr-card-cap-padding-x) * .5) 0;border-radius:var(--tblr-card-border-radius) var(--tblr-card-border-radius) 0 0}.card-header-pills{flex:1;margin-top:-.5rem;margin-bottom:-.5rem}.card-rotate-left,.card-rotate-start{transform:rotate(-1.5deg)}.card-rotate-right,.card-rotate-end{transform:rotate(1.5deg)}.card-link{color:inherit}.card-link:hover{color:inherit;text-decoration:none;box-shadow:0 1px 6px #00000014}.card-link-rotate:hover{transform:rotate(1.5deg);opacity:1}.card-link-pop:hover{transform:translateY(-2px);opacity:1}.card-footer{margin-top:auto}.card-footer:last-child{border-radius:0 0 var(--tblr-card-border-radius) var(--tblr-card-border-radius)}.card-footer-transparent{background:transparent;border-color:transparent;padding-top:0}.card-footer-borderless{border-top:none}.card-progress{height:.25rem}.card-progress:last-child{border-radius:0 0 2px 2px}.card-progress:first-child{border-radius:2px 2px 0 0}.card-meta{color:var(--tblr-secondary)}.card-title{display:block;margin:0 0 1rem;font-size:1rem;font-weight:var(--tblr-font-weight-medium);color:inherit;line-height:1.5rem}a.card-title:hover{color:inherit}.card-header .card-title{margin:0}.card-subtitle{margin-bottom:1.25rem;color:var(--tblr-secondary);font-weight:400}.card-header .card-subtitle{margin:0}.card-title .card-subtitle{margin:0 0 0 .25rem;font-size:.875rem}.card-body{position:relative}.card-body>:last-child{margin-bottom:0}.card-sm>.card-body{padding:1rem}@media(min-width:768px){.card-md>.card-body{padding:2.5rem}}@media(min-width:768px){.card-lg>.card-body{padding:2rem}}@media(min-width:992px){.card-lg>.card-body{padding:4rem}}@media print{.card-body{padding:0}}.card-body+.card-body{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.card-body-scrollable{overflow:auto}.card-options{top:1.5rem;right:.75rem;display:flex;margin-left:auto}.card-options-link{display:inline-block;min-width:1rem;margin-left:.25rem;color:var(--tblr-secondary)}.card-status-top{position:absolute;top:0;right:0;left:0;height:2px;border-radius:var(--tblr-card-border-radius) var(--tblr-card-border-radius) 0 0}.card-status-start{position:absolute;right:auto;bottom:0;width:2px;height:100%;border-radius:var(--tblr-card-border-radius) 0 0 var(--tblr-card-border-radius)}.card-status-bottom{position:absolute;top:initial;bottom:0;width:100%;height:2px;border-radius:0 0 var(--tblr-card-border-radius) var(--tblr-card-border-radius)}.card-table{margin-bottom:0!important}.card-table tr td:first-child,.card-table tr th:first-child{padding-left:1.25rem;border-left:0}.card-table tr td:last-child,.card-table tr th:last-child{padding-right:1.25rem;border-right:0}.card-table thead tr:first-child,.card-table tbody tr:first-child,.card-table tfoot tr:first-child,.card-table thead tr:first-child td,.card-table thead tr:first-child th,.card-table tbody tr:first-child td,.card-table tbody tr:first-child th,.card-table tfoot tr:first-child td,.card-table tfoot tr:first-child th{border-top:0}.card-body+.card-table{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-table-border-color)}.card-code{padding:0}.card-code .highlight{margin:0;border:0}.card-code pre{margin:0!important;border:0!important}.card-chart{position:relative;z-index:1;height:3.5rem}.card-avatar{margin-left:auto;margin-right:auto;box-shadow:0 0 0 .25rem var(--tblr-card-bg, var(--tblr-bg-surface));margin-top:calc(-1 * var(--tblr-avatar-size) * .5)}.card-body+.card-list-group{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.card-list-group .list-group-item{padding-right:1.25rem;padding-left:1.25rem;border-right:0;border-left:0;border-radius:0}.card-list-group .list-group-item:last-child{border-bottom:0}.card-list-group .list-group-item:first-child{border-top:0}.card-tabs .nav-tabs{position:relative;z-index:1000;border-bottom:0}.card-tabs .nav-tabs .nav-link{background:var(--tblr-bg-surface-tertiary);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.card-tabs .nav-tabs .nav-link.active,.card-tabs .nav-tabs .nav-link:active,.card-tabs .nav-tabs .nav-link:hover{border-color:var(--tblr-border-color-translucent);color:var(--tblr-body-color)}.card-tabs .nav-tabs .nav-link.active{color:inherit;background:var(--tblr-card-bg, var(--tblr-bg-surface));border-bottom-color:transparent}.card-tabs .nav-tabs .nav-item:not(:first-child) .nav-link{border-top-left-radius:0}.card-tabs .nav-tabs .nav-item:not(:last-child) .nav-link{border-top-right-radius:0}.card-tabs .nav-tabs .nav-item+.nav-item{margin-left:calc(-1 * var(--tblr-border-width))}.card-tabs .nav-tabs-bottom,.card-tabs .nav-tabs-bottom .nav-link{margin-bottom:0}.card-tabs .nav-tabs-bottom .nav-link.active{border-top-color:transparent}.card-tabs .nav-tabs-bottom .nav-item{margin-top:calc(-1 * var(--tblr-border-width));margin-bottom:0}.card-tabs .nav-tabs-bottom .nav-item .nav-link{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);border-radius:0 0 var(--tblr-border-radius-lg) var(--tblr-border-radius-lg)}.card-tabs .nav-tabs-bottom .nav-item:not(:first-child) .nav-link{border-bottom-left-radius:0}.card-tabs .nav-tabs-bottom .nav-item:not(:last-child) .nav-link{border-bottom-right-radius:0}.card-tabs .card{border-bottom-left-radius:0}.card-tabs .nav-tabs+.tab-content .card{border-bottom-left-radius:var(--tblr-card-border-radius);border-top-left-radius:0}.card-note{--tblr-card-bg: #fff7dd;--tblr-card-border-color: #fff1c9}.btn-close{--tblr-btn-close-color: currentColor;--tblr-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%231f2937'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e");--tblr-btn-close-opacity: .4;--tblr-btn-close-hover-opacity: .75;--tblr-btn-close-focus-shadow: 0 0 0 .25rem rgba(var(--tblr-primary-rgb), .25);--tblr-btn-close-focus-opacity: 1;--tblr-btn-close-disabled-opacity: .25;--tblr-btn-close-size: 1em;width:var(--tblr-btn-close-size);height:var(--tblr-btn-close-size);padding:.25em;color:var(--tblr-btn-close-color);mask:var(--tblr-btn-close-bg) no-repeat center/calc(var(--tblr-btn-close-size) * .75);background-color:var(--tblr-btn-close-color);border:0;border-radius:var(--tblr-border-radius);opacity:var(--tblr-btn-close-opacity);cursor:pointer;display:block}.btn-close:hover{color:var(--tblr-btn-close-color);text-decoration:none;opacity:var(--tblr-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--tblr-btn-close-focus-shadow);opacity:var(--tblr-btn-close-focus-opacity)}.btn-close:disabled,.btn-close.disabled{pointer-events:none;user-select:none;opacity:var(--tblr-btn-close-disabled-opacity)}.dropdown-menu{user-select:none;background-clip:border-box}.dropdown-menu.card{padding:0;min-width:25rem;display:none}.dropdown-menu.card.show{display:flex}.dropdown-item{min-width:11rem;display:flex;align-items:center;margin:0;line-height:1.4285714286;gap:.5rem}.dropdown-item-icon{width:1.25rem!important;height:1.25rem!important;margin-right:.5rem;color:var(--tblr-secondary);opacity:.7;text-align:center}.dropdown-item-indicator{margin-right:.5rem;margin-left:-.25rem;height:1.25rem;display:inline-flex;line-height:1;vertical-align:bottom;align-items:center}.dropdown-header{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);padding-bottom:.25rem;pointer-events:none}.dropdown-menu-scrollable{height:auto;max-height:13rem;overflow-x:hidden}.dropdown-menu-column{min-width:11rem}.dropdown-menu-column .dropdown-item{min-width:0}.dropdown-menu-columns{display:flex;flex:0 .25rem}.dropdown-menu-arrow:before{content:"";position:absolute;top:-.25rem;left:.75rem;display:block;background:inherit;width:14px;height:14px;transform:rotate(45deg);transform-origin:center;border:1px solid;border-color:inherit;z-index:-1;clip:rect(0px,9px,9px,0px)}.dropdown-menu-arrow.dropdown-menu-end:before{right:.75rem;left:auto}.dropend>.dropdown-menu{margin-top:calc(-.25rem - 1px);margin-left:-.25rem}.dropend .dropdown-toggle:after{margin-left:auto}.dropdown-menu-card{padding:0;min-width:20rem}.dropdown-menu-card>.card{margin:0;border:0;box-shadow:none}.datagrid{--tblr-datagrid-padding: 1.5rem;--tblr-datagrid-item-width: 15rem;display:grid;grid-gap:var(--tblr-datagrid-padding);grid-template-columns:repeat(auto-fit,minmax(var(--tblr-datagrid-item-width),1fr))}.datagrid-title{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);margin-bottom:.25rem}.empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:1rem;text-align:center}@media(min-width:768px){.empty{padding:3rem}}.empty-icon{margin:0 0 1rem;width:3rem;height:3rem;line-height:1;color:var(--tblr-secondary)}.empty-icon svg{width:100%;height:100%}.empty-img{margin:0 0 2rem;line-height:1}.empty-header{margin:0 0 1rem;font-size:4rem;font-weight:var(--tblr-font-weight-light);line-height:1;color:var(--tblr-secondary)}.empty-title{font-size:1.25rem;line-height:1.75rem;font-weight:var(--tblr-font-weight-bold)}.empty-title,.empty-subtitle{margin:0 0 .5rem}.empty-action{margin-top:1.5rem}.empty-bordered{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius)}.row>*{min-width:0}.col-separator{border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.container-slim{--tblr-gutter-x: calc(var(--tblr-page-padding) * 2);--tblr-gutter-y: 0;width:100%;padding-right:calc(var(--tblr-gutter-x) * .5);padding-left:calc(var(--tblr-gutter-x) * .5);margin-right:auto;margin-left:auto;max-width:16rem}.container-tight{--tblr-gutter-x: calc(var(--tblr-page-padding) * 2);--tblr-gutter-y: 0;width:100%;padding-right:calc(var(--tblr-gutter-x) * .5);padding-left:calc(var(--tblr-gutter-x) * .5);margin-right:auto;margin-left:auto;max-width:30rem}.container-narrow{--tblr-gutter-x: calc(var(--tblr-page-padding) * 2);--tblr-gutter-y: 0;width:100%;padding-right:calc(var(--tblr-gutter-x) * .5);padding-left:calc(var(--tblr-gutter-x) * .5);margin-right:auto;margin-left:auto;max-width:61.875rem}.row-0{margin-right:0;margin-left:0}.row-0>.col,.row-0>[class*=col-]{padding-right:0;padding-left:0}.row-0 .card{margin-bottom:0}.row-sm{margin-right:-.375rem;margin-left:-.375rem}.row-sm>.col,.row-sm>[class*=col-]{padding-right:.375rem;padding-left:.375rem}.row-sm .card{margin-bottom:.75rem}.row-md{margin-right:-1.5rem;margin-left:-1.5rem}.row-md>.col,.row-md>[class*=col-]{padding-right:1.5rem;padding-left:1.5rem}.row-md .card{margin-bottom:3rem}.row-lg{margin-right:-3rem;margin-left:-3rem}.row-lg>.col,.row-lg>[class*=col-]{padding-right:3rem;padding-left:3rem}.row-lg .card{margin-bottom:6rem}.row-deck>.col,.row-deck>[class*=col-]{display:flex;align-items:stretch}.row-deck>.col .card,.row-deck>[class*=col-] .card{flex:1 1 auto}.row-cards{--tblr-gutter-x: var(--tblr-page-padding);--tblr-gutter-y: var(--tblr-page-padding);min-width:0}.row-cards .row-cards{flex:1}.space-y{display:flex;flex-direction:column;gap:1rem}.space-x{display:flex;gap:1rem}.space-y-0{display:flex;flex-direction:column;gap:0}.space-x-0{display:flex;gap:0}.space-y-1{display:flex;flex-direction:column;gap:.25rem}.space-x-1{display:flex;gap:.25rem}.space-y-2{display:flex;flex-direction:column;gap:.5rem}.space-x-2{display:flex;gap:.5rem}.space-y-3{display:flex;flex-direction:column;gap:1rem}.space-x-3{display:flex;gap:1rem}.space-y-4{display:flex;flex-direction:column;gap:1.5rem}.space-x-4{display:flex;gap:1.5rem}.space-y-5{display:flex;flex-direction:column;gap:2rem}.space-x-5{display:flex;gap:2rem}.space-y-6{display:flex;flex-direction:column;gap:2.5rem}.space-x-6{display:flex;gap:2.5rem}.divide-y>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y>:not(template):not(:first-child){padding-top:1rem!important}.divide-y>:not(template):not(:last-child){padding-bottom:1rem!important}.divide-x>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x>:not(template):not(:first-child){padding-left:1rem!important}.divide-x>:not(template):not(:last-child){padding-right:1rem!important}.divide-y-0>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-0>:not(template):not(:first-child){padding-top:0!important}.divide-y-0>:not(template):not(:last-child){padding-bottom:0!important}.divide-x-0>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-0>:not(template):not(:first-child){padding-left:0!important}.divide-x-0>:not(template):not(:last-child){padding-right:0!important}.divide-y-1>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-1>:not(template):not(:first-child){padding-top:.25rem!important}.divide-y-1>:not(template):not(:last-child){padding-bottom:.25rem!important}.divide-x-1>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-1>:not(template):not(:first-child){padding-left:.25rem!important}.divide-x-1>:not(template):not(:last-child){padding-right:.25rem!important}.divide-y-2>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-2>:not(template):not(:first-child){padding-top:.5rem!important}.divide-y-2>:not(template):not(:last-child){padding-bottom:.5rem!important}.divide-x-2>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-2>:not(template):not(:first-child){padding-left:.5rem!important}.divide-x-2>:not(template):not(:last-child){padding-right:.5rem!important}.divide-y-3>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-3>:not(template):not(:first-child){padding-top:1rem!important}.divide-y-3>:not(template):not(:last-child){padding-bottom:1rem!important}.divide-x-3>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-3>:not(template):not(:first-child){padding-left:1rem!important}.divide-x-3>:not(template):not(:last-child){padding-right:1rem!important}.divide-y-4>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-4>:not(template):not(:first-child){padding-top:1.5rem!important}.divide-y-4>:not(template):not(:last-child){padding-bottom:1.5rem!important}.divide-x-4>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-4>:not(template):not(:first-child){padding-left:1.5rem!important}.divide-x-4>:not(template):not(:last-child){padding-right:1.5rem!important}.divide-y-5>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-5>:not(template):not(:first-child){padding-top:2rem!important}.divide-y-5>:not(template):not(:last-child){padding-bottom:2rem!important}.divide-x-5>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-5>:not(template):not(:first-child){padding-left:2rem!important}.divide-x-5>:not(template):not(:last-child){padding-right:2rem!important}.divide-y-6>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-6>:not(template):not(:first-child){padding-top:2.5rem!important}.divide-y-6>:not(template):not(:last-child){padding-bottom:2.5rem!important}.divide-x-6>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-6>:not(template):not(:first-child){padding-left:2.5rem!important}.divide-x-6>:not(template):not(:last-child){padding-right:2.5rem!important}.divide-y-fill{display:flex;flex-direction:column;height:100%}.divide-y-fill>:not(template){flex:1;display:flex;justify-content:center;flex-direction:column}.icon{--tblr-icon-size: 1.25rem;width:var(--tblr-icon-size);height:var(--tblr-icon-size);font-size:var(--tblr-icon-size);vertical-align:bottom;stroke-width:1.5}.icon:hover{text-decoration:none}.icon-inline{--tblr-icon-size: 1rem;vertical-align:-.2rem}.icon-filled{fill:currentColor}.icon-sm{--tblr-icon-size: 1rem;stroke-width:1}.icon-md{--tblr-icon-size: 2.5rem;stroke-width:1}.icon-lg{--tblr-icon-size: 3.5rem;stroke-width:1}.icon-pulse{transition:all .15s ease 0s;animation:pulse 2s ease infinite;animation-fill-mode:both}.icon-tada{transition:all .15s ease 0s;animation:tada 3s ease infinite;animation-fill-mode:both}.icon-rotate{transition:all .15s ease 0s;animation:rotate-360 3s linear infinite;animation-fill-mode:both}.img-responsive{--tblr-img-responsive-ratio: 75%;background:no-repeat center/cover;padding-top:var(--tblr-img-responsive-ratio)}.img-responsive-grid{padding-top:calc(var(--tblr-img-responsive-ratio) - var(--tblr-gutter-y) / 2)}.img-responsive-1x1{--tblr-img-responsive-ratio: 100%}.img-responsive-2x1{--tblr-img-responsive-ratio: 50%}.img-responsive-1x2{--tblr-img-responsive-ratio: 200%}.img-responsive-3x1{--tblr-img-responsive-ratio: 33.3333333333%}.img-responsive-1x3{--tblr-img-responsive-ratio: 300%}.img-responsive-4x1{--tblr-img-responsive-ratio: 25%}.img-responsive-1x4{--tblr-img-responsive-ratio: 400%}.img-responsive-4x3{--tblr-img-responsive-ratio: 75%}.img-responsive-3x4{--tblr-img-responsive-ratio: 133.3333333333%}.img-responsive-16x9{--tblr-img-responsive-ratio: 56.25%}.img-responsive-9x16{--tblr-img-responsive-ratio: 177.7777777778%}.img-responsive-21x9{--tblr-img-responsive-ratio: 42.8571428571%}.img-responsive-9x21{--tblr-img-responsive-ratio: 233.3333333333%}.img-bg{background:no-repeat center/cover}textarea[cols]{height:auto}.col-form-label,.form-label{display:block;font-weight:var(--tblr-font-weight-medium)}.col-form-label.required:after,.form-label.required:after{content:"*";margin-left:.25rem;color:#d63939}.form-label-description{float:right;font-weight:var(--tblr-font-weight-normal);color:var(--tblr-secondary)}.form-hint{display:block;color:var(--tblr-secondary)}.form-hint:last-child{margin-bottom:0}.form-hint+.form-control{margin-top:.25rem}.form-label+.form-hint{margin-top:-.25rem}.input-group+.form-hint,.form-control+.form-hint,.form-select+.form-hint{margin-top:.5rem;color:var(--tblr-secondary)}.form-select:-moz-focusring{color:var(--tblr-body-color)}.form-control:-webkit-autofill{box-shadow:0 0 0 1000px var(--tblr-bg-surface-secondary) inset;color:var(--tblr-body-color);-webkit-text-fill-color:var(--tblr-body-color)}.form-control:disabled,.form-control.disabled{color:var(--tblr-secondary);user-select:none}.form-control[size]{width:auto}.form-control-light{background-color:var(--tblr-gray-100);border-color:transparent}.form-control-dark{background-color:#0000001a;color:#fff;border-color:transparent}.form-control-dark:focus{background-color:#0000001a;box-shadow:none;border-color:#ffffff3d}.form-control-dark::placeholder{color:#fff9}.form-control-rounded{border-radius:10rem}.form-control-flush{padding:0;background:none!important;border-color:transparent!important;resize:none;box-shadow:none!important;line-height:inherit}.form-footer{margin-top:2rem}.form-fieldset{padding:1rem;margin-bottom:1rem;background:var(--tblr-bg-surface-secondary);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius)}fieldset:empty{display:none}.form-help{display:inline-flex;font-weight:var(--tblr-font-weight-bold);align-items:center;justify-content:center;width:1.125rem;height:1.125rem;font-size:.75rem;color:var(--tblr-secondary);text-align:center;text-decoration:none;cursor:pointer;user-select:none;background:var(--tblr-gray-100);border-radius:100rem;transition:background-color .3s,color .3s}@media(prefers-reduced-motion:reduce){.form-help{transition:none}}.form-help:hover,.form-help[aria-describedby]{color:#fff;background:var(--tblr-primary)}.input-group{box-shadow:var(--tblr-shadow-input);border-radius:var(--tblr-border-radius)}.input-group .form-control,.input-group .btn{box-shadow:none}.input-group-link{font-size:.75rem}.input-group-flat:focus-within{box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25);border-radius:var(--tblr-border-radius)}.input-group-flat:focus-within .form-control,.input-group-flat:focus-within .input-group-text{border-color:#80c2be!important}.input-group-flat .form-control:focus{border-color:var(--tblr-border-color);box-shadow:none}.input-group-flat .form-control:not(:last-child){border-right:0}.input-group-flat .form-control:not(:first-child){border-left:0}.input-group-flat .input-group-text{background:var(--tblr-bg-forms);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.input-group-flat .input-group-text{transition:none}}.input-group-flat .input-group-text:first-child{padding-right:0}.input-group-flat .input-group-text:last-child{padding-left:0}.form-file-button{margin-left:0;border-left:0}label[for=floating-input]{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(max-width:575.98px){.form-control,.form-select{font-size:1rem}}.input-icon{position:relative}.input-icon .form-control:not(:last-child),.input-icon .form-select:not(:last-child){padding-right:2.5rem}.input-icon .form-control:not(:first-child),.input-icon .form-select:not(:last-child){padding-left:2.5rem}.input-icon-addon{position:absolute;top:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;min-width:2.5rem;color:var(--tblr-icon-color);pointer-events:none;font-size:1.2em}.input-icon-addon:last-child{right:0;left:auto}.form-colorinput{position:relative;display:inline-block;margin:0;line-height:1;cursor:pointer}.form-colorinput-input{position:absolute;z-index:-1;opacity:0}.form-colorinput-color{display:block;width:1.5rem;height:1.5rem;color:#fff;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);border-radius:var(--tblr-border-radius);box-shadow:0 1px 2px #0000000d}.form-colorinput-color:before{position:absolute;top:0;left:0;width:100%;height:100%;content:"";background:no-repeat center center/1.25rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/%3e%3c/svg%3e");opacity:0;transition:opacity .3s}@media(prefers-reduced-motion:reduce){.form-colorinput-color:before{transition:none}}.form-colorinput-input:checked~.form-colorinput-color:before{opacity:1}.form-colorinput-input:focus~.form-colorinput-color{border-color:var(--tblr-primary);box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-colorinput-light .form-colorinput-color:before{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3e%3cpath fill='none' stroke='%231f2937' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/%3e%3c/svg%3e")}.form-imagecheck{--tblr-form-imagecheck-radius: var(--tblr-border-radius);position:relative;margin:0;cursor:pointer}.form-imagecheck-input{position:absolute;z-index:-1;opacity:0}.form-imagecheck-figure{position:relative;display:block;margin:0;user-select:none;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-form-imagecheck-radius)}.form-imagecheck-input:focus~.form-imagecheck-figure{border-color:var(--tblr-primary);box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-imagecheck-input:checked~.form-imagecheck-figure{border-color:var(--tblr-primary)}.form-imagecheck-figure:before{position:absolute;top:.25rem;left:.25rem;z-index:1;display:block;width:1.25rem;height:1.25rem;color:#fff;pointer-events:none;content:"";user-select:none;background:var(--tblr-bg-forms);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius);transition:opacity .3s}@media(prefers-reduced-motion:reduce){.form-imagecheck-figure:before{transition:none}}.form-imagecheck-input:checked~.form-imagecheck-figure:before{background-color:var(--tblr-primary);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/%3e%3c/svg%3e");background-repeat:repeat;background-position:center;background-size:1.25rem;border-color:var(--tblr-border-color-translucent)}.form-imagecheck-input[type=radio]~.form-imagecheck-figure:before{border-radius:50%}.form-imagecheck-input[type=radio]:checked~.form-imagecheck-figure:before{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3ccircle r='3' fill='%23ffffff' cx='8' cy='8' /%3e%3c/svg%3e")}.form-imagecheck-image{max-width:100%;display:block;opacity:.64;transition:opacity .3s}@media(prefers-reduced-motion:reduce){.form-imagecheck-image{transition:none}}.form-imagecheck-image:first-child{border-top-left-radius:calc(var(--tblr-form-imagecheck-radius) - 1px);border-top-right-radius:calc(var(--tblr-form-imagecheck-radius) - 1px)}.form-imagecheck-image:last-child{border-bottom-right-radius:calc(var(--tblr-form-imagecheck-radius) - 1px);border-bottom-left-radius:calc(var(--tblr-form-imagecheck-radius) - 1px)}.form-imagecheck:hover .form-imagecheck-image,.form-imagecheck-input:focus~.form-imagecheck-figure .form-imagecheck-image,.form-imagecheck-input:checked~.form-imagecheck-figure .form-imagecheck-image{opacity:1}.form-imagecheck-caption{padding:.25rem;font-size:.765625rem;color:var(--tblr-secondary);text-align:center;transition:color .3s}@media(prefers-reduced-motion:reduce){.form-imagecheck-caption{transition:none}}.form-imagecheck:hover .form-imagecheck-caption,.form-imagecheck-input:focus~.form-imagecheck-figure .form-imagecheck-caption,.form-imagecheck-input:checked~.form-imagecheck-figure .form-imagecheck-caption{color:var(--tblr-body-color)}.form-selectgroup{display:inline-flex;margin:0 -.5rem -.5rem 0;flex-wrap:wrap}.form-selectgroup .form-selectgroup-item{margin:0 .5rem .5rem 0}.form-selectgroup-vertical{flex-direction:column}.form-selectgroup-item{display:block;position:relative}.form-selectgroup-input{position:absolute;top:0;left:0;z-index:-1;opacity:0}.form-selectgroup-label{position:relative;display:block;min-width:calc(1.25rem + 1.125rem + calc(var(--tblr-border-width) * 2));margin:0;padding:.5625rem 1rem;font-size:.875rem;line-height:1.25rem;color:var(--tblr-secondary);background:var(--tblr-bg-forms);text-align:center;cursor:pointer;user-select:none;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius);box-shadow:var(--tblr-shadow-input);transition:border-color .3s,background .3s,color .3s}@media(prefers-reduced-motion:reduce){.form-selectgroup-label{transition:none}}.form-selectgroup-label .icon:only-child{margin:0 -.25rem}.form-selectgroup-label:hover{color:var(--tblr-body-color)}.form-selectgroup-check{display:inline-block;width:1.25rem;height:1.25rem;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);vertical-align:middle;box-shadow:var(--tblr-shadow-input)}.form-selectgroup-input[type=checkbox]+.form-selectgroup-label .form-selectgroup-check{border-radius:var(--tblr-border-radius)}.form-selectgroup-input[type=radio]+.form-selectgroup-label .form-selectgroup-check{border-radius:50%}.form-selectgroup-input:checked+.form-selectgroup-label .form-selectgroup-check{background-color:var(--tblr-primary);background-repeat:repeat;background-position:center;background-size:1.25rem;border-color:var(--tblr-border-color-translucent)}.form-selectgroup-input[type=checkbox]:checked+.form-selectgroup-label .form-selectgroup-check{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/%3e%3c/svg%3e")}.form-selectgroup-input[type=radio]:checked+.form-selectgroup-label .form-selectgroup-check{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3ccircle r='3' fill='%23ffffff' cx='8' cy='8' /%3e%3c/svg%3e")}.form-selectgroup-check-floated{position:absolute;top:.5625rem;right:.5625rem}.form-selectgroup-input:checked+.form-selectgroup-label{z-index:1;color:var(--tblr-primary);background:rgba(var(--tblr-primary-rgb),.04);border-color:var(--tblr-primary)}.form-selectgroup-input:focus+.form-selectgroup-label{z-index:2;color:var(--tblr-primary);border-color:var(--tblr-primary);box-shadow:0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-selectgroup-boxes .form-selectgroup-label{text-align:left;padding:1.25rem 1rem;color:inherit}.form-selectgroup-boxes .form-selectgroup-input:checked+.form-selectgroup-label{color:inherit}.form-selectgroup-boxes .form-selectgroup-input:checked+.form-selectgroup-label .form-selectgroup-title{color:var(--tblr-primary)}.form-selectgroup-boxes .form-selectgroup-input:checked+.form-selectgroup-label .form-selectgroup-label-content{opacity:1}.form-selectgroup-pills{flex-wrap:wrap;align-items:flex-start}.form-selectgroup-pills .form-selectgroup-item{flex-grow:0}.form-selectgroup-pills .form-selectgroup-label{border-radius:50px}.form-control-color::-webkit-color-swatch{border:none}[type=search]::-webkit-search-cancel-button{-webkit-appearance:none}.form-control::file-selector-button{background-color:var(--tblr-btn-color, var(--tblr-tertiary-bg))}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--tblr-btn-color, var(--tblr-secondary-bg))}.form-check{user-select:none}.form-check.form-check-highlight .form-check-input:not(:checked)~.form-check-label{color:var(--tblr-secondary)}.form-check .form-check-label-off{color:var(--tblr-secondary)}.form-check .form-check-input:checked~.form-check-label-off{display:none}.form-check .form-check-input:not(:checked)~.form-check-label-on{display:none}.form-check-input{background-size:1.25rem;margin-top:0rem;box-shadow:var(--tblr-shadow-input)}.form-switch .form-check-input{transition:background-color .3s,background-position .3s}@media(prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-check-label{display:block}.form-check-label.required:after{content:"*";margin-left:.25rem;color:#d63939}.form-check-description{display:block;color:var(--tblr-secondary);font-size:.75rem;margin-top:.25rem}.form-check-single,.form-check-single .form-check-input{margin:0}.form-switch .form-check-input{height:1.25rem;margin-top:0rem}.form-switch-lg{padding-left:3.5rem;min-height:1.5rem}.form-switch-lg .form-check-input{height:1.5rem;width:2.75rem;background-size:1.5rem;margin-left:-3.5rem}.form-switch-lg .form-check-label{padding-top:.125rem}.form-check-input:checked{border:none}.form-select.is-invalid-lite,.form-control.is-invalid-lite,.form-select.is-valid-lite,.form-control.is-valid-lite{border-color:var(--tblr-border-color)!important}.legend{--tblr-legend-size: .75em;display:inline-block;background:var(--tblr-border-color);width:var(--tblr-legend-size);height:var(--tblr-legend-size);border-radius:var(--tblr-border-radius-sm);border:1px solid var(--tblr-border-color-translucent)}.list-group{margin-left:0;margin-right:0}.list-group-header{background:var(--tblr-bg-surface-tertiary);padding:.5rem 1.25rem;font-size:.75rem;font-weight:var(--tblr-font-weight-medium);line-height:1;text-transform:uppercase;color:var(--tblr-gray-500);border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.list-group-flush>.list-group-header:last-child{border-bottom-width:0}.list-group-item{background-color:inherit}.list-group-item.active{background-color:rgba(var(--tblr-secondary-rgb),.08);border-left-color:#00857d;border-left-width:2px}.list-group-item.disabled,.list-group-item:disabled{color:#6b7280;background-color:rgba(var(--tblr-secondary-rgb),.08)}.list-bordered .list-item{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);margin-top:-1px}.list-bordered .list-item:first-child{border-top:none}.list-group-hoverable .list-group-item:active,.list-group-hoverable .list-group-item:focus,.list-group-hoverable .list-group-item:hover{background-color:rgba(var(--tblr-secondary-rgb),.08)}.list-group-hoverable .list-group-item-actions{opacity:0;transition:opacity .3s}@media(prefers-reduced-motion:reduce){.list-group-hoverable .list-group-item-actions{transition:none}}.list-group-hoverable .list-group-item:hover .list-group-item-actions,.list-group-hoverable .list-group-item-actions.show{opacity:1}.list-group-transparent{--tblr-list-group-border-radius: 0;margin:0 -1.25rem}.list-group-transparent .list-group-item{background:none;border:0}.list-group-transparent .list-group-item .icon{color:var(--tblr-secondary)}.list-group-transparent .list-group-item.active{font-weight:var(--tblr-font-weight-bold);color:inherit;background:var(--tblr-active-bg)}.list-group-transparent .list-group-item.active .icon{color:inherit}.list-separated{display:flex;flex-direction:column;gap:1rem}.list-inline{margin:0}.list-inline-item:not(:last-child){margin-right:auto;margin-inline-end:.5rem}.list-inline-dots .list-inline-item+.list-inline-item:before{content:" \b7 ";margin-inline-end:.5rem}.loader{position:relative;display:block;width:2.5rem;height:2.5rem;color:#066fd1;vertical-align:middle}.loader:after{position:absolute;top:0;left:0;width:100%;height:100%;content:"";border:1px var(--tblr-border-style);border-color:transparent;border-top-color:currentColor;border-left-color:currentColor;border-radius:100rem;animation:rotate-360 .6s linear;animation-iteration-count:infinite}.dimmer{position:relative}.dimmer .loader{position:absolute;top:50%;right:0;left:0;display:none;margin:0 auto;transform:translateY(-50%)}.dimmer.active .loader{display:block}.dimmer.active .dimmer-content{pointer-events:none;opacity:.1}@keyframes animated-dots{0%{transform:translate(-100%)}}.animated-dots{display:inline-block;overflow:hidden;vertical-align:bottom}.animated-dots:after{display:inline-block;content:"...";animation:animated-dots 1.2s steps(4,jump-none) infinite}.modal-content>.btn-close,.modal-header>.btn-close{position:absolute;top:0;right:0;width:3.5rem;height:3.5rem;margin:0;padding:0;z-index:10}.modal-body{scrollbar-color:color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent) transparent}.modal-body::-webkit-scrollbar{width:1rem;height:1rem;transition:background .3s}@media(prefers-reduced-motion:reduce){.modal-body::-webkit-scrollbar{transition:none}}.modal-body::-webkit-scrollbar-thumb{border-radius:1rem;border:5px solid transparent;box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent)}.modal-body::-webkit-scrollbar-track{background:transparent}.modal-body:hover::-webkit-scrollbar-thumb{box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 40%,transparent)}.modal-body::-webkit-scrollbar-corner{background:transparent}.modal-body .modal-title{margin-bottom:1rem}.modal-body+.modal-body{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.modal-status{position:absolute;top:0;left:0;right:0;height:2px;background:var(--tblr-secondary);border-radius:var(--tblr-border-radius-lg) var(--tblr-border-radius-lg) 0 0}.modal-header{align-items:center;min-height:3.5rem;background:transparent;padding:0 3.5rem 0 1.5rem}.modal-title{font-size:1rem;font-weight:var(--tblr-font-weight-bold);color:inherit;line-height:1.4285714286}.modal-footer{padding-top:.75rem;padding-bottom:.75rem}.modal-blur{backdrop-filter:blur(4px)}.modal-full-width{max-width:none;margin:0 .5rem}.nav{--tblr-nav-link-hover-bg: color-mix(in srgb, var(--tblr-nav-link-color) 4%, transparent)}.nav-vertical,.nav-vertical .nav{flex-direction:column;flex-wrap:nowrap}.nav-vertical .nav{margin-left:1.25rem;border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);padding-left:.5rem}.nav-vertical .nav-link.active,.nav-vertical .nav-item.show .nav-link{font-weight:var(--tblr-font-weight-bold);color:var(--tblr-nav-link-active-color)}.nav-vertical.nav-pills{margin:0 -.75rem}.nav-bordered{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.nav-bordered .nav-item+.nav-item{margin-left:1.25rem}.nav-bordered .nav-link{padding-left:0;padding-right:0;margin:0 0 calc(-1 * var(--tblr-border-width));border:0;border-bottom:2px var(--tblr-border-style) transparent}.nav-bordered .nav-link:hover{background-color:transparent}.nav-bordered .nav-link.active,.nav-bordered .nav-item.show .nav-link{color:var(--tblr-primary);border-color:var(--tblr-primary)}.nav-underline .nav-link{border-radius:0}.nav-link{display:flex;transition:color .3s,background-color .3s}@media(prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link{align-items:center}.nav-link:hover,.nav-link:focus{background-color:var(--tblr-nav-link-hover-bg)}.nav-link-toggle{margin-left:auto;padding:0 .25rem;transition:transform .3s}@media(prefers-reduced-motion:reduce){.nav-link-toggle{transition:none}}.nav-link-toggle:after{content:"";display:inline-block;vertical-align:.306em;width:.36em;height:.36em;border-bottom:1px var(--tblr-border-style);border-left:1px var(--tblr-border-style);margin-right:.1em;margin-left:.4em;transform:rotate(-45deg)}.nav-link-toggle:after{margin:0}.nav-link[aria-expanded=true] .nav-link-toggle{transform:rotate(180deg)}.nav-link-icon{width:1.25rem;height:1.25rem;margin-right:.5rem;color:inherit}.nav-link-icon svg{display:block;height:100%}.nav-fill .nav-item .nav-link{justify-content:center}.stars{display:inline-flex;color:#9ca3af;font-size:.75rem}.stars .star:not(:first-child){margin-left:.25rem}.pagination{margin:0;--tblr-pagination-gap: .25rem;user-select:none;gap:var(--tblr-pagination-gap);line-height:var(--tblr-body-line-height)}.page-link{min-width:2rem;border-radius:var(--tblr-pagination-border-radius)}.page-item:not(.active) .page-link:hover{background:var(--tblr-pagination-hover-bg)}.page-text{padding-left:.5rem;padding-right:.5rem}.page-item{text-align:center}.page-item.page-prev,.page-item.page-next{flex:0 0 50%;text-align:left}.page-item.page-next{margin-left:auto;text-align:right}.page-item-subtitle{margin-bottom:2px;font-size:12px;color:var(--tblr-secondary);text-transform:uppercase}.page-item.disabled .page-item-subtitle{color:var(--tblr-disabled-color)}.page-item-title{font-size:1rem;font-weight:var(--tblr-font-weight-normal);color:var(--tblr-body-color)}.page-link:hover .page-item-title{color:#00857d}.page-item.disabled .page-item-title{color:var(--tblr-disabled-color)}.pagination-outline{--tblr-pagination-border-color: var(--tblr-border-color);--tblr-pagination-disabled-border-color: var(--tblr-border-color);--tblr-pagination-border-width: 1px}.pagination-circle{--tblr-pagination-border-radius: var(--tblr-border-radius-pill)}@keyframes progress-indeterminate{0%{right:100%;left:-35%}to,60%{right:-90%;left:100%}}.progress{position:relative;width:100%;line-height:.5rem;appearance:none}.progress::-webkit-progress-bar{background:var(--tblr-progress-bg)}.progress::-webkit-progress-value{background-color:var(--tblr-primary)}.progress::-moz-progress-bar{background-color:var(--tblr-primary)}.progress::-ms-fill{background-color:var(--tblr-primary);border:none}.progress-sm{height:.25rem}.progress-bar{height:100%}.progress-bar-indeterminate:after,.progress-bar-indeterminate:before{position:absolute;top:0;bottom:0;left:0;content:"";background-color:inherit;will-change:left,right}.progress-bar-indeterminate:before{animation:progress-indeterminate 1.5s cubic-bezier(.65,.815,.735,.395) infinite}.progress-separated .progress-bar{box-shadow:0 0 0 2px var(--tblr-card-bg, var(--tblr-bg-surface))}.progressbg{position:relative;padding:.25rem .5rem;display:flex}.progressbg-text{position:relative;z-index:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.progressbg-progress{position:absolute;inset:0;z-index:0;height:100%;background:transparent;pointer-events:none}.progressbg-value{font-weight:var(--tblr-font-weight-medium);margin-left:auto;padding-left:2rem}.ribbon{--tblr-ribbon-margin: .25rem;--tblr-ribbon-border-radius: var(--tblr-border-radius);position:absolute;top:.75rem;right:calc(-1 * var(--tblr-ribbon-margin));z-index:1;padding:.25rem .75rem;font-size:.625rem;font-weight:var(--tblr-font-weight-bold);line-height:1;color:#fff;text-align:center;text-transform:uppercase;background:var(--tblr-primary);border-color:var(--tblr-primary);border-radius:var(--tblr-ribbon-border-radius) 0 var(--tblr-ribbon-border-radius) var(--tblr-ribbon-border-radius);display:inline-flex;align-items:center;justify-content:center;min-height:2rem;min-width:2rem}.ribbon:before{position:absolute;right:0;bottom:100%;width:0;height:0;content:"";filter:brightness(70%);border:calc(var(--tblr-ribbon-margin) * .5) var(--tblr-border-style);border-color:inherit;border-top-color:transparent;border-right-color:transparent}.ribbon.bg-blue{border-color:var(--tblr-blue)}.ribbon.bg-blue-lt{border-color:rgba(var(--tblr-blue-rgb),.1)!important}.ribbon.bg-azure{border-color:var(--tblr-azure)}.ribbon.bg-azure-lt{border-color:rgba(var(--tblr-azure-rgb),.1)!important}.ribbon.bg-indigo{border-color:var(--tblr-indigo)}.ribbon.bg-indigo-lt{border-color:rgba(var(--tblr-indigo-rgb),.1)!important}.ribbon.bg-purple{border-color:var(--tblr-purple)}.ribbon.bg-purple-lt{border-color:rgba(var(--tblr-purple-rgb),.1)!important}.ribbon.bg-pink{border-color:var(--tblr-pink)}.ribbon.bg-pink-lt{border-color:rgba(var(--tblr-pink-rgb),.1)!important}.ribbon.bg-red{border-color:var(--tblr-red)}.ribbon.bg-red-lt{border-color:rgba(var(--tblr-red-rgb),.1)!important}.ribbon.bg-orange{border-color:var(--tblr-orange)}.ribbon.bg-orange-lt{border-color:rgba(var(--tblr-orange-rgb),.1)!important}.ribbon.bg-yellow{border-color:var(--tblr-yellow)}.ribbon.bg-yellow-lt{border-color:rgba(var(--tblr-yellow-rgb),.1)!important}.ribbon.bg-lime{border-color:var(--tblr-lime)}.ribbon.bg-lime-lt{border-color:rgba(var(--tblr-lime-rgb),.1)!important}.ribbon.bg-green{border-color:var(--tblr-green)}.ribbon.bg-green-lt{border-color:rgba(var(--tblr-green-rgb),.1)!important}.ribbon.bg-teal{border-color:var(--tblr-teal)}.ribbon.bg-teal-lt{border-color:rgba(var(--tblr-teal-rgb),.1)!important}.ribbon.bg-cyan{border-color:var(--tblr-cyan)}.ribbon.bg-cyan-lt{border-color:rgba(var(--tblr-cyan-rgb),.1)!important}.ribbon .icon{width:1.25rem;height:1.25rem;font-size:1.25rem}.ribbon-top{top:calc(-1 * var(--tblr-ribbon-margin));right:.75rem;width:2rem;padding:.5rem 0;border-radius:0 var(--tblr-ribbon-border-radius) var(--tblr-ribbon-border-radius) var(--tblr-ribbon-border-radius)}.ribbon-top:before{top:0;right:100%;bottom:auto;border-color:inherit;border-top-color:transparent;border-left-color:transparent}.ribbon-top.ribbon-start{right:auto;left:.75rem}.ribbon-top.ribbon-start:before{top:0;right:100%;left:auto}.ribbon-start{right:auto;left:calc(-1 * var(--tblr-ribbon-margin));border-radius:0 var(--tblr-ribbon-border-radius) var(--tblr-ribbon-border-radius) var(--tblr-ribbon-border-radius)}.ribbon-start:before{top:auto;bottom:100%;left:0;border-color:inherit;border-top-color:transparent;border-left-color:transparent}.ribbon-bottom{top:auto;bottom:.75rem}.ribbon-bookmark{padding-left:.25rem;border-radius:0 0 var(--tblr-ribbon-border-radius) 0}.ribbon-bookmark:after{position:absolute;top:0;right:100%;display:block;width:0;height:0;content:"";border:1rem var(--tblr-border-style);border-color:inherit;border-right-width:0;border-left-color:transparent;border-left-width:.5rem}.ribbon-bookmark.ribbon-left{padding-right:.5rem}.ribbon-bookmark.ribbon-left:after{right:auto;left:100%;border-right-color:transparent;border-right-width:.5rem;border-left-width:0}.ribbon-bookmark.ribbon-top{padding-right:0;padding-bottom:.25rem;padding-left:0;border-radius:0 var(--tblr-ribbon-border-radius) 0 0}.ribbon-bookmark.ribbon-top:after{top:100%;right:0;left:0;border-color:inherit;border-width:1rem;border-top-width:0;border-bottom-color:transparent;border-bottom-width:.5rem}.markdown{line-height:2}.markdown>:first-child{margin-top:0}.markdown>:last-child,.markdown>:last-child .highlight{margin-bottom:0}@media(min-width:768px){.markdown>hr,.markdown>.hr{margin-top:3em;margin-bottom:3em}}.markdown>h1,.markdown>.h1,.markdown>h2,.markdown>.h2,.markdown>h3,.markdown>.h3,.markdown>h4,.markdown>.h4,.markdown>h5,.markdown>.h5,.markdown>h6,.markdown>.h6{font-weight:var(--tblr-font-weight-bold)}.markdown>h2,.markdown>.h2,.markdown>h3,.markdown>.h3,.markdown>h4,.markdown>.h4,.markdown>h5,.markdown>.h5,.markdown>h6,.markdown>.h6{margin-top:2.5rem}.markdown>table{font-size:var(--tblr-body-font-size)}.markdown>blockquote{font-size:1rem;margin:1.5rem 0;padding:.5rem 1.5rem}.markdown>img,.markdown>p>img{border-radius:var(--tblr-border-radius);border:1px solid var(--tblr-border-color)}.markdown pre{max-height:20rem}.placeholder:not(.btn):not([class*=bg-]){background-color:currentColor!important}.placeholder:not(.avatar):not([class*=card-img-]){border-radius:var(--tblr-border-radius)}.nav-segmented{--tblr-nav-bg: var(--tblr-bg-surface-tertiary);--tblr-nav-padding: 2px;--tblr-nav-height: 2.5rem;--tblr-nav-gap: .25rem;--tblr-nav-active-bg: var(--tblr-bg-surface);--tblr-nav-font-size: inherit;--tblr-nav-radius: 6px;--tblr-nav-link-disabled-color: var(--tblr-disabled-color);--tblr-nav-link-gap: .25rem;--tblr-nav-link-padding-x: .75rem;--tblr-nav-link-icon-size: 1.25rem;display:inline-flex;flex-wrap:wrap;gap:var(--tblr-nav-gap);padding:var(--tblr-nav-padding);list-style:none;background:var(--tblr-nav-bg);border-radius:calc(var(--tblr-nav-radius) + var(--tblr-nav-padding));box-shadow:inset 0 0 0 1px #0000000a}.nav-segmented .nav-link{display:inline-flex;gap:calc(.25rem + var(--tblr-nav-link-gap));align-items:center;margin:0;font-size:var(--tblr-nav-font-size);min-width:calc(var(--tblr-nav-height) - 2 * var(--tblr-nav-padding));height:calc(var(--tblr-nav-height) - 2 * var(--tblr-nav-padding));padding:0 calc(var(--tblr-nav-link-padding-x) - 2px);border:1px solid transparent;background:transparent;color:var(--tblr-secondary);text-align:center;text-decoration:none;white-space:nowrap;cursor:pointer;transition:background-color .3s,color .3s;border-radius:var(--tblr-nav-radius);flex-grow:1;justify-content:center}.nav-segmented .nav-link:hover,.nav-segmented .nav-link.hover{background:#0000000a;color:var(--tblr-body-color)}.nav-segmented .nav-link.disabled,.nav-segmented .nav-link:disabled{color:var(--tblr-nav-link-disabled-color);cursor:not-allowed}.nav-segmented .nav-link-input:checked+.nav-link,.nav-segmented .nav-link.active{color:var(--tblr-body-color);background:var(--tblr-nav-active-bg);border-color:var(--tblr-border-color)}.nav-segmented .nav-link-input{display:none}.nav-segmented .nav-link-icon{width:var(--tblr-nav-link-icon-size);height:var(--tblr-nav-link-icon-size);margin:0 -.25rem;color:inherit}.nav-segmented-vertical{flex-direction:column}.nav-segmented-vertical .nav-link{justify-content:flex-start}.nav-sm{--tblr-nav-height: 2rem;--tblr-nav-font-size: var(--tblr-font-size-h5);--tblr-nav-radius: 4px;--tblr-nav-link-padding-x: .5rem;--tblr-nav-link-gap: .25rem;--tblr-nav-link-icon-size: 1rem}.nav-lg{--tblr-nav-height: 3rem;--tblr-nav-font-size: var(--tblr-font-size-h3);--tblr-nav-radius: 8px;--tblr-nav-link-padding-x: 1rem;--tblr-nav-link-gap: .5rem;--tblr-nav-link-icon-size: 1.5rem}.steps{--tblr-steps-color: var(--tblr-primary);--tblr-steps-inactive-color: var(--tblr-border-color);--tblr-steps-dot-size: .5rem;--tblr-steps-border-width: 2px;display:flex;flex-wrap:nowrap;width:100%;padding:0;margin:0;list-style:none}.steps-blue{--tblr-steps-color: var(--tblr-blue)}.steps-blue-lt{--tblr-steps-color: var(--tblr-blue-lt)}.steps-azure{--tblr-steps-color: var(--tblr-azure)}.steps-azure-lt{--tblr-steps-color: var(--tblr-azure-lt)}.steps-indigo{--tblr-steps-color: var(--tblr-indigo)}.steps-indigo-lt{--tblr-steps-color: var(--tblr-indigo-lt)}.steps-purple{--tblr-steps-color: var(--tblr-purple)}.steps-purple-lt{--tblr-steps-color: var(--tblr-purple-lt)}.steps-pink{--tblr-steps-color: var(--tblr-pink)}.steps-pink-lt{--tblr-steps-color: var(--tblr-pink-lt)}.steps-red{--tblr-steps-color: var(--tblr-red)}.steps-red-lt{--tblr-steps-color: var(--tblr-red-lt)}.steps-orange{--tblr-steps-color: var(--tblr-orange)}.steps-orange-lt{--tblr-steps-color: var(--tblr-orange-lt)}.steps-yellow{--tblr-steps-color: var(--tblr-yellow)}.steps-yellow-lt{--tblr-steps-color: var(--tblr-yellow-lt)}.steps-lime{--tblr-steps-color: var(--tblr-lime)}.steps-lime-lt{--tblr-steps-color: var(--tblr-lime-lt)}.steps-green{--tblr-steps-color: var(--tblr-green)}.steps-green-lt{--tblr-steps-color: var(--tblr-green-lt)}.steps-teal{--tblr-steps-color: var(--tblr-teal)}.steps-teal-lt{--tblr-steps-color: var(--tblr-teal-lt)}.steps-cyan{--tblr-steps-color: var(--tblr-cyan)}.steps-cyan-lt{--tblr-steps-color: var(--tblr-cyan-lt)}.step-item{position:relative;flex:1 1 0;min-height:1rem;margin-top:0;color:inherit;text-align:center;cursor:default;padding-top:calc(var(--tblr-steps-dot-size))}a.step-item{cursor:pointer}a.step-item:hover{color:inherit}.step-item:after,.step-item:before{background:var(--tblr-steps-color)}.step-item:not(:last-child):after{position:absolute;left:50%;width:100%;content:"";transform:translateY(-50%)}.step-item:after{top:calc(var(--tblr-steps-dot-size) * .5);height:var(--tblr-steps-border-width)}.step-item:before{content:"";position:absolute;top:0;left:50%;z-index:1;box-sizing:content-box;display:flex;align-items:center;justify-content:center;border-radius:100rem;transform:translate(-50%);color:var(--tblr-white);width:var(--tblr-steps-dot-size);height:var(--tblr-steps-dot-size)}.step-item.active{font-weight:var(--tblr-font-weight-bold)}.step-item.active:after{background:var(--tblr-steps-inactive-color)}.step-item.active~.step-item{color:var(--tblr-disabled-color)}.step-item.active~.step-item:after,.step-item.active~.step-item:before{background:var(--tblr-steps-inactive-color)}.steps-counter{--tblr-steps-dot-size: 1.5rem;counter-reset:steps}.steps-counter .step-item{counter-increment:steps}.steps-counter .step-item:before{content:counter(steps)}.steps-vertical{--tblr-steps-dot-offset: 6px;flex-direction:column}.steps-vertical.steps-counter{--tblr-steps-dot-offset: -2px}.steps-vertical .step-item{text-align:left;padding-top:0;padding-left:calc(var(--tblr-steps-dot-size) + 1rem);min-height:auto}.steps-vertical .step-item:not(:first-child){margin-top:1rem}.steps-vertical .step-item:before{top:var(--tblr-steps-dot-offset);left:0;transform:translate(0)}.steps-vertical .step-item:not(:last-child):after{position:absolute;content:"";transform:translate(-50%);top:var(--tblr-steps-dot-offset);left:calc(var(--tblr-steps-dot-size) * .5);width:var(--tblr-steps-border-width);height:calc(100% + 1rem)}@keyframes status-pulsate-main{40%{transform:scale(1.25)}60%{transform:scale(1.25)}}@keyframes status-pulsate-secondary{10%{transform:scale(1)}30%{transform:scale(3)}80%{transform:scale(3)}to{transform:scale(1)}}@keyframes status-pulsate-tertiary{25%{transform:scale(1)}80%{transform:scale(3);opacity:0}to{transform:scale(3);opacity:0}}.status{--tblr-status-height: 1.5rem;--tblr-status-color: #6b7280;--tblr-status-color-rgb: 107, 114, 128;display:inline-flex;align-items:center;height:var(--tblr-status-height);padding:.25rem .75rem;gap:.5rem;color:var(--tblr-status-color);background:rgba(var(--tblr-status-color-rgb),.1);font-size:.875rem;text-transform:none;letter-spacing:normal;border-radius:100rem;font-weight:var(--tblr-font-weight-medium);line-height:1;margin:0}.status .status-dot{background:var(--tblr-status-color)}.status .icon{font-size:1.25rem}.status-lite{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)!important;background:transparent!important;color:var(--tblr-body-color)!important}.status-primary{--tblr-status-color: #00857D;--tblr-status-color-rgb: 0, 133, 125}.status-secondary{--tblr-status-color: #6b7280;--tblr-status-color-rgb: 107, 114, 128}.status-success{--tblr-status-color: #2fb344;--tblr-status-color-rgb: 47, 179, 68}.status-info{--tblr-status-color: #4299e1;--tblr-status-color-rgb: 66, 153, 225}.status-warning{--tblr-status-color: #f59f00;--tblr-status-color-rgb: 245, 159, 0}.status-danger{--tblr-status-color: #d63939;--tblr-status-color-rgb: 214, 57, 57}.status-light{--tblr-status-color: #f9fafb;--tblr-status-color-rgb: 249, 250, 251}.status-dark{--tblr-status-color: #1f2937;--tblr-status-color-rgb: 31, 41, 55}.status-muted{--tblr-status-color: #6b7280;--tblr-status-color-rgb: 107, 114, 128}.status-blue{--tblr-status-color: #066fd1;--tblr-status-color-rgb: 6, 111, 209}.status-azure{--tblr-status-color: #4299e1;--tblr-status-color-rgb: 66, 153, 225}.status-indigo{--tblr-status-color: #4263eb;--tblr-status-color-rgb: 66, 99, 235}.status-purple{--tblr-status-color: #ae3ec9;--tblr-status-color-rgb: 174, 62, 201}.status-pink{--tblr-status-color: #d6336c;--tblr-status-color-rgb: 214, 51, 108}.status-red{--tblr-status-color: #d63939;--tblr-status-color-rgb: 214, 57, 57}.status-orange{--tblr-status-color: #f76707;--tblr-status-color-rgb: 247, 103, 7}.status-yellow{--tblr-status-color: #f59f00;--tblr-status-color-rgb: 245, 159, 0}.status-lime{--tblr-status-color: #74b816;--tblr-status-color-rgb: 116, 184, 22}.status-green{--tblr-status-color: #2fb344;--tblr-status-color-rgb: 47, 179, 68}.status-teal{--tblr-status-color: #0ca678;--tblr-status-color-rgb: 12, 166, 120}.status-cyan{--tblr-status-color: #17a2b8;--tblr-status-color-rgb: 23, 162, 184}.status-dot{--tblr-status-dot-color: var(--tblr-status-color, #6b7280);--tblr-status-size: .5rem;position:relative;display:inline-block;width:var(--tblr-status-size);height:var(--tblr-status-size);background:var(--tblr-status-dot-color);border-radius:100rem}.status-dot-animated:before{content:"";position:absolute;inset:0;z-index:0;background:inherit;border-radius:inherit;opacity:.6;animation:1s linear 2s backwards infinite status-pulsate-tertiary}.status-indicator{--tblr-status-indicator-size: 2.5rem;--tblr-status-indicator-color: var(--tblr-status-color, #6b7280);display:block;position:relative;width:var(--tblr-status-indicator-size);height:var(--tblr-status-indicator-size)}.status-indicator-circle{--tblr-status-circle-size: .75rem;position:absolute;left:50%;top:50%;margin:calc(var(--tblr-status-circle-size) / -2) 0 0 calc(var(--tblr-status-circle-size) / -2);width:var(--tblr-status-circle-size);height:var(--tblr-status-circle-size);border-radius:100rem;background:var(--tblr-status-color)}.status-indicator-circle:nth-child(1){z-index:3}.status-indicator-circle:nth-child(2){z-index:2;opacity:.1}.status-indicator-circle:nth-child(3){z-index:1;opacity:.3}.status-indicator-animated .status-indicator-circle:nth-child(1){animation:2s linear 1s infinite backwards status-pulsate-main}.status-indicator-animated .status-indicator-circle:nth-child(2){animation:2s linear 1s infinite backwards status-pulsate-secondary}.status-indicator-animated .status-indicator-circle:nth-child(3){animation:2s linear 1s infinite backwards status-pulsate-tertiary}.switch-icon{display:inline-block;line-height:1;border:0;padding:0;background:transparent;width:1.25rem;height:1.25rem;vertical-align:bottom;position:relative;cursor:pointer}.switch-icon.disabled{pointer-events:none;opacity:.4}.switch-icon:focus{outline:none}.switch-icon svg{display:block;width:100%;height:100%}.switch-icon .switch-icon-a,.switch-icon .switch-icon-b{display:block;width:100%;height:100%}.switch-icon .switch-icon-a{opacity:1}.switch-icon .switch-icon-b{position:absolute;top:0;left:0;opacity:0}.switch-icon.active .switch-icon-a{opacity:0}.switch-icon.active .switch-icon-b{opacity:1}.switch-icon-fade .switch-icon-a,.switch-icon-fade .switch-icon-b{transition:opacity .5s}@media(prefers-reduced-motion:reduce){.switch-icon-fade .switch-icon-a,.switch-icon-fade .switch-icon-b{transition:none}}.switch-icon-scale .switch-icon-a,.switch-icon-scale .switch-icon-b{transition:opacity .5s,transform 0s .5s}@media(prefers-reduced-motion:reduce){.switch-icon-scale .switch-icon-a,.switch-icon-scale .switch-icon-b{transition:none}}.switch-icon-scale .switch-icon-b{transform:scale(1.5)}.switch-icon-scale.active .switch-icon-a,.switch-icon-scale.active .switch-icon-b{transition:opacity 0s,transform .5s}@media(prefers-reduced-motion:reduce){.switch-icon-scale.active .switch-icon-a,.switch-icon-scale.active .switch-icon-b{transition:none}}.switch-icon-scale.active .switch-icon-b{transform:scale(1)}.switch-icon-flip{perspective:10em}.switch-icon-flip .switch-icon-a,.switch-icon-flip .switch-icon-b{backface-visibility:hidden;transform-style:preserve-3d;transition:opacity 0s .2s,transform .4s ease-in-out}@media(prefers-reduced-motion:reduce){.switch-icon-flip .switch-icon-a,.switch-icon-flip .switch-icon-b{transition:none}}.switch-icon-flip .switch-icon-a{opacity:1;transform:rotateY(0)}.switch-icon-flip .switch-icon-b{opacity:1;transform:rotateY(-180deg)}.switch-icon-flip.active .switch-icon-a{opacity:1;transform:rotateY(180deg)}.switch-icon-flip.active .switch-icon-b{opacity:1;transform:rotateY(0)}.switch-icon-slide-up,.switch-icon-slide-left,.switch-icon-slide-start,.switch-icon-slide-right,.switch-icon-slide-end,.switch-icon-slide-down{overflow:hidden}.switch-icon-slide-up .switch-icon-a,.switch-icon-slide-up .switch-icon-b,.switch-icon-slide-left .switch-icon-a,.switch-icon-slide-left .switch-icon-b,.switch-icon-slide-start .switch-icon-a,.switch-icon-slide-start .switch-icon-b,.switch-icon-slide-right .switch-icon-a,.switch-icon-slide-right .switch-icon-b,.switch-icon-slide-end .switch-icon-a,.switch-icon-slide-end .switch-icon-b,.switch-icon-slide-down .switch-icon-a,.switch-icon-slide-down .switch-icon-b{transition:opacity .3s,transform .3s}@media(prefers-reduced-motion:reduce){.switch-icon-slide-up .switch-icon-a,.switch-icon-slide-up .switch-icon-b,.switch-icon-slide-left .switch-icon-a,.switch-icon-slide-left .switch-icon-b,.switch-icon-slide-start .switch-icon-a,.switch-icon-slide-start .switch-icon-b,.switch-icon-slide-right .switch-icon-a,.switch-icon-slide-right .switch-icon-b,.switch-icon-slide-end .switch-icon-a,.switch-icon-slide-end .switch-icon-b,.switch-icon-slide-down .switch-icon-a,.switch-icon-slide-down .switch-icon-b{transition:none}}.switch-icon-slide-up .switch-icon-a,.switch-icon-slide-left .switch-icon-a,.switch-icon-slide-start .switch-icon-a,.switch-icon-slide-right .switch-icon-a,.switch-icon-slide-end .switch-icon-a,.switch-icon-slide-down .switch-icon-a{transform:translateY(0)}.switch-icon-slide-up .switch-icon-b,.switch-icon-slide-left .switch-icon-b,.switch-icon-slide-start .switch-icon-b,.switch-icon-slide-right .switch-icon-b,.switch-icon-slide-end .switch-icon-b,.switch-icon-slide-down .switch-icon-b{transform:translateY(100%)}.switch-icon-slide-up.active .switch-icon-a,.switch-icon-slide-left.active .switch-icon-a,.switch-icon-slide-start.active .switch-icon-a,.switch-icon-slide-right.active .switch-icon-a,.switch-icon-slide-end.active .switch-icon-a,.switch-icon-slide-down.active .switch-icon-a{transform:translateY(-100%)}.switch-icon-slide-up.active .switch-icon-b,.switch-icon-slide-left.active .switch-icon-b,.switch-icon-slide-start.active .switch-icon-b,.switch-icon-slide-right.active .switch-icon-b,.switch-icon-slide-end.active .switch-icon-b,.switch-icon-slide-down.active .switch-icon-b{transform:translateY(0)}.switch-icon-slide-left .switch-icon-a,.switch-icon-slide-start .switch-icon-a{transform:translate(0)}.switch-icon-slide-left .switch-icon-b,.switch-icon-slide-start .switch-icon-b{transform:translate(100%)}.switch-icon-slide-left.active .switch-icon-a,.switch-icon-slide-start.active .switch-icon-a{transform:translate(-100%)}.switch-icon-slide-left.active .switch-icon-b,.switch-icon-slide-start.active .switch-icon-b,.switch-icon-slide-right .switch-icon-a,.switch-icon-slide-end .switch-icon-a{transform:translate(0)}.switch-icon-slide-right .switch-icon-b,.switch-icon-slide-end .switch-icon-b{transform:translate(-100%)}.switch-icon-slide-right.active .switch-icon-a,.switch-icon-slide-end.active .switch-icon-a{transform:translate(100%)}.switch-icon-slide-right.active .switch-icon-b,.switch-icon-slide-end.active .switch-icon-b{transform:translate(0)}.switch-icon-slide-down .switch-icon-a{transform:translateY(0)}.switch-icon-slide-down .switch-icon-b{transform:translateY(-100%)}.switch-icon-slide-down.active .switch-icon-a{transform:translateY(100%)}.switch-icon-slide-down.active .switch-icon-b{transform:translateY(0)}.table thead th,.markdown>table thead th{background:var(--tblr-bg-surface-tertiary);font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);padding-top:.5rem;padding-bottom:.5rem;white-space:nowrap}@media print{.table thead th,.markdown>table thead th{background:transparent}}.table-responsive .table,.table-responsive .markdown>table{margin-bottom:0}.table-responsive+.card-footer{border-top:0}.table-transparent thead th{background:transparent}.table-nowrap>:not(caption)>*>*{white-space:nowrap}.table-vcenter>:not(caption)>*>*{vertical-align:middle}.table-center>:not(caption)>*>*{text-align:center}.td-truncate{max-width:1px;width:100%}.table-mobile{display:block}.table-mobile thead{display:none}.table-mobile tbody,.table-mobile tr{display:flex;flex-direction:column}.table-mobile td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile .btn{display:block}@media(max-width:575.98px){.table-mobile-sm{display:block}.table-mobile-sm thead{display:none}.table-mobile-sm tbody,.table-mobile-sm tr{display:flex;flex-direction:column}.table-mobile-sm td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile-sm td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile-sm tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile-sm .btn{display:block}}@media(max-width:767.98px){.table-mobile-md{display:block}.table-mobile-md thead{display:none}.table-mobile-md tbody,.table-mobile-md tr{display:flex;flex-direction:column}.table-mobile-md td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile-md td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile-md tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile-md .btn{display:block}}@media(max-width:991.98px){.table-mobile-lg{display:block}.table-mobile-lg thead{display:none}.table-mobile-lg tbody,.table-mobile-lg tr{display:flex;flex-direction:column}.table-mobile-lg td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile-lg td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile-lg tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile-lg .btn{display:block}}@media(max-width:1199.98px){.table-mobile-xl{display:block}.table-mobile-xl thead{display:none}.table-mobile-xl tbody,.table-mobile-xl tr{display:flex;flex-direction:column}.table-mobile-xl td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile-xl td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile-xl tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile-xl .btn{display:block}}@media(max-width:1399.98px){.table-mobile-xxl{display:block}.table-mobile-xxl thead{display:none}.table-mobile-xxl tbody,.table-mobile-xxl tr{display:flex;flex-direction:column}.table-mobile-xxl td{display:block;padding:.5rem!important;border:none;color:var(--tblr-body-color)!important}.table-mobile-xxl td[data-label]:before{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);content:attr(data-label);display:block}.table-mobile-xxl tr{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)}.table-mobile-xxl .btn{display:block}}.table-sort{font:inherit;color:inherit;text-transform:inherit;letter-spacing:inherit;border:0;background:inherit;display:block;width:100%;text-align:inherit;transition:color .3s}@media(prefers-reduced-motion:reduce){.table-sort{transition:none}}.table-sort{margin:-.5rem;padding:.5rem}.table-sort:hover,.table-sort.asc,.table-sort.desc{color:var(--tblr-body-color)}.table-sort:after{content:"";display:inline-flex;width:1rem;height:1rem;vertical-align:bottom;mask-image:url("data:image/svg+xml,");background:currentColor;margin-left:.25rem}.table-sort.asc:after{mask-image:url("data:image/svg+xml,")}.table-sort.desc:after{mask-image:url("data:image/svg+xml,")}.table-borderless thead th{background:transparent}.table-selectable tbody tr .on-checked{display:none}.table-selectable tbody tr .on-unchecked{display:initial}.table-selectable tbody tr:has(.table-selectable-check:checked){background-color:var(--tblr-active-bg)}.table-selectable tbody tr:has(.table-selectable-check:checked) .on-checked{display:initial}.table-selectable tbody tr:has(.table-selectable-check:checked) .on-unchecked{display:none}.tag{--tblr-tag-height: 1.5rem;border:1px solid var(--tblr-border-color);display:inline-flex;align-items:center;height:var(--tblr-tag-height);border-radius:var(--tblr-border-radius);padding:0 .5rem;background:var(--tblr-bg-surface);box-shadow:var(--tblr-shadow-input);gap:.5rem}.tag .btn-close{margin-right:-.25rem;margin-left:-.125rem;padding:0;width:1rem;height:1rem;font-size:.5rem}.tag-badge{--tblr-badge-font-size: .625rem;--tblr-badge-padding-x: .25rem;--tblr-badge-padding-y: .125rem;margin-right:-.25rem}.tag-avatar,.tag-flag,.tag-payment,.tag-icon,.tag-check{margin-left:-.25rem}.tag-icon{color:var(--tblr-secondary);margin-right:-.125rem;width:1rem;height:1rem}.tag-check{width:1rem;height:1rem;background-size:1rem}.tags-list{--tblr-list-gap: .5rem;display:flex;flex-wrap:wrap;gap:var(--tblr-list-gap)}.toast{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);box-shadow:#1f29370a 0 2px 4px}.toast .toast-header{user-select:none}.toast button[data-bs-dismiss=toast]{outline:none}.toast-primary{--tblr-toast-color: #00857D}.toast-secondary{--tblr-toast-color: #6b7280}.toast-success{--tblr-toast-color: #2fb344}.toast-info{--tblr-toast-color: #4299e1}.toast-warning{--tblr-toast-color: #f59f00}.toast-danger{--tblr-toast-color: #d63939}.toast-light{--tblr-toast-color: #f9fafb}.toast-dark{--tblr-toast-color: #1f2937}.toast-muted{--tblr-toast-color: #6b7280}.toast-blue{--tblr-toast-color: #066fd1}.toast-azure{--tblr-toast-color: #4299e1}.toast-indigo{--tblr-toast-color: #4263eb}.toast-purple{--tblr-toast-color: #ae3ec9}.toast-pink{--tblr-toast-color: #d6336c}.toast-red{--tblr-toast-color: #d63939}.toast-orange{--tblr-toast-color: #f76707}.toast-yellow{--tblr-toast-color: #f59f00}.toast-lime{--tblr-toast-color: #74b816}.toast-green{--tblr-toast-color: #2fb344}.toast-teal{--tblr-toast-color: #0ca678}.toast-cyan{--tblr-toast-color: #17a2b8}.toolbar{display:flex;flex-wrap:nowrap;flex-shrink:0;margin:0 -.5rem}.toolbar>*{margin:0 .5rem}.tracking{--tblr-tracking-height: 1.5rem;--tblr-tracking-gap-width: .125rem;--tblr-tracking-block-border-radius: var(--tblr-border-radius);display:flex;gap:var(--tblr-tracking-gap-width)}.tracking-squares{--tblr-tracking-block-border-radius: var(--tblr-border-radius-sm)}.tracking-squares .tracking-block{height:auto}.tracking-squares .tracking-block:before{content:"";display:block;padding-top:100%}.tracking-block{flex:1;border-radius:var(--tblr-tracking-block-border-radius);height:var(--tblr-tracking-height);min-width:.25rem;background:var(--tblr-border-color)}.timeline{--tblr-timeline-icon-size: 2.5rem;position:relative;list-style:none;padding:0}.timeline-event{position:relative}.timeline-event:not(:last-child){margin-bottom:var(--tblr-page-padding)}.timeline-event:not(:last-child):before{content:"";position:absolute;top:var(--tblr-timeline-icon-size);left:calc(var(--tblr-timeline-icon-size) / 2);bottom:calc(-1 * var(--tblr-page-padding));width:var(--tblr-border-width);background-color:var(--tblr-border-color);border-radius:var(--tblr-border-radius)}.timeline-event-icon{position:absolute;display:flex;align-items:center;justify-content:center;width:var(--tblr-timeline-icon-size, 2.5rem);height:var(--tblr-timeline-icon-size, 2.5rem);background:var(--tblr-bg-surface-secondary);color:var(--tblr-secondary);border-radius:var(--tblr-border-radius);z-index:5}.timeline-event-card{margin-left:calc(var(--tblr-timeline-icon-size, 2.5rem) + var(--tblr-page-padding))}.timeline-simple .timeline-event-icon{display:none}.timeline-simple .timeline-event-card{margin-left:0}.hr-text{display:flex;align-items:center;margin:2rem 0;font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary);height:1px}.hr-text:after,.hr-text:before{flex:1 1 auto;height:1px;background-color:var(--tblr-border-color)}.hr-text:before{content:"";margin-right:.5rem}.hr-text:after{content:"";margin-left:.5rem}.hr-text>*:first-child{padding-right:.5rem;padding-left:0;color:var(--tblr-secondary)}.hr-text.hr-text-left:before,.hr-text.hr-text-start:before{content:none}.hr-text.hr-text-left>*:first-child,.hr-text.hr-text-start>*:first-child{padding-right:.5rem;padding-left:.5rem}.hr-text.hr-text-right:before,.hr-text.hr-text-end:before{content:""}.hr-text.hr-text-right:after,.hr-text.hr-text-end:after{content:none}.hr-text.hr-text-right>*:first-child,.hr-text.hr-text-end>*:first-child{padding-right:0;padding-left:.5rem}.card>.hr-text{margin:0}.hr-text-spaceless{margin:-.5rem 0}.lead{color:var(--tblr-secondary);font-size:inherit}a{text-decoration-skip-ink:auto;color:color-mix(in srgb,transparent,var(--tblr-link-color) var(--tblr-link-opacity, 100%))}a:hover{color:color-mix(in srgb,transparent,var(--tblr-link-hover-color) var(--tblr-link-opacity, 100%))}h1 a,h2 a,h3 a,.field-group h2 a,h4 a,h5 a,h6 a,.h1 a,.h2 a,.h3 a,.h4 a,.h5 a,.h6 a,h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover,.h1 a:hover,.h2 a:hover,.h3 a:hover,.h4 a:hover,.h5 a:hover,.h6 a:hover{color:inherit}h1,.h1{font-size:var(--tblr-font-size-h1);line-height:var(--tblr-line-height-h1)}h2,.h2{font-size:var(--tblr-font-size-h2);line-height:var(--tblr-line-height-h2)}h3,.field-group h2,.field-group .h2,.h3{font-size:var(--tblr-font-size-h3);line-height:var(--tblr-line-height-h3)}h4,.h4{font-size:var(--tblr-font-size-h4);line-height:var(--tblr-line-height-h4)}h5,.h5{font-size:var(--tblr-font-size-h5);line-height:var(--tblr-line-height-h5)}h6,.h6{font-size:var(--tblr-font-size-h6);line-height:var(--tblr-line-height-h6)}.fs-base{font-size:var(--tblr-body-font-size)}strong,.strong,b{font-weight:var(--tblr-font-weight-bold)}blockquote{padding:1rem;border-left:2px var(--tblr-border-style) var(--tblr-border-color)}blockquote p{margin-bottom:1rem}blockquote cite{display:block;text-align:right}blockquote cite:before{content:"\2014 "}ul,ol{padding-left:1.5rem}hr,.hr{margin:2rem 0}dl dd:last-child{margin-bottom:0}pre{--tblr-scrollbar-color: var(--tblr-light);padding:1rem;background:var(--tblr-bg-surface-dark);color:var(--tblr-light);border-radius:var(--tblr-border-radius);line-height:1.4285714286}pre{scrollbar-color:color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent) transparent}pre::-webkit-scrollbar{width:1rem;height:1rem;transition:background .3s}@media(prefers-reduced-motion:reduce){pre::-webkit-scrollbar{transition:none}}pre::-webkit-scrollbar-thumb{border-radius:1rem;border:5px solid transparent;box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 20%,transparent)}pre::-webkit-scrollbar-track{background:transparent}pre:hover::-webkit-scrollbar-thumb{box-shadow:inset 0 0 0 1rem color-mix(in srgb,var(--tblr-scrollbar-color, var(--tblr-body-color)) 40%,transparent)}pre::-webkit-scrollbar-corner{background:transparent}pre code{background:transparent;padding:0}code{background:var(--tblr-code-bg);padding:2px 4px;border-radius:var(--tblr-border-radius)}abbr{text-decoration:underline dotted;cursor:help;text-decoration-skip-ink:none}kbd,.kbd{border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);display:inline-block;box-sizing:border-box;max-width:100%;font-size:var(--tblr-font-size-h5);font-weight:var(--tblr-font-weight-medium);line-height:1;vertical-align:baseline;border-radius:var(--tblr-border-radius)}img{max-width:100%;height:auto}.list-unstyled{margin-left:0}::selection,.text-selected{background-color:color-mix(in srgb,var(--tblr-primary) 10%,transparent)}.text-selected{display:inline-block}[class^=link-].disabled,[class*=" link-"].disabled{color:var(--tblr-disabled-color)!important;pointer-events:none}a:hover:has(.icon){text-decoration:none}.link-hoverable{border-radius:var(--tblr-border-radius);transition:background-color .15s ease-in-out}.link-hoverable:hover{text-decoration:none;color:var(--tblr-primary);background:color-mix(in srgb,var(--tblr-secondary) 4%,transparent)}.subheader{font-size:.75rem;font-weight:var(--tblr-font-weight-medium);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary)}.mention{display:inline-block;box-shadow:var(--tblr-shadow-border);border-radius:var(--tblr-border-radius-pill);line-height:1.3333333333em;font-size:.8571428571em;color:var(--tblr-body-color);background:var(--tblr-bg-surface-tertiary);padding:.1666666667em .6666666667em;font-weight:var(--tblr-font-weight-medium)}a.mention{cursor:pointer}a.mention:hover,a.mention.hover{background:var(--tblr-bg-surface-secondary);text-decoration:underline}.mention-avatar,.mention-app,.mention-color{width:1.1666666667em;height:1.1666666667em;border-radius:var(--tblr-border-radius-pill);margin:-.1666666667em .3333333333em 0 -.3333333333em;display:inline-flex;background:no-repeat center center/cover;box-shadow:var(--tblr-shadow-border);vertical-align:middle;text-align:center}.mention-app{box-shadow:none;background:none;border-radius:0}.mention-count{color:var(--tblr-secondary);margin-left:.6666666667em}.text-incorrect{background:color-mix(in srgb,var(--tblr-red) 4%,transparent);text-decoration:underline;text-decoration-thickness:1px;text-decoration-color:var(--tblr-red)}.text-correct{background:color-mix(in srgb,var(--tblr-green) 4%,transparent);text-decoration:underline;text-decoration-thickness:1px;text-decoration-color:var(--tblr-green)}.steps{--tblr-steps-padding: 2rem;--tblr-steps-item-size: 1.5rem;margin-left:1rem;padding-left:var(--tblr-steps-padding);counter-reset:step;border-left:1px solid var(--tblr-border-color);margin-bottom:2rem}.steps h3,.steps .field-group h2,.field-group .steps h2,.steps .field-group .h2,.field-group .steps .h2,.steps .h3{counter-increment:step}.steps h3:not(:first-child),.steps .field-group h2:not(:first-child),.field-group .steps h2:not(:first-child),.steps .field-group .h2:not(:first-child),.field-group .steps .h2:not(:first-child),.steps .h3:not(:first-child){margin-top:2.5rem!important}.steps h3:before,.steps .field-group h2:before,.field-group .steps h2:before,.steps .field-group .h2:before,.field-group .steps .h2:before,.steps .h3:before{content:counter(step);display:inline-block;position:absolute;margin-top:1px;margin-left:calc(-1 * var(--tblr-steps-padding) - var(--tblr-steps-item-size) / 2);width:var(--tblr-steps-item-size);height:var(--tblr-steps-item-size);text-align:center;color:var(--tblr-body-color);border:1px solid var(--tblr-border-color);background:var(--tblr-bg-surface);border-radius:var(--tblr-border-radius);line-height:calc(var(--tblr-steps-item-size) - 2px);font-size:var(--tblr-font-size-h4);font-weight:var(--tblr-font-weight-bold)}.steps>:last-child{margin-bottom:0}.callout{margin-bottom:1.5rem;border:1px solid var(--tblr-primary-200);border-radius:var(--tblr-border-radius);padding:.5rem 1rem;background:var(--tblr-primary-lt)}.callout>:last-child{margin-bottom:0}.chart{display:block;min-height:10rem}.chart text{font-family:inherit}.chart-sm{height:2.5rem}.chart-lg{height:15rem}.chart-square{height:5.75rem}.chart-sparkline{position:relative;width:4rem;height:2.5rem;line-height:1;min-height:0!important}.chart-sparkline-sm{height:1.5rem}.chart-sparkline-square{width:2.5rem}.chart-sparkline-wide{width:6rem}.chart-sparkline-label{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:.625rem}.chart-sparkline-label .icon{width:1rem;height:1rem;font-size:1rem}.offcanvas-header{border-bottom:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.offcanvas-footer{padding:1.5rem}.offcanvas-title{font-size:1rem;font-weight:var(--tblr-font-weight-medium);line-height:1.5rem}.offcanvas-narrow{width:20rem}.chat-bubbles{display:flex;flex-direction:column;gap:1rem}.chat-bubble{background:var(--tblr-bg-surface-secondary);border-radius:var(--tblr-border-radius-lg);padding:1rem;position:relative}.chat-bubble-me{background-color:var(--tblr-primary-lt);box-shadow:none}.chat-bubble-title{margin-bottom:.25rem}.chat-bubble-author{font-weight:600}.chat-bubble-date{color:var(--tblr-secondary)}.chat-bubble-body>*:last-child{margin-bottom:0}.signature{--tblr-signature-padding: var(--tblr-spacer-1);--tblr-signature-border-radius: var(--tblr-border-radius);border:var(--tblr-border-width) solid var(--tblr-border-color);padding:var(--tblr-signature-padding);border-radius:var(--tblr-border-radius)}.signature-canvas{border:var(--tblr-border-width) dashed var(--tblr-border-color);border-radius:calc(var(--tblr-signature-border-radius) - var(--tblr-signature-padding));display:block;cursor:crosshair;width:100%}.clearfix:after{display:block;clear:both;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--tblr-border-width);min-height:1em;background-color:currentcolor;opacity:.16}.stretched-link:after{position:absolute;inset:0;z-index:1;content:""}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden:not(caption),.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption){position:absolute!important}.visually-hidden *,.visually-hidden-focusable:not(:focus):not(:focus-within) *{overflow:hidden!important}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:sticky;top:0;z-index:1020}.sticky-bottom{position:sticky;bottom:0;z-index:1020}@media(min-width:576px){.sticky-sm-top{position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:768px){.sticky-md-top{position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:992px){.sticky-lg-top{position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:1200px){.sticky-xl-top{position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:1400px){.sticky-xxl-top{position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:sticky;bottom:0;z-index:1020}}.ratio{position:relative;width:100%}.ratio:before{display:block;padding-top:var(--tblr-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--tblr-aspect-ratio: 100%}.ratio-2x1{--tblr-aspect-ratio: 50%}.ratio-1x2{--tblr-aspect-ratio: 200%}.ratio-3x1{--tblr-aspect-ratio: 33.3333333333%}.ratio-1x3{--tblr-aspect-ratio: 300%}.ratio-4x1{--tblr-aspect-ratio: 25%}.ratio-1x4{--tblr-aspect-ratio: 400%}.ratio-4x3{--tblr-aspect-ratio: 75%}.ratio-3x4{--tblr-aspect-ratio: 133.3333333333%}.ratio-16x9{--tblr-aspect-ratio: 56.25%}.ratio-9x16{--tblr-aspect-ratio: 177.7777777778%}.ratio-21x9{--tblr-aspect-ratio: 42.8571428571%}.ratio-9x21{--tblr-aspect-ratio: 233.3333333333%}.focus-ring:focus{outline:0;box-shadow:var(--tblr-focus-ring-x, 0) var(--tblr-focus-ring-y, 0) var(--tblr-focus-ring-blur, 0) var(--tblr-focus-ring-width) var(--tblr-focus-ring-color)}.bg-white-overlay{color:#fff;background-color:#f9fafb3d}.bg-dark-overlay{color:#fff;background-color:#1f29373d}.bg-cover{background-repeat:no-repeat;background-size:cover;background-position:center}.bg-primary{background-color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-primary-lt{color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-primary-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-primary{border-color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-primary{--tblr-gradient-from: var(--tblr-primary)}.bg-gradient-to-primary{--tblr-gradient-to: var(--tblr-primary)}.bg-gradient-via-primary{--tblr-gradient-via: var(--tblr-primary);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-primary{color:#f9fafb!important;background-color:RGBA(var(--tblr-primary-rgb),var(--tblr-bg-opacity, 1))!important}.link-primary{color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-primary:hover,.link-primary:focus{color:RGBA(0,106,100,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(0,106,100,var(--tblr-link-underline-opacity, 1))!important}.bg-secondary{background-color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-secondary-lt{color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-secondary-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-secondary{border-color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-secondary{--tblr-gradient-from: var(--tblr-secondary)}.bg-gradient-to-secondary{--tblr-gradient-to: var(--tblr-secondary)}.bg-gradient-via-secondary{--tblr-gradient-via: var(--tblr-secondary);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-secondary,.text-bg-gray{color:#f9fafb!important;background-color:RGBA(var(--tblr-secondary-rgb),var(--tblr-bg-opacity, 1))!important}.link-secondary{color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-secondary:hover,.link-secondary:focus{color:RGBA(86,91,102,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(86,91,102,var(--tblr-link-underline-opacity, 1))!important}.bg-success{background-color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-success-lt{color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-success-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-success{border-color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-success{--tblr-gradient-from: var(--tblr-success)}.bg-gradient-to-success{--tblr-gradient-to: var(--tblr-success)}.bg-gradient-via-success{--tblr-gradient-via: var(--tblr-success);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-success{color:#f9fafb!important;background-color:RGBA(var(--tblr-success-rgb),var(--tblr-bg-opacity, 1))!important}.link-success{color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-success:hover,.link-success:focus{color:RGBA(38,143,54,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(38,143,54,var(--tblr-link-underline-opacity, 1))!important}.bg-info{background-color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-info-lt{color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-info-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-info{border-color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-info{--tblr-gradient-from: var(--tblr-info)}.bg-gradient-to-info{--tblr-gradient-to: var(--tblr-info)}.bg-gradient-via-info{--tblr-gradient-via: var(--tblr-info);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-info{color:#f9fafb!important;background-color:RGBA(var(--tblr-info-rgb),var(--tblr-bg-opacity, 1))!important}.link-info{color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-info:hover,.link-info:focus{color:RGBA(53,122,180,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(53,122,180,var(--tblr-link-underline-opacity, 1))!important}.bg-warning{background-color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-warning-lt{color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-warning-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-warning{border-color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-warning{--tblr-gradient-from: var(--tblr-warning)}.bg-gradient-to-warning{--tblr-gradient-to: var(--tblr-warning)}.bg-gradient-via-warning{--tblr-gradient-via: var(--tblr-warning);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-warning{color:#f9fafb!important;background-color:RGBA(var(--tblr-warning-rgb),var(--tblr-bg-opacity, 1))!important}.link-warning{color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-warning:hover,.link-warning:focus{color:RGBA(196,127,0,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(196,127,0,var(--tblr-link-underline-opacity, 1))!important}.bg-danger{background-color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-danger-lt{color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-danger-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-danger{border-color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-danger{--tblr-gradient-from: var(--tblr-danger)}.bg-gradient-to-danger{--tblr-gradient-to: var(--tblr-danger)}.bg-gradient-via-danger{--tblr-gradient-via: var(--tblr-danger);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-danger{color:#f9fafb!important;background-color:RGBA(var(--tblr-danger-rgb),var(--tblr-bg-opacity, 1))!important}.link-danger{color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-danger:hover,.link-danger:focus{color:RGBA(171,46,46,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(171,46,46,var(--tblr-link-underline-opacity, 1))!important}.bg-light{background-color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-light-lt{color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-light-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-light{border-color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-light{--tblr-gradient-from: var(--tblr-light)}.bg-gradient-to-light{--tblr-gradient-to: var(--tblr-light)}.bg-gradient-via-light{--tblr-gradient-via: var(--tblr-light);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-light,.text-bg-white{color:#1f2937!important;background-color:RGBA(var(--tblr-light-rgb),var(--tblr-bg-opacity, 1))!important}.link-light{color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-light:hover,.link-light:focus{color:RGBA(250,251,252,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(250,251,252,var(--tblr-link-underline-opacity, 1))!important}.bg-dark{background-color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-dark-lt{color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-dark-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-dark{border-color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-dark{--tblr-gradient-from: var(--tblr-dark)}.bg-gradient-to-dark{--tblr-gradient-to: var(--tblr-dark)}.bg-gradient-via-dark{--tblr-gradient-via: var(--tblr-dark);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-dark,.text-bg-black{color:#f9fafb!important;background-color:RGBA(var(--tblr-dark-rgb),var(--tblr-bg-opacity, 1))!important}.link-dark{color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-dark:hover,.link-dark:focus{color:RGBA(25,33,44,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(25,33,44,var(--tblr-link-underline-opacity, 1))!important}.bg-muted{background-color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-muted-lt{color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-muted-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-muted{border-color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-muted{--tblr-gradient-from: var(--tblr-muted)}.bg-gradient-to-muted{--tblr-gradient-to: var(--tblr-muted)}.bg-gradient-via-muted{--tblr-gradient-via: var(--tblr-muted);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-muted{color:#f9fafb!important;background-color:RGBA(var(--tblr-muted-rgb),var(--tblr-bg-opacity, 1))!important}.link-muted{color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-muted:hover,.link-muted:focus{color:RGBA(86,91,102,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(86,91,102,var(--tblr-link-underline-opacity, 1))!important}.bg-blue{background-color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-blue-lt{color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-blue-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-blue{border-color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-blue{--tblr-gradient-from: var(--tblr-blue)}.bg-gradient-to-blue{--tblr-gradient-to: var(--tblr-blue)}.bg-gradient-via-blue{--tblr-gradient-via: var(--tblr-blue);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-blue{color:#f9fafb!important;background-color:RGBA(var(--tblr-blue-rgb),var(--tblr-bg-opacity, 1))!important}.link-blue{color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-blue:hover,.link-blue:focus{color:RGBA(5,89,167,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(5,89,167,var(--tblr-link-underline-opacity, 1))!important}.bg-azure{background-color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-azure-lt{color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-azure-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-azure{border-color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-azure{--tblr-gradient-from: var(--tblr-azure)}.bg-gradient-to-azure{--tblr-gradient-to: var(--tblr-azure)}.bg-gradient-via-azure{--tblr-gradient-via: var(--tblr-azure);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-azure{color:#f9fafb!important;background-color:RGBA(var(--tblr-azure-rgb),var(--tblr-bg-opacity, 1))!important}.link-azure{color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-azure:hover,.link-azure:focus{color:RGBA(53,122,180,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(53,122,180,var(--tblr-link-underline-opacity, 1))!important}.bg-indigo{background-color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-indigo-lt{color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-indigo-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-indigo{border-color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-indigo{--tblr-gradient-from: var(--tblr-indigo)}.bg-gradient-to-indigo{--tblr-gradient-to: var(--tblr-indigo)}.bg-gradient-via-indigo{--tblr-gradient-via: var(--tblr-indigo);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-indigo{color:#f9fafb!important;background-color:RGBA(var(--tblr-indigo-rgb),var(--tblr-bg-opacity, 1))!important}.link-indigo{color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-indigo:hover,.link-indigo:focus{color:RGBA(53,79,188,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(53,79,188,var(--tblr-link-underline-opacity, 1))!important}.bg-purple{background-color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-purple-lt{color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-purple-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-purple{border-color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-purple{--tblr-gradient-from: var(--tblr-purple)}.bg-gradient-to-purple{--tblr-gradient-to: var(--tblr-purple)}.bg-gradient-via-purple{--tblr-gradient-via: var(--tblr-purple);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-purple{color:#f9fafb!important;background-color:RGBA(var(--tblr-purple-rgb),var(--tblr-bg-opacity, 1))!important}.link-purple{color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-purple:hover,.link-purple:focus{color:RGBA(139,50,161,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(139,50,161,var(--tblr-link-underline-opacity, 1))!important}.bg-pink{background-color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-pink-lt{color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-pink-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-pink{border-color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-pink{--tblr-gradient-from: var(--tblr-pink)}.bg-gradient-to-pink{--tblr-gradient-to: var(--tblr-pink)}.bg-gradient-via-pink{--tblr-gradient-via: var(--tblr-pink);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-pink{color:#f9fafb!important;background-color:RGBA(var(--tblr-pink-rgb),var(--tblr-bg-opacity, 1))!important}.link-pink{color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-pink:hover,.link-pink:focus{color:RGBA(171,41,86,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(171,41,86,var(--tblr-link-underline-opacity, 1))!important}.bg-red{background-color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-red-lt{color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-red-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-red{border-color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-red{--tblr-gradient-from: var(--tblr-red)}.bg-gradient-to-red{--tblr-gradient-to: var(--tblr-red)}.bg-gradient-via-red{--tblr-gradient-via: var(--tblr-red);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-red{color:#f9fafb!important;background-color:RGBA(var(--tblr-red-rgb),var(--tblr-bg-opacity, 1))!important}.link-red{color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-red:hover,.link-red:focus{color:RGBA(171,46,46,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(171,46,46,var(--tblr-link-underline-opacity, 1))!important}.bg-orange{background-color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-orange-lt{color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-orange-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-orange{border-color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-orange{--tblr-gradient-from: var(--tblr-orange)}.bg-gradient-to-orange{--tblr-gradient-to: var(--tblr-orange)}.bg-gradient-via-orange{--tblr-gradient-via: var(--tblr-orange);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-orange{color:#f9fafb!important;background-color:RGBA(var(--tblr-orange-rgb),var(--tblr-bg-opacity, 1))!important}.link-orange{color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-orange:hover,.link-orange:focus{color:RGBA(198,82,6,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(198,82,6,var(--tblr-link-underline-opacity, 1))!important}.bg-yellow{background-color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-yellow-lt{color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-yellow-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-yellow{border-color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-yellow{--tblr-gradient-from: var(--tblr-yellow)}.bg-gradient-to-yellow{--tblr-gradient-to: var(--tblr-yellow)}.bg-gradient-via-yellow{--tblr-gradient-via: var(--tblr-yellow);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-yellow{color:#f9fafb!important;background-color:RGBA(var(--tblr-yellow-rgb),var(--tblr-bg-opacity, 1))!important}.link-yellow{color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-yellow:hover,.link-yellow:focus{color:RGBA(196,127,0,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(196,127,0,var(--tblr-link-underline-opacity, 1))!important}.bg-lime{background-color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-lime-lt{color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-lime-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-lime{border-color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-lime{--tblr-gradient-from: var(--tblr-lime)}.bg-gradient-to-lime{--tblr-gradient-to: var(--tblr-lime)}.bg-gradient-via-lime{--tblr-gradient-via: var(--tblr-lime);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-lime{color:#f9fafb!important;background-color:RGBA(var(--tblr-lime-rgb),var(--tblr-bg-opacity, 1))!important}.link-lime{color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-lime:hover,.link-lime:focus{color:RGBA(93,147,18,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(93,147,18,var(--tblr-link-underline-opacity, 1))!important}.bg-green{background-color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-green-lt{color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-green-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-green{border-color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-green{--tblr-gradient-from: var(--tblr-green)}.bg-gradient-to-green{--tblr-gradient-to: var(--tblr-green)}.bg-gradient-via-green{--tblr-gradient-via: var(--tblr-green);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-green{color:#f9fafb!important;background-color:RGBA(var(--tblr-green-rgb),var(--tblr-bg-opacity, 1))!important}.link-green{color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-green:hover,.link-green:focus{color:RGBA(38,143,54,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(38,143,54,var(--tblr-link-underline-opacity, 1))!important}.bg-teal{background-color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-teal-lt{color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-teal-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-teal{border-color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-teal{--tblr-gradient-from: var(--tblr-teal)}.bg-gradient-to-teal{--tblr-gradient-to: var(--tblr-teal)}.bg-gradient-via-teal{--tblr-gradient-via: var(--tblr-teal);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-teal{color:#f9fafb!important;background-color:RGBA(var(--tblr-teal-rgb),var(--tblr-bg-opacity, 1))!important}.link-teal{color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-teal:hover,.link-teal:focus{color:RGBA(10,133,96,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(10,133,96,var(--tblr-link-underline-opacity, 1))!important}.bg-cyan{background-color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-cyan-lt{color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-cyan-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-cyan{border-color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-cyan{--tblr-gradient-from: var(--tblr-cyan)}.bg-gradient-to-cyan{--tblr-gradient-to: var(--tblr-cyan)}.bg-gradient-via-cyan{--tblr-gradient-via: var(--tblr-cyan);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-cyan{color:#f9fafb!important;background-color:RGBA(var(--tblr-cyan-rgb),var(--tblr-bg-opacity, 1))!important}.link-cyan{color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-cyan:hover,.link-cyan:focus{color:RGBA(18,130,147,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(18,130,147,var(--tblr-link-underline-opacity, 1))!important}.bg-white{background-color:color-mix(in srgb,var(--tblr-white) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.bg-white-lt{color:color-mix(in srgb,var(--tblr-white) calc(var(--tblr-text-opacity, 1) * 100%),transparent)!important;background-color:color-mix(in srgb,var(--tblr-white-lt) calc(var(--tblr-bg-opacity, 1) * 100%),transparent)!important}.border-white{border-color:color-mix(in srgb,var(--tblr-white) calc(var(--tblr-border-opacity, 1) * 100%),transparent)!important}.bg-gradient-from-white{--tblr-gradient-from: var(--tblr-white)}.bg-gradient-to-white{--tblr-gradient-to: var(--tblr-white)}.bg-gradient-via-white{--tblr-gradient-via: var(--tblr-white);--tblr-gradient-stops: var(--tblr-gradient-from, transparent), var(--tblr-gradient-via, transparent), var(--tblr-gradient-to, transparent)}.text-bg-white{color:#1f2937!important;background-color:RGBA(var(--tblr-white-rgb),var(--tblr-bg-opacity, 1))!important}.link-white{color:color-mix(in srgb,var(--tblr-white) calc(var(--tblr-link-opacity, 1) * 100%),transparent)!important;text-decoration-color:color-mix(in srgb,var(--tblr-white) calc(var(--tblr-link-underline-opacity, 1) * 100%),transparent)!important}.link-white:hover,.link-white:focus{color:RGBA(255,255,255,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(255,255,255,var(--tblr-link-underline-opacity, 1))!important}.text-primary{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-primary) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-primary-fg{color:var(--tblr-primary-fg)!important}.text-secondary{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-secondary) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-secondary-fg{color:var(--tblr-secondary-fg)!important}.text-success{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-success) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-success-fg{color:var(--tblr-success-fg)!important}.text-info{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-info) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-info-fg{color:var(--tblr-info-fg)!important}.text-warning{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-warning) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-warning-fg{color:var(--tblr-warning-fg)!important}.text-danger{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-danger) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-danger-fg{color:var(--tblr-danger-fg)!important}.text-light{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-light) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-light-fg{color:var(--tblr-light-fg)!important}.text-dark{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-dark) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-dark-fg{color:var(--tblr-dark-fg)!important}.text-muted{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-muted) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-muted-fg{color:var(--tblr-muted-fg)!important}.text-blue{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-blue) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-blue-fg{color:var(--tblr-blue-fg)!important}.text-azure{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-azure) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-azure-fg{color:var(--tblr-azure-fg)!important}.text-indigo{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-indigo) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-indigo-fg{color:var(--tblr-indigo-fg)!important}.text-purple{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-purple) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-purple-fg{color:var(--tblr-purple-fg)!important}.text-pink{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-pink) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-pink-fg{color:var(--tblr-pink-fg)!important}.text-red{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-red) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-red-fg{color:var(--tblr-red-fg)!important}.text-orange{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-orange) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-orange-fg{color:var(--tblr-orange-fg)!important}.text-yellow{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-yellow) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-yellow-fg{color:var(--tblr-yellow-fg)!important}.text-lime{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-lime) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-lime-fg{color:var(--tblr-lime-fg)!important}.text-green{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-green) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-green-fg{color:var(--tblr-green-fg)!important}.text-teal{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-teal) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-teal-fg{color:var(--tblr-teal-fg)!important}.text-cyan{--tblr-text-opacity: 1;color:color-mix(in srgb,var(--tblr-cyan) calc(var(--tblr-text-opacity) * 100%),transparent)!important}.text-cyan-fg{color:var(--tblr-cyan-fg)!important}.bg-gray-50{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-50) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-50-fg{color:var(--tblr-gray-50-fg)!important}.bg-gray-100{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-100) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-100-fg{color:var(--tblr-gray-100-fg)!important}.bg-gray-200{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-200) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-200-fg{color:var(--tblr-gray-200-fg)!important}.bg-gray-300{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-300) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-300-fg{color:var(--tblr-gray-300-fg)!important}.bg-gray-400{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-400) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-400-fg{color:var(--tblr-gray-400-fg)!important}.bg-gray-500{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-500) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-500-fg{color:var(--tblr-gray-500-fg)!important}.bg-gray-600{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-600) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-600-fg{color:var(--tblr-gray-600-fg)!important}.bg-gray-700{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-700) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-700-fg{color:var(--tblr-gray-700-fg)!important}.bg-gray-800{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-800) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-800-fg{color:var(--tblr-gray-800-fg)!important}.bg-gray-900{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-900) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-900-fg{color:var(--tblr-gray-900-fg)!important}.bg-gray-950{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-gray-950) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-gray-950-fg{color:var(--tblr-gray-950-fg)!important}.bg-x{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-x) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-x-fg{color:var(--tblr-x-fg)!important}.bg-facebook{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-facebook) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-facebook-fg{color:var(--tblr-facebook-fg)!important}.bg-twitter{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-twitter) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-twitter-fg{color:var(--tblr-twitter-fg)!important}.bg-linkedin{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-linkedin) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-linkedin-fg{color:var(--tblr-linkedin-fg)!important}.bg-google{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-google) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-google-fg{color:var(--tblr-google-fg)!important}.bg-youtube{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-youtube) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-youtube-fg{color:var(--tblr-youtube-fg)!important}.bg-vimeo{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-vimeo) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-vimeo-fg{color:var(--tblr-vimeo-fg)!important}.bg-dribbble{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-dribbble) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-dribbble-fg{color:var(--tblr-dribbble-fg)!important}.bg-github{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-github) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-github-fg{color:var(--tblr-github-fg)!important}.bg-instagram{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-instagram) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-instagram-fg{color:var(--tblr-instagram-fg)!important}.bg-pinterest{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-pinterest) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-pinterest-fg{color:var(--tblr-pinterest-fg)!important}.bg-vk{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-vk) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-vk-fg{color:var(--tblr-vk-fg)!important}.bg-rss{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-rss) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-rss-fg{color:var(--tblr-rss-fg)!important}.bg-flickr{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-flickr) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-flickr-fg{color:var(--tblr-flickr-fg)!important}.bg-bitbucket{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-bitbucket) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-bitbucket-fg{color:var(--tblr-bitbucket-fg)!important}.bg-tabler{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-tabler) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.text-tabler-fg{color:var(--tblr-tabler-fg)!important}.bg-inverted{--tblr-bg-opacity: 1;background-color:color-mix(in srgb,var(--tblr-bg-surface-inverted) calc(var(--tblr-bg-opacity) * 100%),transparent)!important}.bg-surface{background-color:var(--tblr-bg-surface)!important}.bg-surface-secondary{background-color:var(--tblr-bg-surface-secondary)!important}.bg-surface-tertiary{background-color:var(--tblr-bg-surface-tertiary)!important}.bg-surface-backdrop{background-color:color-mix(in srgb,var(--tblr-gray-800) 24%,transparent)!important}.scrollable{overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch}.scrollable.hover{overflow-y:hidden}.scrollable.hover>*{margin-top:-1px}.scrollable.hover:hover,.scrollable.hover:focus,.scrollable.hover:active{overflow:visible;overflow-y:auto}.touch .scrollable{overflow-y:auto!important}.scroll-x,.scroll-y{overflow:hidden;-webkit-overflow-scrolling:touch}.scroll-y{overflow-y:auto}.scroll-x{overflow-x:auto}.no-scroll{overflow:hidden}.w-0{width:0!important}.h-0{height:0!important}.w-1{width:.25rem!important}.h-1{height:.25rem!important}.w-2{width:.5rem!important}.h-2{height:.5rem!important}.w-3{width:1rem!important}.h-3{height:1rem!important}.w-4{width:1.5rem!important}.h-4{height:1.5rem!important}.w-5{width:2rem!important}.h-5{height:2rem!important}.w-6{width:2.5rem!important}.h-6{height:2.5rem!important}.w-auto{width:auto!important}.h-auto{height:auto!important}.w-px{width:1px!important}.h-px{height:1px!important}.w-full{width:100%!important}.h-full{height:100%!important}.opacity-0{opacity:0!important}.opacity-5{opacity:.05!important}.opacity-10{opacity:.1!important}.opacity-15{opacity:.15!important}.opacity-20{opacity:.2!important}.opacity-25{opacity:.25!important}.opacity-30{opacity:.3!important}.opacity-35{opacity:calc(35 / 100)!important}.opacity-40{opacity:.4!important}.opacity-45{opacity:.45!important}.opacity-50{opacity:.5!important}.opacity-55{opacity:.55!important}.opacity-60{opacity:.6!important}.opacity-65{opacity:.65!important}.opacity-70{opacity:calc(70 / 100)!important}.opacity-75{opacity:.75!important}.opacity-80{opacity:.8!important}.opacity-85{opacity:.85!important}.opacity-90{opacity:.9!important}.opacity-95{opacity:calc(95 / 100)!important}.opacity-100{opacity:1!important}.hover-shadow-sm:hover{box-shadow:0 .125rem .25rem #00000013!important}.hover-shadow:hover{box-shadow:rgba(var(--tblr-body-color-rgb),.04) 0 2px 4px!important}.hover-shadow-lg:hover{box-shadow:0 1rem 3rem #0000002d!important}.hover-shadow-none:hover{box-shadow:none!important}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.subpixel-antialiased{-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.hover-rotate-start,.hover-rotate-end,.hover-scale,.hover-elevate-down,.hover-elevate-up{transition:transform .3s ease}.hover-rotate-start:hover,.hover-rotate-end:hover,.hover-scale:hover,.hover-elevate-down:hover,.hover-elevate-up:hover{will-change:transform}.hover-elevate-up:hover{transform:translateY(-4px)}.hover-elevate-down:hover{transform:translateY(4px)}.hover-scale:hover{transform:scale(1.1)}.hover-rotate-end:hover{transform:rotate(4deg)}.hover-rotate-start:hover{transform:rotate(-4deg)}.ts-control{border:1px solid var(--tblr-border-color);padding:.5625rem 1rem;width:100%;overflow:hidden;position:relative;z-index:1;box-sizing:border-box;box-shadow:none;border-radius:var(--tblr-border-radius);display:flex;flex-wrap:wrap}.ts-wrapper.multi.has-items .ts-control{padding:calc(.5625rem - 1px) 1rem calc(.5625rem - 4px)}.full .ts-control{background-color:var(--tblr-bg-forms)}.disabled .ts-control,.disabled .ts-control *{cursor:default!important}.focus .ts-control{box-shadow:none}.ts-control>*{vertical-align:baseline;display:inline-block}.ts-wrapper.multi .ts-control>div{cursor:pointer;margin:0 3px 3px 0;padding:1px 5px;background:#efefef;color:#1f2937;border:0px solid #e5e7eb;overflow:auto}.ts-wrapper.multi .ts-control>div.active{background:#00857d;color:#fff;border:0px solid rgba(0,0,0,0)}.ts-wrapper.multi.disabled .ts-control>div,.ts-wrapper.multi.disabled .ts-control>div.active{color:#787878;background:#fff;border:0px solid white}.ts-control>input{flex:1 1 auto;min-width:7rem;display:inline-block!important;padding:0!important;min-height:0!important;max-height:none!important;max-width:100%!important;margin:0!important;text-indent:0!important;border:0 none!important;background:none!important;line-height:inherit!important;user-select:auto!important;box-shadow:none!important}.ts-control>input::-ms-clear{display:none}.ts-control>input:focus{outline:none!important}.has-items .ts-control>input{margin:0 4px!important}.ts-control.rtl{text-align:right}.ts-control.rtl.single .ts-control:after{left:calc(1rem + 5px);right:auto}.ts-control.rtl .ts-control>input{margin:0 4px 0 -2px!important}.disabled .ts-control{opacity:1;background-color:var(--tblr-bg-surface-secondary)}.input-hidden .ts-control>input{opacity:0;position:absolute;left:-10000px}.ts-dropdown{position:absolute;top:100%;left:0;width:100%;z-index:10;border:1px solid #d0d0d0;background:var(--tblr-bg-surface);margin:.25rem 0 0;border-top:0 none;box-sizing:border-box;box-shadow:0 1px 3px #0000001a;border-radius:0 0 var(--tblr-border-radius) var(--tblr-border-radius)}.ts-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.ts-dropdown [data-selectable] .highlight{background:#ffed2866;border-radius:1px}.ts-dropdown .option,.ts-dropdown .optgroup-header,.ts-dropdown .no-results,.ts-dropdown .create{padding:3px 1rem}.ts-dropdown .option,.ts-dropdown [data-disabled],.ts-dropdown [data-disabled] [data-selectable].option{cursor:inherit;opacity:.5}.ts-dropdown [data-selectable].option{opacity:1;cursor:pointer}.ts-dropdown .optgroup:first-child .optgroup-header{border-top:0 none}.ts-dropdown .optgroup-header{color:#4b5563;background:var(--tblr-bg-surface);cursor:default}.ts-dropdown .active{background-color:rgba(var(--tblr-secondary-rgb),.08);color:inherit}.ts-dropdown .active.create{color:inherit}.ts-dropdown .create{color:#1f293780}.ts-dropdown .spinner{display:inline-block;width:30px;height:30px;margin:3px 1rem}.ts-dropdown .spinner:after{content:" ";display:block;width:24px;height:24px;margin:3px;border-radius:50%;border:5px solid #d0d0d0;border-color:#d0d0d0 transparent #d0d0d0 transparent;animation:lds-dual-ring 1.2s linear infinite}@keyframes lds-dual-ring{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.ts-dropdown-content{overflow:hidden auto;max-height:200px;scroll-behavior:smooth}.ts-wrapper.plugin-drag_drop .ts-dragging{color:transparent!important}.ts-wrapper.plugin-drag_drop .ts-dragging>*{visibility:hidden!important}.plugin-checkbox_options:not(.rtl) .option input{margin-right:.5rem}.plugin-checkbox_options.rtl .option input{margin-left:.5rem}.plugin-clear_button{--ts-pr-clear-button: 1em}.plugin-clear_button .clear-button{opacity:0;position:absolute;top:50%;transform:translateY(-50%);right:calc(1rem - 5px);margin-right:0!important;background:transparent!important;transition:opacity .5s;cursor:pointer}.plugin-clear_button.form-select .clear-button,.plugin-clear_button.single .clear-button{right:max(var(--ts-pr-caret),1rem)}.plugin-clear_button.focus.has-items .clear-button,.plugin-clear_button:not(.disabled):hover.has-items .clear-button{opacity:1}.ts-wrapper .dropdown-header{position:relative;padding:6px 1rem;border-bottom:1px solid #d0d0d0;background:color-mix(var(--tblr-bg-surface),#d0d0d0,85%);border-radius:var(--tblr-border-radius) var(--tblr-border-radius) 0 0}.ts-wrapper .dropdown-header-close{position:absolute;right:1rem;top:50%;color:#1f2937;opacity:.4;margin-top:-12px;line-height:20px;font-size:20px!important}.ts-wrapper .dropdown-header-close:hover{color:#000}.plugin-dropdown_input.focus.dropdown-active .ts-control{box-shadow:none;border:1px solid var(--tblr-border-color);box-shadow:var(--tblr-shadow-input)}.plugin-dropdown_input .dropdown-input{border:1px solid #d0d0d0;border-width:0 0 1px;display:block;padding:.5625rem 1rem;box-shadow:none;width:100%;background:transparent}.plugin-dropdown_input.focus .ts-dropdown .dropdown-input{border-color:#80c2be;outline:0;box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.plugin-dropdown_input .items-placeholder{border:0 none!important;box-shadow:none!important;width:100%}.plugin-dropdown_input.has-items .items-placeholder,.plugin-dropdown_input.dropdown-active .items-placeholder{display:none!important}.ts-wrapper.plugin-input_autogrow.has-items .ts-control>input{min-width:0}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input{flex:none;min-width:4px}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input::placeholder{color:transparent}.ts-dropdown.plugin-optgroup_columns .ts-dropdown-content{display:flex}.ts-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0 none;flex-grow:1;flex-basis:0;min-width:0}.ts-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0 none}.ts-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.ts-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0 none}.ts-wrapper.plugin-remove_button .item{display:inline-flex;align-items:center}.ts-wrapper.plugin-remove_button .item .remove{color:inherit;text-decoration:none;vertical-align:middle;display:inline-block;padding:0 5px;border-radius:0 2px 2px 0;box-sizing:border-box}.ts-wrapper.plugin-remove_button .item .remove:hover{background:#0000000d}.ts-wrapper.plugin-remove_button.disabled .item .remove:hover{background:none}.ts-wrapper.plugin-remove_button .remove-single{position:absolute;right:0;top:0;font-size:23px}.ts-wrapper.plugin-remove_button:not(.rtl) .item{padding-right:0!important}.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove{border-left:1px solid #e5e7eb;margin-left:5px}.ts-wrapper.plugin-remove_button:not(.rtl) .item.active .remove{border-left-color:#0000}.ts-wrapper.plugin-remove_button:not(.rtl).disabled .item .remove{border-left-color:#fff}.ts-wrapper.plugin-remove_button.rtl .item{padding-left:0!important}.ts-wrapper.plugin-remove_button.rtl .item .remove{border-right:1px solid #e5e7eb;margin-right:5px}.ts-wrapper.plugin-remove_button.rtl .item.active .remove{border-right-color:#0000}.ts-wrapper.plugin-remove_button.rtl.disabled .item .remove{border-right-color:#fff}:root{--ts-pr-clear-button: 0px;--ts-pr-caret: 0px;--ts-pr-min: .75rem}.ts-wrapper.single .ts-control,.ts-wrapper.single .ts-control input{cursor:pointer}.ts-control:not(.rtl){padding-right:max(var(--ts-pr-min),var(--ts-pr-clear-button) + var(--ts-pr-caret))!important}.ts-control.rtl{padding-left:max(var(--ts-pr-min),var(--ts-pr-clear-button) + var(--ts-pr-caret))!important}.ts-wrapper{position:relative}.ts-dropdown,.ts-control,.ts-control input{color:#1f2937;font-family:inherit;font-size:inherit;line-height:1.25rem}.ts-control,.ts-wrapper.single.input-active .ts-control{background:var(--tblr-bg-forms);cursor:text}.ts-hidden-accessible{border:0!important;clip:rect(0 0 0 0)!important;clip-path:inset(50%)!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;white-space:nowrap!important}.ts-dropdown,.ts-dropdown.form-control,.ts-dropdown.form-select{height:auto;padding:0;z-index:1000;background:var(--tblr-bg-surface);border:1px solid var(--tblr-border-color-translucent);border-radius:6px;box-shadow:0 6px 12px #0000002d}.ts-dropdown .optgroup-header{font-size:.765625rem;line-height:1.4285714286}.ts-dropdown .optgroup:first-child:before{display:none}.ts-dropdown .optgroup:before{content:" ";display:block;height:0;margin:var(--tblr-spacer-2) 0;overflow:hidden;border-top:1px solid var(--tblr-border-color-translucent);margin-left:-1rem;margin-right:-1rem}.ts-dropdown .create{padding-left:1rem}.ts-dropdown-content{padding:5px 0}.ts-control{box-shadow:var(--tblr-shadow-input);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.ts-control{transition:none}}.ts-control{display:flex;align-items:center}.focus .ts-control{border-color:#80c2be;outline:0;box-shadow:var(--tblr-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.ts-control .item{display:flex;align-items:center}.ts-control input::placeholder{color:var(--bs-secondary-color, #a7aeb8);opacity:1}.ts-wrapper.is-invalid,select.tomselected.is-invalid+div.ts-wrapper,.was-validated .invalid,.was-validated :invalid+.ts-wrapper{border-color:var(--tblr-form-invalid-color)}.ts-wrapper.is-invalid:not(.single),select.tomselected.is-invalid+div.ts-wrapper:not(.single),.was-validated .invalid:not(.single),.was-validated :invalid+.ts-wrapper:not(.single){background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23d63939' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='18' y1='6' x2='6' y2='18'%3e%3c/line%3e%3cline x1='6' y1='6' x2='18' y2='18'%3e%3c/line%3e%3c/svg%3e");background-position:right 1.53125rem center;background-size:1.8125rem 1.8125rem;background-repeat:no-repeat}.ts-wrapper.is-invalid.single,select.tomselected.is-invalid+div.ts-wrapper.single,.was-validated .invalid.single,.was-validated :invalid+.ts-wrapper.single{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23d63939' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cline x1='18' y1='6' x2='6' y2='18'%3e%3c/line%3e%3cline x1='6' y1='6' x2='18' y2='18'%3e%3c/line%3e%3c/svg%3e");background-position:right 1rem center,center right 3rem;background-size:16px 12px,1.8125rem 1.8125rem;background-repeat:no-repeat}.ts-wrapper.is-invalid.focus .ts-control,select.tomselected.is-invalid+div.ts-wrapper.focus .ts-control,.was-validated .invalid.focus .ts-control,.was-validated :invalid+.ts-wrapper.focus .ts-control{border-color:var(--tblr-form-invalid-color);box-shadow:0 0 0 .25rem rgba(var(--tblr-form-invalid-color),.25)}.ts-wrapper.is-valid,.was-validated .valid,.was-validated :valid+.ts-wrapper{border-color:var(--tblr-form-valid-color)}.ts-wrapper.is-valid:not(.single),.was-validated .valid:not(.single),.was-validated :valid+.ts-wrapper:not(.single){background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%232fb344' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='20 6 9 17 4 12'%3e%3c/polyline%3e%3c/svg%3e");background-position:right 1.53125rem center;background-size:1.8125rem 1.8125rem;background-repeat:no-repeat}.ts-wrapper.is-valid.single,.was-validated .valid.single,.was-validated :valid+.ts-wrapper.single{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%232fb344' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='20 6 9 17 4 12'%3e%3c/polyline%3e%3c/svg%3e");background-position:right 1rem center,center right 3rem;background-size:16px 12px,1.8125rem 1.8125rem;background-repeat:no-repeat}.ts-wrapper.is-valid.focus .ts-control,.was-validated .valid.focus .ts-control,.was-validated :valid+.ts-wrapper.focus .ts-control{border-color:var(--tblr-form-valid-color);box-shadow:0 0 0 .25rem rgba(var(--tblr-form-valid-color),.25)}.ts-wrapper{min-height:calc(1.25rem + 1.125rem + calc(var(--tblr-border-width) * 2));display:flex}.input-group-sm>.ts-wrapper,.ts-wrapper.form-select-sm,.ts-wrapper.form-control-sm{min-height:calc(1.25rem + .625rem + calc(var(--tblr-border-width) * 2))}.input-group-sm>.ts-wrapper .ts-control,.ts-wrapper.form-select-sm .ts-control,.ts-wrapper.form-control-sm .ts-control{border-radius:var(--tblr-border-radius-sm);font-size:.75rem}.input-group-sm>.ts-wrapper.has-items .ts-control,.ts-wrapper.form-select-sm.has-items .ts-control,.ts-wrapper.form-control-sm.has-items .ts-control{font-size:.75rem}.input-group-sm>.ts-wrapper.multi.has-items .ts-control,.ts-wrapper.form-select-sm.multi.has-items .ts-control,.ts-wrapper.form-control-sm.multi.has-items .ts-control{padding-top:calc((calc(1.25rem + .625rem + calc(var(--tblr-border-width) * 2)) - 1.25rem * .75rem - calc((var(--tblr-border-width) + 1px) * 2)) / 2)!important}.ts-wrapper.multi.has-items .ts-control{padding-left:calc(1rem - 5px);--ts-pr-min: calc(1rem - 5px) }.ts-wrapper.multi .ts-control>div{border-radius:calc(var(--tblr-border-radius) - 1px)}.input-group-lg>.ts-wrapper,.ts-wrapper.form-control-lg,.ts-wrapper.form-select-lg{min-height:calc(1.25rem + 1.375rem + calc(var(--tblr-border-width) * 2))}.input-group-lg>.ts-wrapper .ts-control,.ts-wrapper.form-control-lg .ts-control,.ts-wrapper.form-select-lg .ts-control{border-radius:var(--tblr-border-radius-lg);font-size:1rem}.ts-wrapper:not(.form-control,.form-select){padding:0;border:none;height:auto;box-shadow:none;background:none}.ts-wrapper:not(.form-control,.form-select).single .ts-control{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right 1rem center;background-size:16px 12px}.ts-wrapper.form-select,.ts-wrapper.single{--ts-pr-caret: 3rem}.ts-wrapper.form-control,.ts-wrapper.form-select{padding:0!important;height:auto;box-shadow:none;display:flex}.ts-wrapper.form-control .ts-control,.ts-wrapper.form-control.single.input-active .ts-control,.ts-wrapper.form-select .ts-control,.ts-wrapper.form-select.single.input-active .ts-control{border:none!important}.ts-wrapper.form-control:not(.disabled) .ts-control,.ts-wrapper.form-control:not(.disabled).single.input-active .ts-control,.ts-wrapper.form-select:not(.disabled) .ts-control,.ts-wrapper.form-select:not(.disabled).single.input-active .ts-control{background:transparent!important}.input-group>.ts-wrapper{flex-grow:1;width:1%}.input-group>.ts-wrapper:not(:nth-child(2))>.ts-control{border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.ts-wrapper:not(:last-child)>.ts-control{border-top-right-radius:0;border-bottom-right-radius:0}.form-select .ts-dropdown,.form-select .ts-control,.form-select .ts-control input{color:var(--tblr-body-color)}:root{--ts-pr-clear-button: 0rem;--ts-pr-caret: 0rem}.ts-input{color:inherit}.focus .ts-control{border-radius:var(--tblr-border-radius)}.ts-control{color:inherit}.ts-control .dropdown-menu{width:100%;height:auto}.ts-wrapper .form-control,.ts-wrapper .form-select,.ts-wrapper.form-control,.ts-wrapper.form-select{box-shadow:var(--tblr-shadow-input)}.ts-wrapper.is-invalid .ts-control,select.tomselected.is-invalid+div.ts-wrapper .ts-control,.ts-wrapper.is-valid .ts-control{--ts-pr-clear-button: 1.5rem}.ts-dropdown{background:var(--tblr-bg-surface);color:var(--tblr-body-color);box-shadow:var(--tblr-shadow-dropdown);z-index:1000}.ts-dropdown .option{padding:.5rem .75rem}.ts-control,.ts-control input{color:var(--tblr-body-color)}.ts-control input::placeholder{color:var(--tblr-tertiary)}.ts-wrapper.multi .ts-control>div,.ts-wrapper.multi.disabled .ts-control>div{background:var(--tblr-bg-surface-secondary);border:1px solid var(--tblr-border-color);color:var(--tblr-body-color)}.ts-wrapper.disabled .ts-control{opacity:1}.ts-wrapper.disabled .ts-control>div.item{color:var(--tblr-gray-500)}html{scroll-behavior:auto!important}mark,.mark{padding-left:0;padding-right:0}.table-responsive .dropdown,.table-responsive .btn-group,.table-responsive .btn-group-vertical{position:static}.progress{min-width:80px}hr.dropdown-divider,.dropdown-divider.hr{margin-bottom:.25rem;margin-top:.25rem}.dropdown-item{font-weight:400}*{font-feature-settings:"liga" 0;font-variant-ligatures:none}@media(min-width:992px){:root,:host{margin-left:0;scrollbar-gutter:stable}}pre{background-color:transparent;color:inherit}.alert{background:var(--tblr-bg-surface)}.badge{user-select:text}.btn{display:inline-block}.btn:focus{border:1px solid var(--tblr-primary-fg);outline:2px solid var(--tblr-primary)!important}.btn-sm,.btn-group-sm>.btn{border-radius:6px}.dropdown-item{display:inline-block}.footer .text-primary{color:#001423!important}.nav-tabs .nav-link{display:inline-block}.page,.page-tabs .nav-tabs .nav-link.active{background-color:var(--tblr-bg-surface-tertiary)!important}.navbar{--tblr-navbar-active-bg: rgba(0, 0, 0, .06)}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{--tblr-alert-color: darken(var(--tblr-warning),10%);--tblr-link-color: #00F2D4;--tblr-link-color-rgb: 0,242,212;--tblr-link-hover-color-rgb: 0,242,212;--tblr-secondary: #9ca3af;--tblr-primary: #00F2D4;--tblr-primary-fg: #001423;--tblr-primary-rgb: 0,242,212;--tblr-btn-active-color: #001423}html[data-bs-theme=dark] ::selection,body[data-bs-theme=dark] html[data-bs-theme=light] ::selection{background-color:rgba(var(--tblr-primary-rgb),.48)}html[data-bs-theme=dark] .btn-primary,body[data-bs-theme=dark] html[data-bs-theme=light] .btn-primary,html[data-bs-theme=dark] .bg-primary .card-title,body[data-bs-theme=dark] html[data-bs-theme=light] .bg-primary .card-title,html[data-bs-theme=dark] .bg-primary a,body[data-bs-theme=dark] html[data-bs-theme=light] .bg-primary a,html[data-bs-theme=dark] .bg-primary i,body[data-bs-theme=dark] html[data-bs-theme=light] .bg-primary i,html[data-bs-theme=dark] .text-bg-primary,body[data-bs-theme=dark] html[data-bs-theme=light] .text-bg-primary{color:#001423!important}html[data-bs-theme=dark] .card,body[data-bs-theme=dark] html[data-bs-theme=light] .card{background:#001423!important}html[data-bs-theme=dark],body[data-bs-theme=dark] html[data-bs-theme=light],html[data-bs-theme=dark] .navbar,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar,html[data-bs-theme=dark] .page-header,body[data-bs-theme=dark] html[data-bs-theme=light] .page-header{background-color:#001423}html[data-bs-theme=dark] .page,body[data-bs-theme=dark] html[data-bs-theme=light] .page,html[data-bs-theme=dark] .page-tabs .nav-tabs .nav-link.active,body[data-bs-theme=dark] html[data-bs-theme=light] .page-tabs .nav-tabs .nav-link.active{background-color:#081b2a!important}html[data-bs-theme=dark] .page-link.active,body[data-bs-theme=dark] html[data-bs-theme=light] .page-link.active,html[data-bs-theme=dark] .active>.page-link,body[data-bs-theme=dark] html[data-bs-theme=light] .active>.page-link{color:#001423}html[data-bs-theme=dark] .text-bg-primary,body[data-bs-theme=dark] html[data-bs-theme=light] .text-bg-primary{color:#001423!important}html[data-bs-theme=dark] .text-muted,body[data-bs-theme=dark] html[data-bs-theme=light] .text-muted{color:var(--tblr-secondary-color)!important}html[data-bs-theme=dark] .text-secondary,body[data-bs-theme=dark] html[data-bs-theme=light] .text-secondary{color:#9ca3af!important}html[data-bs-theme=dark] .footer .text-primary,body[data-bs-theme=dark] html[data-bs-theme=light] .footer .text-primary{color:#fff!important}html[data-bs-theme=dark] .toast,body[data-bs-theme=dark] html[data-bs-theme=light] .toast{color:var(--tblr-body-color)}html[data-bs-theme=dark] .table-primary,body[data-bs-theme=dark] html[data-bs-theme=light] .table-primary{--tblr-table-bg: rgba(var(--tblr-secondary-rgb), .48);--tblr-table-hover-bg: inherit;--tblr-table-hover-color: inherit}pre code{padding:unset}.dropdown-toggle:after{font-family:Material Design Icons;content:"\f0140";padding-right:9px;border-bottom:none;border-left:none;transform:none;vertical-align:.05em;height:auto}.accordion-button-toggle{align-items:center;justify-content:center;flex:0 0 auto}.accordion-button-toggle>i{display:block;line-height:1}:root:not(.dummy)[data-bs-theme=light] .hide-theme-light,:root:not(.dummy)[data-bs-theme=dark] .hide-theme-dark,body[data-bs-theme=dark] [data-bs-theme=light]:root:not(.dummy) .hide-theme-dark{display:none!important}:root:not(.dummy)[data-bs-theme=dark] .hide-theme-light,body[data-bs-theme=dark] [data-bs-theme=light]:root:not(.dummy) .hide-theme-light,:root:not(.dummy)[data-bs-theme=light] .hide-theme-dark{display:inline-flex!important}.ts-wrapper.multi .ts-control{padding:7px 7px 3px}.ts-wrapper.multi .ts-control div{margin:0 4px 4px 0}.badge a{color:inherit;text-decoration:none}.page-body .card{margin-bottom:1rem}.page-body .card .card-header,.page-body .card .card-body,.page-body .card .card-footer{padding:.75rem}.page-body .card .card-header{background:var(--tblr-bg-surface-tertiary)}.page-body .card h2.card-header,.page-body .card .card-header.h2{font-size:var(--tblr-font-size-h5);line-height:var(--tblr-line-height-h5);margin-bottom:0}.page-body .card .list-group-item{padding:.5rem .75rem}.page-body .card .table,.page-body .card .markdown>table{margin-bottom:0}form.object-edit{margin:auto;max-width:800px}.col-form-label.required{font-weight:700}.col-form-label.required:after{position:absolute;display:inline-block;margin-left:0;font-family:Material Design Icons;font-size:8px;content:"\f06c4"}.has-errors input,.has-errors select,.has-errors textarea{border:1px solid #d63939}select[multiple] optgroup{top:0;background-color:var(--bs-body-bg);font-style:normal;font-weight:700}select[multiple] option{padding-left:.5rem}.modifier-select{min-width:10rem;max-width:15rem;width:auto;white-space:nowrap}.page{background-color:var(--tblr-bg-surface-secondary)}.page-header{background-color:var(--tblr-bg-surface);min-height:0}.navbar-vertical.navbar-expand-lg .navbar-collapse .nav-link-icon,.navbar-vertical.navbar-expand-lg .navbar-collapse .nav-link-title{color:#001423}.navbar-vertical.navbar-expand-lg .navbar-collapse .text-secondary{color:#00857d!important}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item a{color:#001423}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item .btn-group{visibility:hidden}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:hover,.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.active{background-color:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:hover a,.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.active a{text-decoration:none}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:hover .btn-group,.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item.active .btn-group{visibility:visible}.navbar-vertical.navbar-expand-lg .navbar-nav{z-index:1}@media(max-width:991.98px){.navbar-vertical.navbar-expand-lg .navbar-brand{padding:.2rem 0}}.navbar-vertical.navbar-expand-lg .navbar-brand a:hover{text-decoration:none}.navbar-vertical.navbar-expand-lg img.motif{bottom:0;display:none;left:0;mask-image:linear-gradient(180deg,#0000,#0000004d);opacity:.5;position:fixed;user-drag:none;user-select:none;-moz-user-select:none;-webkit-user-drag:none;-webkit-user-select:none;-ms-user-select:none;width:18rem}@media(min-width:992px){.navbar-vertical.navbar-expand-lg img.motif{display:block}}html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg{background:linear-gradient(180deg,#00857d00,#00857d1a),#fff}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg{background:linear-gradient(180deg,#00f2d400,#00f2d41a),#001423}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .nav-item.dropdown.active:after,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .nav-item.dropdown.active:after{border-color:#00f2d4!important}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .nav-link-title,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .nav-link-title,html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .nav-link-icon,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .nav-link-icon,html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item a,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .dropdown-item a{color:#fff!important}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item.active,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .dropdown-item.active,html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item:hover,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .dropdown-item:hover{background-color:#ffffff0f!important}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .text-secondary,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg .text-secondary{color:#00f2d4!important}html[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg img.motif,body[data-bs-theme=dark] html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg img.motif{opacity:.25}.progress{height:20px}.progress .progress-label{display:flex;flex-direction:column;justify-content:center;padding-left:.25rem}.table thead th,.markdown>table thead th{font-size:.625rem}table.object-list tbody>tr:last-child>td{border-bottom-width:0}table.object-list th.asc>a:after{content:"\f0140";font-family:Material Design Icons}table.object-list th.desc>a:after{content:"\f0143";font-family:Material Design Icons}table.attr-table th{font-weight:400;width:min-content}table.attr-table th,table.attr-table td{border-bottom-style:dashed}table.attr-table tr:last-child{border-bottom-style:hidden}table.attr-table td{overflow-wrap:anywhere}td pre{margin-bottom:0}table th.orderable a{color:var(--tblr-body-color)}html[data-bs-theme=dark] .table thead th,body[data-bs-theme=dark] html[data-bs-theme=light] .table thead th,body[data-bs-theme=dark] html[data-bs-theme=light] .markdown>table thead th,html[data-bs-theme=dark] .markdown>table thead th{background:#001423!important}.page-tabs{border-bottom:1px solid var(--tblr-border-color-translucent)}.page-tabs .nav-tabs{position:relative;border:none}.page-tabs .nav-tabs .nav-link.active,.page-tabs .nav-tabs .nav-link:active,.page-tabs .nav-tabs .nav-link:hover{border-color:var(--tblr-border-color-translucent);border-bottom-color:transparent}.page-tabs .nav-tabs .nav-link.active{color:inherit;background:var(--tblr-bg-surface-secondary);border-bottom-color:transparent}pre.change-data{border-radius:0;padding:0;margin-inline:-.75rem}pre.change-data>span{display:block;padding-inline:.5rem;max-width:100%;min-width:fit-content;border-left:.25rem solid transparent}pre.change-data>span.added{background-color:var(--tblr-green-200);border-left-color:var(--tblr-green-darken)}pre.change-data>span.removed{background-color:var(--tblr-red-200);border-left-color:var(--tblr-red-darken)}pre.change-diff{border:var(--tblr-border-width) solid transparent}pre.change-diff.change-added{background-color:var(--tblr-green-lt);border-color:var(--tblr-green)}pre.change-diff.change-removed{background-color:var(--tblr-red-lt);border-color:var(--tblr-red)}pre.block{padding:1rem;border:var(--tblr-border-width) solid #e5e7eb;border-radius:6px}.grid-stack .card-header.bg-default{background:var(--tblr-bg-surface-secondary)!important}.grid-stack .card-header a{color:inherit!important}tr[data-cable-status=connected]{background-color:#2fb34426}tr[data-cable-status=planned]{background-color:#066fd126}tr[data-cable-status=decommissioning]{background-color:#f59f0026}tr[data-mark-connected=true]{background-color:#2fb34426}tr[data-virtual=true]{background-color:#00857d26}tr[data-enabled=disabled]{background-color:#9ca3af26}tr[data-cable-status=connected] button.mark-installed{display:none}tr:not([data-cable-status=connected]) button.mark-planned{display:none}.rendered-markdown table{width:100%}.rendered-markdown table th{border-bottom:2px solid #dddddd;padding:8px}.rendered-markdown table td{border-top:1px solid #dddddd;padding:8px}.rendered-markdown table th[align=left]{text-align:left}.rendered-markdown table th[align=center]{text-align:center}.rendered-markdown table th[align=right]{text-align:right}.rendered-markdown p:last-child{margin-bottom:0}td>.rendered-markdown{max-height:200px;overflow-y:scroll}.markdown-widget .preview{border:1px solid #e5e7eb;border-radius:6px;min-height:200px}span.color-label{display:inline-block;width:5rem;height:1rem;padding:.25em .5em;border:1px solid #303030;border-radius:6px}.record-depth{display:inline;user-select:none;opacity:33%}.record-depth span:only-of-type,.record-depth span:last-of-type{margin-right:.25rem}.hide-last-child :last-child{visibility:hidden;opacity:0}.netbox-edition{letter-spacing:.15rem}.logo{height:80px}.sso-icon{height:24px}img.plugin-icon{max-width:1.4285em;height:auto}.thumbnail{max-width:200px}.thumbnail img{border:1px solid #606060}.image-preview-popover{--bs-popover-max-width: clamp(240px, 25vw, 640px)}.image-preview-popover .popover-header{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.image-preview-popover .popover-body{display:flex;justify-content:center;align-items:center}.image-preview-popover img{display:block;max-width:100%;max-height:clamp(160px,33vh,640px);height:auto}html[data-bs-theme=dark] img.plugin-icon,body[data-bs-theme=dark] html[data-bs-theme=light] img.plugin-icon{filter:grayscale(100%) invert(100%) brightness(80%)}.sticky-actions{z-index:4}.sticky-actions[data-sticky-when=always],.sticky-actions.is-sticky-active{position:sticky;bottom:.25rem}.sticky-actions[data-sticky-position=left],.sticky-actions[data-sticky-position=right]{display:flex;align-items:center;gap:.5rem;padding:.5rem;max-width:100%;width:fit-content}.sticky-actions[data-sticky-position=right]{margin-left:auto}.sticky-actions[data-sticky-position=full]{max-width:100%}.sticky-actions-footer{max-width:100%;margin-top:1.5rem;padding:1rem;padding-bottom:calc(1rem + env(safe-area-inset-bottom,0));background:var(--tblr-bg-surface-secondary);border-top:1px solid var(--tblr-border-color)}.sticky-actions-footer[data-sticky-when=always],.sticky-actions-footer.is-sticky-active{bottom:0}.sticky-actions-footer>.btn-list{justify-content:flex-end;margin-bottom:0}.object-edit--with-sticky-actions{padding-bottom:calc(1.5rem + env(safe-area-inset-bottom,0))}[data-sticky-when=selection] .bulk-action-buttons .btn:disabled,[data-sticky-when=selection] .bulk-action-buttons .btn.disabled{border-color:transparent;box-shadow:none}[data-sticky-when=selection] .bulk-action-buttons .btn.disabled{pointer-events:none}.btn-float-group-right{position:sticky;bottom:10px;z-index:4;margin-left:auto;width:fit-content}.btn-float-group-left{position:sticky;bottom:10px;z-index:4;width:fit-content}tr[data-read=True] td{background-color:var(--tblr-bg-surface-secondary);color:#6b7280}.rack-loading-container{min-height:200px;margin-left:30px} diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 81d7f18dd..2dac95f44 100644 --- a/netbox/project-static/dist/netbox.js +++ b/netbox/project-static/dist/netbox.js @@ -1,11 +1,11 @@ -"use strict";(()=>{var su=Object.create;var Ni=Object.defineProperty,au=Object.defineProperties,lu=Object.getOwnPropertyDescriptor,cu=Object.getOwnPropertyDescriptors,uu=Object.getOwnPropertyNames,gs=Object.getOwnPropertySymbols,du=Object.getPrototypeOf,vs=Object.prototype.hasOwnProperty,fu=Object.prototype.propertyIsEnumerable;var Gr=(n,e,t)=>e in n?Ni(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,O=(n,e)=>{for(var t in e||(e={}))vs.call(e,t)&&Gr(n,t,e[t]);if(gs)for(var t of gs(e))fu.call(e,t)&&Gr(n,t,e[t]);return n},ae=(n,e)=>au(n,cu(e));var hu=(n,e)=>()=>(e||n((e={exports:{}}).exports,e),e.exports),Es=(n,e)=>{for(var t in e)Ni(n,t,{get:e[t],enumerable:!0})},pu=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of uu(e))!vs.call(n,r)&&r!==t&&Ni(n,r,{get:()=>e[r],enumerable:!(i=lu(e,r))||i.enumerable});return n};var mu=(n,e,t)=>(t=n!=null?su(du(n)):{},pu(e||!n||!n.__esModule?Ni(t,"default",{value:n,enumerable:!0}):t,n));var te=(n,e,t)=>Gr(n,typeof e!="symbol"?e+"":e,t);var at=(n,e,t)=>new Promise((i,r)=>{var o=l=>{try{a(t.next(l))}catch(c){r(c)}},s=l=>{try{a(t.throw(l))}catch(c){r(c)}},a=l=>l.done?i(l.value):Promise.resolve(l.value).then(o,s);a((t=t.apply(n,e)).next())});var _c=hu((Ei,is)=>{(function(e,t){typeof Ei=="object"&&typeof is=="object"?is.exports=t():typeof define=="function"&&define.amd?define([],t):typeof Ei=="object"?Ei.ClipboardJS=t():e.ClipboardJS=t()})(Ei,function(){return(function(){var n={686:(function(i,r,o){"use strict";o.d(r,{default:function(){return Ie}});var s=o(279),a=o.n(s),l=o(370),c=o.n(l),u=o(817),d=o.n(u);function p(q){try{return document.execCommand(q)}catch(M){return!1}}var E=function(M){var A=d()(M);return p("cut"),A},m=E;function v(q){var M=document.documentElement.getAttribute("dir")==="rtl",A=document.createElement("textarea");A.style.fontSize="12pt",A.style.border="0",A.style.padding="0",A.style.margin="0",A.style.position="absolute",A.style[M?"right":"left"]="-9999px";var $=window.pageYOffset||document.documentElement.scrollTop;return A.style.top="".concat($,"px"),A.setAttribute("readonly",""),A.value=q,A}var _=function(M,A){var $=v(M);A.container.appendChild($);var B=d()($);return p("copy"),$.remove(),B},C=function(M){var A=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},$="";return typeof M=="string"?$=_(M,A):M instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(M==null?void 0:M.type)?$=_(M.value,A):($=d()(M),p("copy")),$},D=C;function w(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?w=function(A){return typeof A}:w=function(A){return A&&typeof Symbol=="function"&&A.constructor===Symbol&&A!==Symbol.prototype?"symbol":typeof A},w(q)}var T=function(){var M=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},A=M.action,$=A===void 0?"copy":A,B=M.container,W=M.target,U=M.text;if($!=="copy"&&$!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(W!==void 0)if(W&&w(W)==="object"&&W.nodeType===1){if($==="copy"&&W.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if($==="cut"&&(W.hasAttribute("readonly")||W.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(U)return D(U,{container:B});if(W)return $==="cut"?m(W):D(W,{container:B})},K=T;function z(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?z=function(A){return typeof A}:z=function(A){return A&&typeof Symbol=="function"&&A.constructor===Symbol&&A!==Symbol.prototype?"symbol":typeof A},z(q)}function H(q,M){if(!(q instanceof M))throw new TypeError("Cannot call a class as a function")}function R(q,M){for(var A=0;A0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof B.action=="function"?B.action:this.defaultAction,this.target=typeof B.target=="function"?B.target:this.defaultTarget,this.text=typeof B.text=="function"?B.text:this.defaultText,this.container=z(B.container)==="object"?B.container:document.body}},{key:"listenClick",value:function(B){var W=this;this.listener=c()(B,"click",function(U){return W.onClick(U)})}},{key:"onClick",value:function(B){var W=B.delegateTarget||B.currentTarget,U=this.action(W)||"copy",ee=K({action:U,container:this.container,target:this.target(W),text:this.text(W)});this.emit(ee?"success":"error",{action:U,text:ee,trigger:W,clearSelection:function(){W&&W.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(B){return ie("action",B)}},{key:"defaultTarget",value:function(B){var W=ie("target",B);if(W)return document.querySelector(W)}},{key:"defaultText",value:function(B){return ie("text",B)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(B){var W=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return D(B,W)}},{key:"cut",value:function(B){return m(B)}},{key:"isSupported",value:function(){var B=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],W=typeof B=="string"?[B]:B,U=!!document.queryCommandSupported;return W.forEach(function(ee){U=U&&!!document.queryCommandSupported(ee)}),U}}]),A})(a()),Ie=Ue}),828:(function(i){var r=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var o=Element.prototype;o.matches=o.matchesSelector||o.mozMatchesSelector||o.msMatchesSelector||o.oMatchesSelector||o.webkitMatchesSelector}function s(a,l){for(;a&&a.nodeType!==r;){if(typeof a.matches=="function"&&a.matches(l))return a;a=a.parentNode}}i.exports=s}),438:(function(i,r,o){var s=o(828);function a(u,d,p,E,m){var v=c.apply(this,arguments);return u.addEventListener(p,v,m),{destroy:function(){u.removeEventListener(p,v,m)}}}function l(u,d,p,E,m){return typeof u.addEventListener=="function"?a.apply(null,arguments):typeof p=="function"?a.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return a(v,d,p,E,m)}))}function c(u,d,p,E){return function(m){m.delegateTarget=s(m.target,d),m.delegateTarget&&E.call(u,m)}}i.exports=l}),879:(function(i,r){r.node=function(o){return o!==void 0&&o instanceof HTMLElement&&o.nodeType===1},r.nodeList=function(o){var s=Object.prototype.toString.call(o);return o!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in o&&(o.length===0||r.node(o[0]))},r.string=function(o){return typeof o=="string"||o instanceof String},r.fn=function(o){var s=Object.prototype.toString.call(o);return s==="[object Function]"}}),370:(function(i,r,o){var s=o(879),a=o(438);function l(p,E,m){if(!p&&!E&&!m)throw new Error("Missing required arguments");if(!s.string(E))throw new TypeError("Second argument must be a String");if(!s.fn(m))throw new TypeError("Third argument must be a Function");if(s.node(p))return c(p,E,m);if(s.nodeList(p))return u(p,E,m);if(s.string(p))return d(p,E,m);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(p,E,m){return p.addEventListener(E,m),{destroy:function(){p.removeEventListener(E,m)}}}function u(p,E,m){return Array.prototype.forEach.call(p,function(v){v.addEventListener(E,m)}),{destroy:function(){Array.prototype.forEach.call(p,function(v){v.removeEventListener(E,m)})}}}function d(p,E,m){return a(document.body,p,E,m)}i.exports=l}),817:(function(i){function r(o){var s;if(o.nodeName==="SELECT")o.focus(),s=o.value;else if(o.nodeName==="INPUT"||o.nodeName==="TEXTAREA"){var a=o.hasAttribute("readonly");a||o.setAttribute("readonly",""),o.select(),o.setSelectionRange(0,o.value.length),a||o.removeAttribute("readonly"),s=o.value}else{o.hasAttribute("contenteditable")&&o.focus();var l=window.getSelection(),c=document.createRange();c.selectNodeContents(o),l.removeAllRanges(),l.addRange(c),s=l.toString()}return s}i.exports=r}),279:(function(i){function r(){}r.prototype={on:function(o,s,a){var l=this.e||(this.e={});return(l[o]||(l[o]=[])).push({fn:s,ctx:a}),this},once:function(o,s,a){var l=this;function c(){l.off(o,c),s.apply(a,arguments)}return c._=s,this.on(o,c,a)},emit:function(o){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[o]||[]).slice(),l=0,c=a.length;for(l;lTs,afterRead:()=>_s,afterWrite:()=>As,applyStyles:()=>pn,arrow:()=>Hi,auto:()=>qn,basePlacements:()=>lt,beforeMain:()=>ws,beforeRead:()=>ys,beforeWrite:()=>Cs,bottom:()=>ge,clippingParents:()=>Kr,computeStyles:()=>gn,createPopper:()=>Zn,createPopperBase:()=>Is,createPopperLite:()=>Ps,detectOverflow:()=>ke,end:()=>bt,eventListeners:()=>vn,flip:()=>Fi,hide:()=>$i,left:()=>pe,main:()=>xs,modifierPhases:()=>Qr,offset:()=>Bi,placements:()=>Un,popper:()=>$t,popperGenerator:()=>Yt,popperOffsets:()=>bn,preventOverflow:()=>Vi,read:()=>bs,reference:()=>Xr,right:()=>me,start:()=>rt,top:()=>de,variationPlacements:()=>ki,viewport:()=>Wn,write:()=>Ss});var de="top",ge="bottom",me="right",pe="left",qn="auto",lt=[de,ge,me,pe],rt="start",bt="end",Kr="clippingParents",Wn="viewport",$t="popper",Xr="reference",ki=lt.reduce(function(n,e){return n.concat([e+"-"+rt,e+"-"+bt])},[]),Un=[].concat(lt,[qn]).reduce(function(n,e){return n.concat([e,e+"-"+rt,e+"-"+bt])},[]),ys="beforeRead",bs="read",_s="afterRead",ws="beforeMain",xs="main",Ts="afterMain",Cs="beforeWrite",Ss="write",As="afterWrite",Qr=[ys,bs,_s,ws,xs,Ts,Cs,Ss,As];function xe(n){return n?(n.nodeName||"").toLowerCase():null}function ce(n){if(n==null)return window;if(n.toString()!=="[object Window]"){var e=n.ownerDocument;return e&&e.defaultView||window}return n}function Ye(n){var e=ce(n).Element;return n instanceof e||n instanceof Element}function _e(n){var e=ce(n).HTMLElement;return n instanceof e||n instanceof HTMLElement}function hn(n){if(typeof ShadowRoot=="undefined")return!1;var e=ce(n).ShadowRoot;return n instanceof e||n instanceof ShadowRoot}function gu(n){var e=n.state;Object.keys(e.elements).forEach(function(t){var i=e.styles[t]||{},r=e.attributes[t]||{},o=e.elements[t];!_e(o)||!xe(o)||(Object.assign(o.style,i),Object.keys(r).forEach(function(s){var a=r[s];a===!1?o.removeAttribute(s):o.setAttribute(s,a===!0?"":a)}))})}function vu(n){var e=n.state,t={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,t.popper),e.styles=t,e.elements.arrow&&Object.assign(e.elements.arrow.style,t.arrow),function(){Object.keys(e.elements).forEach(function(i){var r=e.elements[i],o=e.attributes[i]||{},s=Object.keys(e.styles.hasOwnProperty(i)?e.styles[i]:t[i]),a=s.reduce(function(l,c){return l[c]="",l},{});!_e(r)||!xe(r)||(Object.assign(r.style,a),Object.keys(o).forEach(function(l){r.removeAttribute(l)}))})}}var pn={name:"applyStyles",enabled:!0,phase:"write",fn:gu,effect:vu,requires:["computeStyles"]};function Te(n){return n.split("-")[0]}var Ze=Math.max,Bt=Math.min,ct=Math.round;function mn(){var n=navigator.userAgentData;return n!=null&&n.brands&&Array.isArray(n.brands)?n.brands.map(function(e){return e.brand+"/"+e.version}).join(" "):navigator.userAgent}function Yn(){return!/^((?!chrome|android).)*safari/i.test(mn())}function Ge(n,e,t){e===void 0&&(e=!1),t===void 0&&(t=!1);var i=n.getBoundingClientRect(),r=1,o=1;e&&_e(n)&&(r=n.offsetWidth>0&&ct(i.width)/n.offsetWidth||1,o=n.offsetHeight>0&&ct(i.height)/n.offsetHeight||1);var s=Ye(n)?ce(n):window,a=s.visualViewport,l=!Yn()&&t,c=(i.left+(l&&a?a.offsetLeft:0))/r,u=(i.top+(l&&a?a.offsetTop:0))/o,d=i.width/r,p=i.height/o;return{width:d,height:p,top:u,right:c+d,bottom:u+p,left:c,x:c,y:u}}function Vt(n){var e=Ge(n),t=n.offsetWidth,i=n.offsetHeight;return Math.abs(e.width-t)<=1&&(t=e.width),Math.abs(e.height-i)<=1&&(i=e.height),{x:n.offsetLeft,y:n.offsetTop,width:t,height:i}}function Gn(n,e){var t=e.getRootNode&&e.getRootNode();if(n.contains(e))return!0;if(t&&hn(t)){var i=e;do{if(i&&n.isSameNode(i))return!0;i=i.parentNode||i.host}while(i)}return!1}function Ne(n){return ce(n).getComputedStyle(n)}function Jr(n){return["table","td","th"].indexOf(xe(n))>=0}function Se(n){return((Ye(n)?n.ownerDocument:n.document)||window.document).documentElement}function ut(n){return xe(n)==="html"?n:n.assignedSlot||n.parentNode||(hn(n)?n.host:null)||Se(n)}function Ds(n){return!_e(n)||Ne(n).position==="fixed"?null:n.offsetParent}function Eu(n){var e=/firefox/i.test(mn()),t=/Trident/i.test(mn());if(t&&_e(n)){var i=Ne(n);if(i.position==="fixed")return null}var r=ut(n);for(hn(r)&&(r=r.host);_e(r)&&["html","body"].indexOf(xe(r))<0;){var o=Ne(r);if(o.transform!=="none"||o.perspective!=="none"||o.contain==="paint"||["transform","perspective"].indexOf(o.willChange)!==-1||e&&o.willChange==="filter"||e&&o.filter&&o.filter!=="none")return r;r=r.parentNode}return null}function et(n){for(var e=ce(n),t=Ds(n);t&&Jr(t)&&Ne(t).position==="static";)t=Ds(t);return t&&(xe(t)==="html"||xe(t)==="body"&&Ne(t).position==="static")?e:t||Eu(n)||e}function zt(n){return["top","bottom"].indexOf(n)>=0?"x":"y"}function jt(n,e,t){return Ze(n,Bt(e,t))}function Os(n,e,t){var i=jt(n,e,t);return i>t?t:i}function Kn(){return{top:0,right:0,bottom:0,left:0}}function Xn(n){return Object.assign({},Kn(),n)}function Qn(n,e){return e.reduce(function(t,i){return t[i]=n,t},{})}var yu=function(e,t){return e=typeof e=="function"?e(Object.assign({},t.rects,{placement:t.placement})):e,Xn(typeof e!="number"?e:Qn(e,lt))};function bu(n){var e,t=n.state,i=n.name,r=n.options,o=t.elements.arrow,s=t.modifiersData.popperOffsets,a=Te(t.placement),l=zt(a),c=[pe,me].indexOf(a)>=0,u=c?"height":"width";if(!(!o||!s)){var d=yu(r.padding,t),p=Vt(o),E=l==="y"?de:pe,m=l==="y"?ge:me,v=t.rects.reference[u]+t.rects.reference[l]-s[l]-t.rects.popper[u],_=s[l]-t.rects.reference[l],C=et(o),D=C?l==="y"?C.clientHeight||0:C.clientWidth||0:0,w=v/2-_/2,T=d[E],K=D-p[u]-d[m],z=D/2-p[u]/2+w,H=jt(T,z,K),R=l;t.modifiersData[i]=(e={},e[R]=H,e.centerOffset=H-z,e)}}function _u(n){var e=n.state,t=n.options,i=t.element,r=i===void 0?"[data-popper-arrow]":i;r!=null&&(typeof r=="string"&&(r=e.elements.popper.querySelector(r),!r)||Gn(e.elements.popper,r)&&(e.elements.arrow=r))}var Hi={name:"arrow",enabled:!0,phase:"main",fn:bu,effect:_u,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Ke(n){return n.split("-")[1]}var wu={top:"auto",right:"auto",bottom:"auto",left:"auto"};function xu(n,e){var t=n.x,i=n.y,r=e.devicePixelRatio||1;return{x:ct(t*r)/r||0,y:ct(i*r)/r||0}}function Ls(n){var e,t=n.popper,i=n.popperRect,r=n.placement,o=n.variation,s=n.offsets,a=n.position,l=n.gpuAcceleration,c=n.adaptive,u=n.roundOffsets,d=n.isFixed,p=s.x,E=p===void 0?0:p,m=s.y,v=m===void 0?0:m,_=typeof u=="function"?u({x:E,y:v}):{x:E,y:v};E=_.x,v=_.y;var C=s.hasOwnProperty("x"),D=s.hasOwnProperty("y"),w=pe,T=de,K=window;if(c){var z=et(t),H="clientHeight",R="clientWidth";if(z===ce(t)&&(z=Se(t),Ne(z).position!=="static"&&a==="absolute"&&(H="scrollHeight",R="scrollWidth")),z=z,r===de||(r===pe||r===me)&&o===bt){T=ge;var L=d&&z===K&&K.visualViewport?K.visualViewport.height:z[H];v-=L-i.height,v*=l?1:-1}if(r===pe||(r===de||r===ge)&&o===bt){w=me;var j=d&&z===K&&K.visualViewport?K.visualViewport.width:z[R];E-=j-i.width,E*=l?1:-1}}var Y=Object.assign({position:a},c&&wu),Q=u===!0?xu({x:E,y:v},ce(t)):{x:E,y:v};if(E=Q.x,v=Q.y,l){var Z;return Object.assign({},Y,(Z={},Z[T]=D?"0":"",Z[w]=C?"0":"",Z.transform=(K.devicePixelRatio||1)<=1?"translate("+E+"px, "+v+"px)":"translate3d("+E+"px, "+v+"px, 0)",Z))}return Object.assign({},Y,(e={},e[T]=D?v+"px":"",e[w]=C?E+"px":"",e.transform="",e))}function Tu(n){var e=n.state,t=n.options,i=t.gpuAcceleration,r=i===void 0?!0:i,o=t.adaptive,s=o===void 0?!0:o,a=t.roundOffsets,l=a===void 0?!0:a,c={placement:Te(e.placement),variation:Ke(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:r,isFixed:e.options.strategy==="fixed"};e.modifiersData.popperOffsets!=null&&(e.styles.popper=Object.assign({},e.styles.popper,Ls(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:s,roundOffsets:l})))),e.modifiersData.arrow!=null&&(e.styles.arrow=Object.assign({},e.styles.arrow,Ls(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})}var gn={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:Tu,data:{}};var Ri={passive:!0};function Cu(n){var e=n.state,t=n.instance,i=n.options,r=i.scroll,o=r===void 0?!0:r,s=i.resize,a=s===void 0?!0:s,l=ce(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach(function(u){u.addEventListener("scroll",t.update,Ri)}),a&&l.addEventListener("resize",t.update,Ri),function(){o&&c.forEach(function(u){u.removeEventListener("scroll",t.update,Ri)}),a&&l.removeEventListener("resize",t.update,Ri)}}var vn={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:Cu,data:{}};var Su={left:"right",right:"left",bottom:"top",top:"bottom"};function En(n){return n.replace(/left|right|bottom|top/g,function(e){return Su[e]})}var Au={start:"end",end:"start"};function Ii(n){return n.replace(/start|end/g,function(e){return Au[e]})}function qt(n){var e=ce(n),t=e.pageXOffset,i=e.pageYOffset;return{scrollLeft:t,scrollTop:i}}function Wt(n){return Ge(Se(n)).left+qt(n).scrollLeft}function Zr(n,e){var t=ce(n),i=Se(n),r=t.visualViewport,o=i.clientWidth,s=i.clientHeight,a=0,l=0;if(r){o=r.width,s=r.height;var c=Yn();(c||!c&&e==="fixed")&&(a=r.offsetLeft,l=r.offsetTop)}return{width:o,height:s,x:a+Wt(n),y:l}}function eo(n){var e,t=Se(n),i=qt(n),r=(e=n.ownerDocument)==null?void 0:e.body,o=Ze(t.scrollWidth,t.clientWidth,r?r.scrollWidth:0,r?r.clientWidth:0),s=Ze(t.scrollHeight,t.clientHeight,r?r.scrollHeight:0,r?r.clientHeight:0),a=-i.scrollLeft+Wt(n),l=-i.scrollTop;return Ne(r||t).direction==="rtl"&&(a+=Ze(t.clientWidth,r?r.clientWidth:0)-o),{width:o,height:s,x:a,y:l}}function Ut(n){var e=Ne(n),t=e.overflow,i=e.overflowX,r=e.overflowY;return/auto|scroll|overlay|hidden/.test(t+r+i)}function Pi(n){return["html","body","#document"].indexOf(xe(n))>=0?n.ownerDocument.body:_e(n)&&Ut(n)?n:Pi(ut(n))}function _t(n,e){var t;e===void 0&&(e=[]);var i=Pi(n),r=i===((t=n.ownerDocument)==null?void 0:t.body),o=ce(i),s=r?[o].concat(o.visualViewport||[],Ut(i)?i:[]):i,a=e.concat(s);return r?a:a.concat(_t(ut(s)))}function yn(n){return Object.assign({},n,{left:n.x,top:n.y,right:n.x+n.width,bottom:n.y+n.height})}function Du(n,e){var t=Ge(n,!1,e==="fixed");return t.top=t.top+n.clientTop,t.left=t.left+n.clientLeft,t.bottom=t.top+n.clientHeight,t.right=t.left+n.clientWidth,t.width=n.clientWidth,t.height=n.clientHeight,t.x=t.left,t.y=t.top,t}function Ms(n,e,t){return e===Wn?yn(Zr(n,t)):Ye(e)?Du(e,t):yn(eo(Se(n)))}function Ou(n){var e=_t(ut(n)),t=["absolute","fixed"].indexOf(Ne(n).position)>=0,i=t&&_e(n)?et(n):n;return Ye(i)?e.filter(function(r){return Ye(r)&&Gn(r,i)&&xe(r)!=="body"}):[]}function to(n,e,t,i){var r=e==="clippingParents"?Ou(n):[].concat(e),o=[].concat(r,[t]),s=o[0],a=o.reduce(function(l,c){var u=Ms(n,c,i);return l.top=Ze(u.top,l.top),l.right=Bt(u.right,l.right),l.bottom=Bt(u.bottom,l.bottom),l.left=Ze(u.left,l.left),l},Ms(n,s,i));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}function Jn(n){var e=n.reference,t=n.element,i=n.placement,r=i?Te(i):null,o=i?Ke(i):null,s=e.x+e.width/2-t.width/2,a=e.y+e.height/2-t.height/2,l;switch(r){case de:l={x:s,y:e.y-t.height};break;case ge:l={x:s,y:e.y+e.height};break;case me:l={x:e.x+e.width,y:a};break;case pe:l={x:e.x-t.width,y:a};break;default:l={x:e.x,y:e.y}}var c=r?zt(r):null;if(c!=null){var u=c==="y"?"height":"width";switch(o){case rt:l[c]=l[c]-(e[u]/2-t[u]/2);break;case bt:l[c]=l[c]+(e[u]/2-t[u]/2);break;default:}}return l}function ke(n,e){e===void 0&&(e={});var t=e,i=t.placement,r=i===void 0?n.placement:i,o=t.strategy,s=o===void 0?n.strategy:o,a=t.boundary,l=a===void 0?Kr:a,c=t.rootBoundary,u=c===void 0?Wn:c,d=t.elementContext,p=d===void 0?$t:d,E=t.altBoundary,m=E===void 0?!1:E,v=t.padding,_=v===void 0?0:v,C=Xn(typeof _!="number"?_:Qn(_,lt)),D=p===$t?Xr:$t,w=n.rects.popper,T=n.elements[m?D:p],K=to(Ye(T)?T:T.contextElement||Se(n.elements.popper),l,u,s),z=Ge(n.elements.reference),H=Jn({reference:z,element:w,strategy:"absolute",placement:r}),R=yn(Object.assign({},w,H)),L=p===$t?R:z,j={top:K.top-L.top+C.top,bottom:L.bottom-K.bottom+C.bottom,left:K.left-L.left+C.left,right:L.right-K.right+C.right},Y=n.modifiersData.offset;if(p===$t&&Y){var Q=Y[r];Object.keys(j).forEach(function(Z){var he=[me,ge].indexOf(Z)>=0?1:-1,Ce=[de,ge].indexOf(Z)>=0?"y":"x";j[Z]+=Q[Ce]*he})}return j}function no(n,e){e===void 0&&(e={});var t=e,i=t.placement,r=t.boundary,o=t.rootBoundary,s=t.padding,a=t.flipVariations,l=t.allowedAutoPlacements,c=l===void 0?Un:l,u=Ke(i),d=u?a?ki:ki.filter(function(m){return Ke(m)===u}):lt,p=d.filter(function(m){return c.indexOf(m)>=0});p.length===0&&(p=d);var E=p.reduce(function(m,v){return m[v]=ke(n,{placement:v,boundary:r,rootBoundary:o,padding:s})[Te(v)],m},{});return Object.keys(E).sort(function(m,v){return E[m]-E[v]})}function Lu(n){if(Te(n)===qn)return[];var e=En(n);return[Ii(n),e,Ii(e)]}function Mu(n){var e=n.state,t=n.options,i=n.name;if(!e.modifiersData[i]._skip){for(var r=t.mainAxis,o=r===void 0?!0:r,s=t.altAxis,a=s===void 0?!0:s,l=t.fallbackPlacements,c=t.padding,u=t.boundary,d=t.rootBoundary,p=t.altBoundary,E=t.flipVariations,m=E===void 0?!0:E,v=t.allowedAutoPlacements,_=e.options.placement,C=Te(_),D=C===_,w=l||(D||!m?[En(_)]:Lu(_)),T=[_].concat(w).reduce(function(B,W){return B.concat(Te(W)===qn?no(e,{placement:W,boundary:u,rootBoundary:d,padding:c,flipVariations:m,allowedAutoPlacements:v}):W)},[]),K=e.rects.reference,z=e.rects.popper,H=new Map,R=!0,L=T[0],j=0;j=0,Ce=he?"width":"height",re=ke(e,{placement:Y,boundary:u,rootBoundary:d,altBoundary:p,padding:c}),ie=he?Z?me:pe:Z?ge:de;K[Ce]>z[Ce]&&(ie=En(ie));var Ue=En(ie),Ie=[];if(o&&Ie.push(re[Q]<=0),a&&Ie.push(re[ie]<=0,re[Ue]<=0),Ie.every(function(B){return B})){L=Y,R=!1;break}H.set(Y,Ie)}if(R)for(var q=m?3:1,M=function(W){var U=T.find(function(ee){var se=H.get(ee);if(se)return se.slice(0,W).every(function(yt){return yt})});if(U)return L=U,"break"},A=q;A>0;A--){var $=M(A);if($==="break")break}e.placement!==L&&(e.modifiersData[i]._skip=!0,e.placement=L,e.reset=!0)}}var Fi={name:"flip",enabled:!0,phase:"main",fn:Mu,requiresIfExists:["offset"],data:{_skip:!1}};function Ns(n,e,t){return t===void 0&&(t={x:0,y:0}),{top:n.top-e.height-t.y,right:n.right-e.width+t.x,bottom:n.bottom-e.height+t.y,left:n.left-e.width-t.x}}function ks(n){return[de,me,ge,pe].some(function(e){return n[e]>=0})}function Nu(n){var e=n.state,t=n.name,i=e.rects.reference,r=e.rects.popper,o=e.modifiersData.preventOverflow,s=ke(e,{elementContext:"reference"}),a=ke(e,{altBoundary:!0}),l=Ns(s,i),c=Ns(a,r,o),u=ks(l),d=ks(c);e.modifiersData[t]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:u,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":u,"data-popper-escaped":d})}var $i={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:Nu};function ku(n,e,t){var i=Te(n),r=[pe,de].indexOf(i)>=0?-1:1,o=typeof t=="function"?t(Object.assign({},e,{placement:n})):t,s=o[0],a=o[1];return s=s||0,a=(a||0)*r,[pe,me].indexOf(i)>=0?{x:a,y:s}:{x:s,y:a}}function Hu(n){var e=n.state,t=n.options,i=n.name,r=t.offset,o=r===void 0?[0,0]:r,s=Un.reduce(function(u,d){return u[d]=ku(d,e.rects,o),u},{}),a=s[e.placement],l=a.x,c=a.y;e.modifiersData.popperOffsets!=null&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[i]=s}var Bi={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:Hu};function Ru(n){var e=n.state,t=n.name;e.modifiersData[t]=Jn({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})}var bn={name:"popperOffsets",enabled:!0,phase:"read",fn:Ru,data:{}};function io(n){return n==="x"?"y":"x"}function Iu(n){var e=n.state,t=n.options,i=n.name,r=t.mainAxis,o=r===void 0?!0:r,s=t.altAxis,a=s===void 0?!1:s,l=t.boundary,c=t.rootBoundary,u=t.altBoundary,d=t.padding,p=t.tether,E=p===void 0?!0:p,m=t.tetherOffset,v=m===void 0?0:m,_=ke(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:u}),C=Te(e.placement),D=Ke(e.placement),w=!D,T=zt(C),K=io(T),z=e.modifiersData.popperOffsets,H=e.rects.reference,R=e.rects.popper,L=typeof v=="function"?v(Object.assign({},e.rects,{placement:e.placement})):v,j=typeof L=="number"?{mainAxis:L,altAxis:L}:Object.assign({mainAxis:0,altAxis:0},L),Y=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,Q={x:0,y:0};if(z){if(o){var Z,he=T==="y"?de:pe,Ce=T==="y"?ge:me,re=T==="y"?"height":"width",ie=z[T],Ue=ie+_[he],Ie=ie-_[Ce],q=E?-R[re]/2:0,M=D===rt?H[re]:R[re],A=D===rt?-R[re]:-H[re],$=e.elements.arrow,B=E&&$?Vt($):{width:0,height:0},W=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:Kn(),U=W[he],ee=W[Ce],se=jt(0,H[re],B[re]),yt=w?H[re]/2-q-se-U-j.mainAxis:M-se-U-j.mainAxis,zr=w?-H[re]/2+q+se+ee+j.mainAxis:A+se+ee+j.mainAxis,sn=e.elements.arrow&&et(e.elements.arrow),an=sn?T==="y"?sn.clientTop||0:sn.clientLeft||0:0,Ci=(Z=Y==null?void 0:Y[T])!=null?Z:0,jr=ie+yt-Ci-an,Si=ie+zr-Ci,Ai=jt(E?Bt(Ue,jr):Ue,ie,E?Ze(Ie,Si):Ie);z[T]=Ai,Q[T]=Ai-ie}if(a){var Bn,Di=T==="x"?de:pe,ln=T==="x"?ge:me,ot=z[K],cn=K==="y"?"height":"width",Vn=ot+_[Di],un=ot-_[ln],dn=[de,pe].indexOf(C)!==-1,Ft=(Bn=Y==null?void 0:Y[K])!=null?Bn:0,Oi=dn?Vn:ot-H[cn]-R[cn]-Ft+j.altAxis,zn=dn?ot+H[cn]+R[cn]-Ft-j.altAxis:un,Li=E&&dn?Os(Oi,ot,zn):jt(E?Oi:Vn,ot,E?zn:un);z[K]=Li,Q[K]=Li-ot}e.modifiersData[i]=Q}}var Vi={name:"preventOverflow",enabled:!0,phase:"main",fn:Iu,requiresIfExists:["offset"]};function ro(n){return{scrollLeft:n.scrollLeft,scrollTop:n.scrollTop}}function oo(n){return n===ce(n)||!_e(n)?qt(n):ro(n)}function Pu(n){var e=n.getBoundingClientRect(),t=ct(e.width)/n.offsetWidth||1,i=ct(e.height)/n.offsetHeight||1;return t!==1||i!==1}function so(n,e,t){t===void 0&&(t=!1);var i=_e(e),r=_e(e)&&Pu(e),o=Se(e),s=Ge(n,r,t),a={scrollLeft:0,scrollTop:0},l={x:0,y:0};return(i||!i&&!t)&&((xe(e)!=="body"||Ut(o))&&(a=oo(e)),_e(e)?(l=Ge(e,!0),l.x+=e.clientLeft,l.y+=e.clientTop):o&&(l.x=Wt(o))),{x:s.left+a.scrollLeft-l.x,y:s.top+a.scrollTop-l.y,width:s.width,height:s.height}}function Fu(n){var e=new Map,t=new Set,i=[];n.forEach(function(o){e.set(o.name,o)});function r(o){t.add(o.name);var s=[].concat(o.requires||[],o.requiresIfExists||[]);s.forEach(function(a){if(!t.has(a)){var l=e.get(a);l&&r(l)}}),i.push(o)}return n.forEach(function(o){t.has(o.name)||r(o)}),i}function ao(n){var e=Fu(n);return Qr.reduce(function(t,i){return t.concat(e.filter(function(r){return r.phase===i}))},[])}function lo(n){var e;return function(){return e||(e=new Promise(function(t){Promise.resolve().then(function(){e=void 0,t(n())})})),e}}function co(n){var e=n.reduce(function(t,i){var r=t[i.name];return t[i.name]=r?Object.assign({},r,i,{options:Object.assign({},r.options,i.options),data:Object.assign({},r.data,i.data)}):i,t},{});return Object.keys(e).map(function(t){return e[t]})}var Hs={placement:"bottom",modifiers:[],strategy:"absolute"};function Rs(){for(var n=arguments.length,e=new Array(n),t=0;t(n&&window.CSS&&window.CSS.escape&&(n=n.replace(/#([^\s"#']+)/g,(e,t)=>`#${CSS.escape(t)}`)),n),ju=n=>n==null?`${n}`:Object.prototype.toString.call(n).match(/\s([a-z]+)/i)[1].toLowerCase(),qu=n=>{do n+=Math.floor(Math.random()*Vu);while(document.getElementById(n));return n},Wu=n=>{if(!n)return 0;let{transitionDuration:e,transitionDelay:t}=window.getComputedStyle(n),i=Number.parseFloat(e),r=Number.parseFloat(t);return!i&&!r?0:(e=e.split(",")[0],t=t.split(",")[0],(Number.parseFloat(e)+Number.parseFloat(t))*zu)},ha=n=>{n.dispatchEvent(new Event(Ao))},dt=n=>!n||typeof n!="object"?!1:(typeof n.jquery!="undefined"&&(n=n[0]),typeof n.nodeType!="undefined"),xt=n=>dt(n)?n.jquery?n[0]:n:typeof n=="string"&&n.length>0?document.querySelector(fa(n)):null,An=n=>{if(!dt(n)||n.getClientRects().length===0)return!1;let e=getComputedStyle(n).getPropertyValue("visibility")==="visible",t=n.closest("details:not([open])");if(!t)return e;if(t!==n){let i=n.closest("summary");if(i&&i.parentNode!==t||i===null)return!1}return e},Tt=n=>!n||n.nodeType!==Node.ELEMENT_NODE||n.classList.contains("disabled")?!0:typeof n.disabled!="undefined"?n.disabled:n.hasAttribute("disabled")&&n.getAttribute("disabled")!=="false",pa=n=>{if(!document.documentElement.attachShadow)return null;if(typeof n.getRootNode=="function"){let e=n.getRootNode();return e instanceof ShadowRoot?e:null}return n instanceof ShadowRoot?n:n.parentNode?pa(n.parentNode):null},Qi=()=>{},ri=n=>{n.offsetHeight},ma=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,fo=[],Uu=n=>{document.readyState==="loading"?(fo.length||document.addEventListener("DOMContentLoaded",()=>{for(let e of fo)e()}),fo.push(n)):n()},Xe=()=>document.documentElement.dir==="rtl",Je=n=>{Uu(()=>{let e=ma();if(e){let t=n.NAME,i=e.fn[t];e.fn[t]=n.jQueryInterface,e.fn[t].Constructor=n,e.fn[t].noConflict=()=>(e.fn[t]=i,n.jQueryInterface)}})},Pe=(n,e=[],t=n)=>typeof n=="function"?n.call(...e):t,ga=(n,e,t=!0)=>{if(!t){Pe(n);return}let r=Wu(e)+5,o=!1,s=({target:a})=>{a===e&&(o=!0,e.removeEventListener(Ao,s),Pe(n))};e.addEventListener(Ao,s),setTimeout(()=>{o||ha(e)},r)},Mo=(n,e,t,i)=>{let r=n.length,o=n.indexOf(e);return o===-1?!t&&i?n[r-1]:n[0]:(o+=t?1:-1,i&&(o=(o+r)%r),n[Math.max(0,Math.min(o,r-1))])},Yu=/[^.]*(?=\..*)\.|.*/,Gu=/\..*/,Ku=/::\d+$/,ho={},Fs=1,va={mouseenter:"mouseover",mouseleave:"mouseout"},Xu=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function Ea(n,e){return e&&`${e}::${Fs++}`||n.uidEvent||Fs++}function ya(n){let e=Ea(n);return n.uidEvent=e,ho[e]=ho[e]||{},ho[e]}function Qu(n,e){return function t(i){return No(i,{delegateTarget:n}),t.oneOff&&x.off(n,i.type,e),e.apply(n,[i])}}function Ju(n,e,t){return function i(r){let o=n.querySelectorAll(e);for(let{target:s}=r;s&&s!==this;s=s.parentNode)for(let a of o)if(a===s)return No(r,{delegateTarget:s}),i.oneOff&&x.off(n,r.type,e,t),t.apply(s,[r])}}function ba(n,e,t=null){return Object.values(n).find(i=>i.callable===e&&i.delegationSelector===t)}function _a(n,e,t){let i=typeof e=="string",r=i?t:e||t,o=wa(n);return Xu.has(o)||(o=n),[i,r,o]}function $s(n,e,t,i,r){if(typeof e!="string"||!n)return;let[o,s,a]=_a(e,t,i);e in va&&(s=(m=>function(v){if(!v.relatedTarget||v.relatedTarget!==v.delegateTarget&&!v.delegateTarget.contains(v.relatedTarget))return m.call(this,v)})(s));let l=ya(n),c=l[a]||(l[a]={}),u=ba(c,s,o?t:null);if(u){u.oneOff=u.oneOff&&r;return}let d=Ea(s,e.replace(Yu,"")),p=o?Ju(n,t,s):Qu(n,s);p.delegationSelector=o?t:null,p.callable=s,p.oneOff=r,p.uidEvent=d,c[d]=p,n.addEventListener(a,p,o)}function Do(n,e,t,i,r){let o=ba(e[t],i,r);o&&(n.removeEventListener(t,o,!!r),delete e[t][o.uidEvent])}function Zu(n,e,t,i){let r=e[t]||{};for(let[o,s]of Object.entries(r))o.includes(i)&&Do(n,e,t,s.callable,s.delegationSelector)}function wa(n){return n=n.replace(Gu,""),va[n]||n}var x={on(n,e,t,i){$s(n,e,t,i,!1)},one(n,e,t,i){$s(n,e,t,i,!0)},off(n,e,t,i){if(typeof e!="string"||!n)return;let[r,o,s]=_a(e,t,i),a=s!==e,l=ya(n),c=l[s]||{},u=e.startsWith(".");if(typeof o!="undefined"){if(!Object.keys(c).length)return;Do(n,l,s,o,r?t:null);return}if(u)for(let d of Object.keys(l))Zu(n,l,d,e.slice(1));for(let[d,p]of Object.entries(c)){let E=d.replace(Ku,"");(!a||e.includes(E))&&Do(n,l,s,p.callable,p.delegationSelector)}},trigger(n,e,t){if(typeof e!="string"||!n)return null;let i=ma(),r=wa(e),o=e!==r,s=null,a=!0,l=!0,c=!1;o&&i&&(s=i.Event(e,t),i(n).trigger(s),a=!s.isPropagationStopped(),l=!s.isImmediatePropagationStopped(),c=s.isDefaultPrevented());let u=No(new Event(e,{bubbles:a,cancelable:!0}),t);return c&&u.preventDefault(),l&&n.dispatchEvent(u),u.defaultPrevented&&s&&s.preventDefault(),u}};function No(n,e={}){for(let[t,i]of Object.entries(e))try{n[t]=i}catch(r){Object.defineProperty(n,t,{configurable:!0,get(){return i}})}return n}function Bs(n){if(n==="true")return!0;if(n==="false")return!1;if(n===Number(n).toString())return Number(n);if(n===""||n==="null")return null;if(typeof n!="string")return n;try{return JSON.parse(decodeURIComponent(n))}catch(e){return n}}function po(n){return n.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}var ft={setDataAttribute(n,e,t){n.setAttribute(`data-bs-${po(e)}`,t)},removeDataAttribute(n,e){n.removeAttribute(`data-bs-${po(e)}`)},getDataAttributes(n){if(!n)return{};let e={},t=Object.keys(n.dataset).filter(i=>i.startsWith("bs")&&!i.startsWith("bsConfig"));for(let i of t){let r=i.replace(/^bs/,"");r=r.charAt(0).toLowerCase()+r.slice(1),e[r]=Bs(n.dataset[i])}return e},getDataAttribute(n,e){return Bs(n.getAttribute(`data-bs-${po(e)}`))}},Xt=class{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(e){return e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e}_mergeConfigObj(e,t){let i=dt(t)?ft.getDataAttribute(t,"config"):{};return O(O(O(O({},this.constructor.Default),typeof i=="object"?i:{}),dt(t)?ft.getDataAttributes(t):{}),typeof e=="object"?e:{})}_typeCheckConfig(e,t=this.constructor.DefaultType){for(let[i,r]of Object.entries(t)){let o=e[i],s=dt(o)?"element":ju(o);if(!new RegExp(r).test(s))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${i}" provided type "${s}" but expected type "${r}".`)}}},ed="5.3.8",We=class extends Xt{constructor(e,t){super(),e=xt(e),e&&(this._element=e,this._config=this._getConfig(t),uo.set(this._element,this.constructor.DATA_KEY,this))}dispose(){uo.remove(this._element,this.constructor.DATA_KEY),x.off(this._element,this.constructor.EVENT_KEY);for(let e of Object.getOwnPropertyNames(this))this[e]=null}_queueCallback(e,t,i=!0){ga(e,t,i)}_getConfig(e){return e=this._mergeConfigObj(e,this._element),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}static getInstance(e){return uo.get(xt(e),this.DATA_KEY)}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,typeof t=="object"?t:null)}static get VERSION(){return ed}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(e){return`${e}${this.EVENT_KEY}`}},mo=n=>{let e=n.getAttribute("data-bs-target");if(!e||e==="#"){let t=n.getAttribute("href");if(!t||!t.includes("#")&&!t.startsWith("."))return null;t.includes("#")&&!t.startsWith("#")&&(t=`#${t.split("#")[1]}`),e=t&&t!=="#"?t.trim():null}return e?e.split(",").map(t=>fa(t)).join(","):null},V={find(n,e=document.documentElement){return[].concat(...Element.prototype.querySelectorAll.call(e,n))},findOne(n,e=document.documentElement){return Element.prototype.querySelector.call(e,n)},children(n,e){return[].concat(...n.children).filter(t=>t.matches(e))},parents(n,e){let t=[],i=n.parentNode.closest(e);for(;i;)t.push(i),i=i.parentNode.closest(e);return t},prev(n,e){let t=n.previousElementSibling;for(;t;){if(t.matches(e))return[t];t=t.previousElementSibling}return[]},next(n,e){let t=n.nextElementSibling;for(;t;){if(t.matches(e))return[t];t=t.nextElementSibling}return[]},focusableChildren(n){let e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map(t=>`${t}:not([tabindex^="-"])`).join(",");return this.find(e,n).filter(t=>!Tt(t)&&An(t))},getSelectorFromElement(n){let e=mo(n);return e&&V.findOne(e)?e:null},getElementFromSelector(n){let e=mo(n);return e?V.findOne(e):null},getMultipleElementsFromSelector(n){let e=mo(n);return e?V.find(e):[]}},or=(n,e="hide")=>{let t=`click.dismiss${n.EVENT_KEY}`,i=n.NAME;x.on(document,t,`[data-bs-dismiss="${i}"]`,function(r){if(["A","AREA"].includes(this.tagName)&&r.preventDefault(),Tt(this))return;let o=V.getElementFromSelector(this)||this.closest(`.${i}`);n.getOrCreateInstance(o)[e]()})},td="alert",nd="bs.alert",xa=`.${nd}`,id=`close${xa}`,rd=`closed${xa}`,od="fade",sd="show",Ji=class n extends We{static get NAME(){return td}close(){if(x.trigger(this._element,id).defaultPrevented)return;this._element.classList.remove(sd);let t=this._element.classList.contains(od);this._queueCallback(()=>this._destroyElement(),this._element,t)}_destroyElement(){this._element.remove(),x.trigger(this._element,rd),this.dispose()}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e](this)}})}};or(Ji,"close");Je(Ji);var ad="button",ld="bs.button",cd=`.${ld}`,ud=".data-api",dd="active",Vs='[data-bs-toggle="button"]',fd=`click${cd}${ud}`,Zi=class n extends We{static get NAME(){return ad}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle(dd))}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this);e==="toggle"&&t[e]()})}};x.on(document,fd,Vs,n=>{n.preventDefault();let e=n.target.closest(Vs);Zi.getOrCreateInstance(e).toggle()});Je(Zi);var hd="swipe",Dn=".bs.swipe",pd=`touchstart${Dn}`,md=`touchmove${Dn}`,gd=`touchend${Dn}`,vd=`pointerdown${Dn}`,Ed=`pointerup${Dn}`,yd="touch",bd="pen",_d="pointer-event",wd=40,xd={endCallback:null,leftCallback:null,rightCallback:null},Td={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"},er=class n extends Xt{constructor(e,t){super(),this._element=e,!(!e||!n.isSupported())&&(this._config=this._getConfig(t),this._deltaX=0,this._supportPointerEvents=!!window.PointerEvent,this._initEvents())}static get Default(){return xd}static get DefaultType(){return Td}static get NAME(){return hd}dispose(){x.off(this._element,Dn)}_start(e){if(!this._supportPointerEvents){this._deltaX=e.touches[0].clientX;return}this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX)}_end(e){this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX-this._deltaX),this._handleSwipe(),Pe(this._config.endCallback)}_move(e){this._deltaX=e.touches&&e.touches.length>1?0:e.touches[0].clientX-this._deltaX}_handleSwipe(){let e=Math.abs(this._deltaX);if(e<=wd)return;let t=e/this._deltaX;this._deltaX=0,t&&Pe(t>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(x.on(this._element,vd,e=>this._start(e)),x.on(this._element,Ed,e=>this._end(e)),this._element.classList.add(_d)):(x.on(this._element,pd,e=>this._start(e)),x.on(this._element,md,e=>this._move(e)),x.on(this._element,gd,e=>this._end(e)))}_eventIsPointerPenTouch(e){return this._supportPointerEvents&&(e.pointerType===bd||e.pointerType===yd)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}},Cd="carousel",Sd="bs.carousel",Dt=`.${Sd}`,Ta=".data-api",Ad="ArrowLeft",Dd="ArrowRight",Od=500,ei="next",_n="prev",xn="left",Ki="right",Ld=`slide${Dt}`,go=`slid${Dt}`,Md=`keydown${Dt}`,Nd=`mouseenter${Dt}`,kd=`mouseleave${Dt}`,Hd=`dragstart${Dt}`,Rd=`load${Dt}${Ta}`,Id=`click${Dt}${Ta}`,Ca="carousel",ji="active",Pd="slide",Fd="carousel-item-end",$d="carousel-item-start",Bd="carousel-item-next",Vd="carousel-item-prev",Sa=".active",Aa=".carousel-item",zd=Sa+Aa,jd=".carousel-item img",qd=".carousel-indicators",Wd="[data-bs-slide], [data-bs-slide-to]",Ud='[data-bs-ride="carousel"]',Yd={[Ad]:Ki,[Dd]:xn},Gd={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Kd={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"},ni=class n extends We{constructor(e,t){super(e,t),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=V.findOne(qd,this._element),this._addEventListeners(),this._config.ride===Ca&&this.cycle()}static get Default(){return Gd}static get DefaultType(){return Kd}static get NAME(){return Cd}next(){this._slide(ei)}nextWhenVisible(){!document.hidden&&An(this._element)&&this.next()}prev(){this._slide(_n)}pause(){this._isSliding&&ha(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval(()=>this.nextWhenVisible(),this._config.interval)}_maybeEnableCycle(){if(this._config.ride){if(this._isSliding){x.one(this._element,go,()=>this.cycle());return}this.cycle()}}to(e){let t=this._getItems();if(e>t.length-1||e<0)return;if(this._isSliding){x.one(this._element,go,()=>this.to(e));return}let i=this._getItemIndex(this._getActive());if(i===e)return;let r=e>i?ei:_n;this._slide(r,t[e])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(e){return e.defaultInterval=e.interval,e}_addEventListeners(){this._config.keyboard&&x.on(this._element,Md,e=>this._keydown(e)),this._config.pause==="hover"&&(x.on(this._element,Nd,()=>this.pause()),x.on(this._element,kd,()=>this._maybeEnableCycle())),this._config.touch&&er.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(let i of V.find(jd,this._element))x.on(i,Hd,r=>r.preventDefault());let t={leftCallback:()=>this._slide(this._directionToOrder(xn)),rightCallback:()=>this._slide(this._directionToOrder(Ki)),endCallback:()=>{this._config.pause==="hover"&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(()=>this._maybeEnableCycle(),Od+this._config.interval))}};this._swipeHelper=new er(this._element,t)}_keydown(e){if(/input|textarea/i.test(e.target.tagName))return;let t=Yd[e.key];t&&(e.preventDefault(),this._slide(this._directionToOrder(t)))}_getItemIndex(e){return this._getItems().indexOf(e)}_setActiveIndicatorElement(e){if(!this._indicatorsElement)return;let t=V.findOne(Sa,this._indicatorsElement);t.classList.remove(ji),t.removeAttribute("aria-current");let i=V.findOne(`[data-bs-slide-to="${e}"]`,this._indicatorsElement);i&&(i.classList.add(ji),i.setAttribute("aria-current","true"))}_updateInterval(){let e=this._activeElement||this._getActive();if(!e)return;let t=Number.parseInt(e.getAttribute("data-bs-interval"),10);this._config.interval=t||this._config.defaultInterval}_slide(e,t=null){if(this._isSliding)return;let i=this._getActive(),r=e===ei,o=t||Mo(this._getItems(),i,r,this._config.wrap);if(o===i)return;let s=this._getItemIndex(o),a=E=>x.trigger(this._element,E,{relatedTarget:o,direction:this._orderToDirection(e),from:this._getItemIndex(i),to:s});if(a(Ld).defaultPrevented||!i||!o)return;let c=!!this._interval;this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(s),this._activeElement=o;let u=r?$d:Fd,d=r?Bd:Vd;o.classList.add(d),ri(o),i.classList.add(u),o.classList.add(u);let p=()=>{o.classList.remove(u,d),o.classList.add(ji),i.classList.remove(ji,d,u),this._isSliding=!1,a(go)};this._queueCallback(p,i,this._isAnimated()),c&&this.cycle()}_isAnimated(){return this._element.classList.contains(Pd)}_getActive(){return V.findOne(zd,this._element)}_getItems(){return V.find(Aa,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(e){return Xe()?e===xn?_n:ei:e===xn?ei:_n}_orderToDirection(e){return Xe()?e===_n?xn:Ki:e===_n?Ki:xn}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="number"){t.to(e);return}if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()}})}};x.on(document,Id,Wd,function(n){let e=V.getElementFromSelector(this);if(!e||!e.classList.contains(Ca))return;n.preventDefault();let t=ni.getOrCreateInstance(e),i=this.getAttribute("data-bs-slide-to");if(i){t.to(i),t._maybeEnableCycle();return}if(ft.getDataAttribute(this,"slide")==="next"){t.next(),t._maybeEnableCycle();return}t.prev(),t._maybeEnableCycle()});x.on(window,Rd,()=>{let n=V.find(Ud);for(let e of n)ni.getOrCreateInstance(e)});Je(ni);var Xd="collapse",Qd="bs.collapse",oi=`.${Qd}`,Jd=".data-api",Zd=`show${oi}`,ef=`shown${oi}`,tf=`hide${oi}`,nf=`hidden${oi}`,rf=`click${oi}${Jd}`,vo="show",Cn="collapse",qi="collapsing",of="collapsed",sf=`:scope .${Cn} .${Cn}`,af="collapse-horizontal",lf="width",cf="height",uf=".collapse.show, .collapse.collapsing",Oo='[data-bs-toggle="collapse"]',df={parent:null,toggle:!0},ff={parent:"(null|element)",toggle:"boolean"},Ct=class n extends We{constructor(e,t){super(e,t),this._isTransitioning=!1,this._triggerArray=[];let i=V.find(Oo);for(let r of i){let o=V.getSelectorFromElement(r),s=V.find(o).filter(a=>a===this._element);o!==null&&s.length&&this._triggerArray.push(r)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return df}static get DefaultType(){return ff}static get NAME(){return Xd}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let e=[];if(this._config.parent&&(e=this._getFirstLevelChildren(uf).filter(a=>a!==this._element).map(a=>n.getOrCreateInstance(a,{toggle:!1}))),e.length&&e[0]._isTransitioning||x.trigger(this._element,Zd).defaultPrevented)return;for(let a of e)a.hide();let i=this._getDimension();this._element.classList.remove(Cn),this._element.classList.add(qi),this._element.style[i]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;let r=()=>{this._isTransitioning=!1,this._element.classList.remove(qi),this._element.classList.add(Cn,vo),this._element.style[i]="",x.trigger(this._element,ef)},s=`scroll${i[0].toUpperCase()+i.slice(1)}`;this._queueCallback(r,this._element,!0),this._element.style[i]=`${this._element[s]}px`}hide(){if(this._isTransitioning||!this._isShown()||x.trigger(this._element,tf).defaultPrevented)return;let t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,ri(this._element),this._element.classList.add(qi),this._element.classList.remove(Cn,vo);for(let r of this._triggerArray){let o=V.getElementFromSelector(r);o&&!this._isShown(o)&&this._addAriaAndCollapsedClass([r],!1)}this._isTransitioning=!0;let i=()=>{this._isTransitioning=!1,this._element.classList.remove(qi),this._element.classList.add(Cn),x.trigger(this._element,nf)};this._element.style[t]="",this._queueCallback(i,this._element,!0)}_isShown(e=this._element){return e.classList.contains(vo)}_configAfterMerge(e){return e.toggle=!!e.toggle,e.parent=xt(e.parent),e}_getDimension(){return this._element.classList.contains(af)?lf:cf}_initializeChildren(){if(!this._config.parent)return;let e=this._getFirstLevelChildren(Oo);for(let t of e){let i=V.getElementFromSelector(t);i&&this._addAriaAndCollapsedClass([t],this._isShown(i))}}_getFirstLevelChildren(e){let t=V.find(sf,this._config.parent);return V.find(e,this._config.parent).filter(i=>!t.includes(i))}_addAriaAndCollapsedClass(e,t){if(e.length)for(let i of e)i.classList.toggle(of,!t),i.setAttribute("aria-expanded",t)}static jQueryInterface(e){let t={};return typeof e=="string"&&/show|hide/.test(e)&&(t.toggle=!1),this.each(function(){let i=n.getOrCreateInstance(this,t);if(typeof e=="string"){if(typeof i[e]=="undefined")throw new TypeError(`No method named "${e}"`);i[e]()}})}};x.on(document,rf,Oo,function(n){(n.target.tagName==="A"||n.delegateTarget&&n.delegateTarget.tagName==="A")&&n.preventDefault();for(let e of V.getMultipleElementsFromSelector(this))Ct.getOrCreateInstance(e,{toggle:!1}).toggle()});Je(Ct);var zs="dropdown",hf="bs.dropdown",Jt=`.${hf}`,ko=".data-api",pf="Escape",js="Tab",mf="ArrowUp",qs="ArrowDown",gf=2,vf=`hide${Jt}`,Ef=`hidden${Jt}`,yf=`show${Jt}`,bf=`shown${Jt}`,Da=`click${Jt}${ko}`,Oa=`keydown${Jt}${ko}`,_f=`keyup${Jt}${ko}`,Tn="show",wf="dropup",xf="dropend",Tf="dropstart",Cf="dropup-center",Sf="dropdown-center",Gt='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',Af=`${Gt}.${Tn}`,Xi=".dropdown-menu",Df=".navbar",Of=".navbar-nav",Lf=".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",Mf=Xe()?"top-end":"top-start",Nf=Xe()?"top-start":"top-end",kf=Xe()?"bottom-end":"bottom-start",Hf=Xe()?"bottom-start":"bottom-end",Rf=Xe()?"left-start":"right-start",If=Xe()?"right-start":"left-start",Pf="top",Ff="bottom",$f={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},Bf={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"},St=class n extends We{constructor(e,t){super(e,t),this._popper=null,this._parent=this._element.parentNode,this._menu=V.next(this._element,Xi)[0]||V.prev(this._element,Xi)[0]||V.findOne(Xi,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return $f}static get DefaultType(){return Bf}static get NAME(){return zs}toggle(){return this._isShown()?this.hide():this.show()}show(){if(Tt(this._element)||this._isShown())return;let e={relatedTarget:this._element};if(!x.trigger(this._element,yf,e).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(Of))for(let i of[].concat(...document.body.children))x.on(i,"mouseover",Qi);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Tn),this._element.classList.add(Tn),x.trigger(this._element,bf,e)}}hide(){if(Tt(this._element)||!this._isShown())return;let e={relatedTarget:this._element};this._completeHide(e)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(e){if(!x.trigger(this._element,vf,e).defaultPrevented){if("ontouchstart"in document.documentElement)for(let i of[].concat(...document.body.children))x.off(i,"mouseover",Qi);this._popper&&this._popper.destroy(),this._menu.classList.remove(Tn),this._element.classList.remove(Tn),this._element.setAttribute("aria-expanded","false"),ft.removeDataAttribute(this._menu,"popper"),x.trigger(this._element,Ef,e)}}_getConfig(e){if(e=super._getConfig(e),typeof e.reference=="object"&&!dt(e.reference)&&typeof e.reference.getBoundingClientRect!="function")throw new TypeError(`${zs.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return e}_createPopper(){if(typeof zi=="undefined")throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org/docs/v2/)");let e=this._element;this._config.reference==="parent"?e=this._parent:dt(this._config.reference)?e=xt(this._config.reference):typeof this._config.reference=="object"&&(e=this._config.reference);let t=this._getPopperConfig();this._popper=Zn(e,this._menu,t)}_isShown(){return this._menu.classList.contains(Tn)}_getPlacement(){let e=this._parent;if(e.classList.contains(xf))return Rf;if(e.classList.contains(Tf))return If;if(e.classList.contains(Cf))return Pf;if(e.classList.contains(Sf))return Ff;let t=getComputedStyle(this._menu).getPropertyValue("--bs-position").trim()==="end";return e.classList.contains(wf)?t?Nf:Mf:t?Hf:kf}_detectNavbar(){return this._element.closest(Df)!==null}_getOffset(){let{offset:e}=this._config;return typeof e=="string"?e.split(",").map(t=>Number.parseInt(t,10)):typeof e=="function"?t=>e(t,this._element):e}_getPopperConfig(){let e={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||this._config.display==="static")&&(ft.setDataAttribute(this._menu,"popper","static"),e.modifiers=[{name:"applyStyles",enabled:!1}]),O(O({},e),Pe(this._config.popperConfig,[void 0,e]))}_selectMenuItem({key:e,target:t}){let i=V.find(Lf,this._menu).filter(r=>An(r));i.length&&Mo(i,t,e===qs,!i.includes(t)).focus()}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()}})}static clearMenus(e){if(e.button===gf||e.type==="keyup"&&e.key!==js)return;let t=V.find(Af);for(let i of t){let r=n.getInstance(i);if(!r||r._config.autoClose===!1)continue;let o=e.composedPath(),s=o.includes(r._menu);if(o.includes(r._element)||r._config.autoClose==="inside"&&!s||r._config.autoClose==="outside"&&s||r._menu.contains(e.target)&&(e.type==="keyup"&&e.key===js||/input|select|option|textarea|form/i.test(e.target.tagName)))continue;let a={relatedTarget:r._element};e.type==="click"&&(a.clickEvent=e),r._completeHide(a)}}static dataApiKeydownHandler(e){let t=/input|textarea/i.test(e.target.tagName),i=e.key===pf,r=[mf,qs].includes(e.key);if(!r&&!i||t&&!i)return;e.preventDefault();let o=this.matches(Gt)?this:V.prev(this,Gt)[0]||V.next(this,Gt)[0]||V.findOne(Gt,e.delegateTarget.parentNode),s=n.getOrCreateInstance(o);if(r){e.stopPropagation(),s.show(),s._selectMenuItem(e);return}s._isShown()&&(e.stopPropagation(),s.hide(),o.focus())}};x.on(document,Oa,Gt,St.dataApiKeydownHandler);x.on(document,Oa,Xi,St.dataApiKeydownHandler);x.on(document,Da,St.clearMenus);x.on(document,_f,St.clearMenus);x.on(document,Da,Gt,function(n){n.preventDefault(),St.getOrCreateInstance(this).toggle()});Je(St);var La="backdrop",Vf="fade",Ws="show",Us=`mousedown.bs.${La}`,zf={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},jf={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"},tr=class extends Xt{constructor(e){super(),this._config=this._getConfig(e),this._isAppended=!1,this._element=null}static get Default(){return zf}static get DefaultType(){return jf}static get NAME(){return La}show(e){if(!this._config.isVisible){Pe(e);return}this._append();let t=this._getElement();this._config.isAnimated&&ri(t),t.classList.add(Ws),this._emulateAnimation(()=>{Pe(e)})}hide(e){if(!this._config.isVisible){Pe(e);return}this._getElement().classList.remove(Ws),this._emulateAnimation(()=>{this.dispose(),Pe(e)})}dispose(){this._isAppended&&(x.off(this._element,Us),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){let e=document.createElement("div");e.className=this._config.className,this._config.isAnimated&&e.classList.add(Vf),this._element=e}return this._element}_configAfterMerge(e){return e.rootElement=xt(e.rootElement),e}_append(){if(this._isAppended)return;let e=this._getElement();this._config.rootElement.append(e),x.on(e,Us,()=>{Pe(this._config.clickCallback)}),this._isAppended=!0}_emulateAnimation(e){ga(e,this._getElement(),this._config.isAnimated)}},qf="focustrap",Wf="bs.focustrap",nr=`.${Wf}`,Uf=`focusin${nr}`,Yf=`keydown.tab${nr}`,Gf="Tab",Kf="forward",Ys="backward",Xf={autofocus:!0,trapElement:null},Qf={autofocus:"boolean",trapElement:"element"},ir=class extends Xt{constructor(e){super(),this._config=this._getConfig(e),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return Xf}static get DefaultType(){return Qf}static get NAME(){return qf}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),x.off(document,nr),x.on(document,Uf,e=>this._handleFocusin(e)),x.on(document,Yf,e=>this._handleKeydown(e)),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,x.off(document,nr))}_handleFocusin(e){let{trapElement:t}=this._config;if(e.target===document||e.target===t||t.contains(e.target))return;let i=V.focusableChildren(t);i.length===0?t.focus():this._lastTabNavDirection===Ys?i[i.length-1].focus():i[0].focus()}_handleKeydown(e){e.key===Gf&&(this._lastTabNavDirection=e.shiftKey?Ys:Kf)}},Gs=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",Ks=".sticky-top",Wi="padding-right",Xs="margin-right",ii=class{constructor(){this._element=document.body}getWidth(){let e=document.documentElement.clientWidth;return Math.abs(window.innerWidth-e)}hide(){let e=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,Wi,t=>t+e),this._setElementAttributes(Gs,Wi,t=>t+e),this._setElementAttributes(Ks,Xs,t=>t-e)}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,Wi),this._resetElementAttributes(Gs,Wi),this._resetElementAttributes(Ks,Xs)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(e,t,i){let r=this.getWidth(),o=s=>{if(s!==this._element&&window.innerWidth>s.clientWidth+r)return;this._saveInitialAttribute(s,t);let a=window.getComputedStyle(s).getPropertyValue(t);s.style.setProperty(t,`${i(Number.parseFloat(a))}px`)};this._applyManipulationCallback(e,o)}_saveInitialAttribute(e,t){let i=e.style.getPropertyValue(t);i&&ft.setDataAttribute(e,t,i)}_resetElementAttributes(e,t){let i=r=>{let o=ft.getDataAttribute(r,t);if(o===null){r.style.removeProperty(t);return}ft.removeDataAttribute(r,t),r.style.setProperty(t,o)};this._applyManipulationCallback(e,i)}_applyManipulationCallback(e,t){if(dt(e)){t(e);return}for(let i of V.find(e,this._element))t(i)}},Jf="modal",Zf="bs.modal",Qe=`.${Zf}`,eh=".data-api",th="Escape",nh=`hide${Qe}`,ih=`hidePrevented${Qe}`,Ma=`hidden${Qe}`,Na=`show${Qe}`,rh=`shown${Qe}`,oh=`resize${Qe}`,sh=`click.dismiss${Qe}`,ah=`mousedown.dismiss${Qe}`,lh=`keydown.dismiss${Qe}`,ch=`click${Qe}${eh}`,Qs="modal-open",uh="fade",Js="show",Eo="modal-static",dh=".modal.show",fh=".modal-dialog",hh=".modal-body",ph='[data-bs-toggle="modal"]',mh={backdrop:!0,focus:!0,keyboard:!0},gh={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"},tt=class n extends We{constructor(e,t){super(e,t),this._dialog=V.findOne(fh,this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new ii,this._addEventListeners()}static get Default(){return mh}static get DefaultType(){return gh}static get NAME(){return Jf}toggle(e){return this._isShown?this.hide():this.show(e)}show(e){this._isShown||this._isTransitioning||x.trigger(this._element,Na,{relatedTarget:e}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Qs),this._adjustDialog(),this._backdrop.show(()=>this._showElement(e)))}hide(){!this._isShown||this._isTransitioning||x.trigger(this._element,nh).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Js),this._queueCallback(()=>this._hideModal(),this._element,this._isAnimated()))}dispose(){x.off(window,Qe),x.off(this._dialog,Qe),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new tr({isVisible:!!this._config.backdrop,isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new ir({trapElement:this._element})}_showElement(e){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;let t=V.findOne(hh,this._dialog);t&&(t.scrollTop=0),ri(this._element),this._element.classList.add(Js);let i=()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,x.trigger(this._element,rh,{relatedTarget:e})};this._queueCallback(i,this._dialog,this._isAnimated())}_addEventListeners(){x.on(this._element,lh,e=>{if(e.key===th){if(this._config.keyboard){this.hide();return}this._triggerBackdropTransition()}}),x.on(window,oh,()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()}),x.on(this._element,ah,e=>{x.one(this._element,sh,t=>{if(!(this._element!==e.target||this._element!==t.target)){if(this._config.backdrop==="static"){this._triggerBackdropTransition();return}this._config.backdrop&&this.hide()}})})}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide(()=>{document.body.classList.remove(Qs),this._resetAdjustments(),this._scrollBar.reset(),x.trigger(this._element,Ma)})}_isAnimated(){return this._element.classList.contains(uh)}_triggerBackdropTransition(){if(x.trigger(this._element,ih).defaultPrevented)return;let t=this._element.scrollHeight>document.documentElement.clientHeight,i=this._element.style.overflowY;i==="hidden"||this._element.classList.contains(Eo)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(Eo),this._queueCallback(()=>{this._element.classList.remove(Eo),this._queueCallback(()=>{this._element.style.overflowY=i},this._dialog)},this._dialog),this._element.focus())}_adjustDialog(){let e=this._element.scrollHeight>document.documentElement.clientHeight,t=this._scrollBar.getWidth(),i=t>0;if(i&&!e){let r=Xe()?"paddingLeft":"paddingRight";this._element.style[r]=`${t}px`}if(!i&&e){let r=Xe()?"paddingRight":"paddingLeft";this._element.style[r]=`${t}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(e,t){return this.each(function(){let i=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof i[e]=="undefined")throw new TypeError(`No method named "${e}"`);i[e](t)}})}};x.on(document,ch,ph,function(n){let e=V.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&n.preventDefault(),x.one(e,Na,r=>{r.defaultPrevented||x.one(e,Ma,()=>{An(this)&&this.focus()})});let t=V.findOne(dh);t&&tt.getInstance(t).hide(),tt.getOrCreateInstance(e).toggle(this)});or(tt);Je(tt);var vh="offcanvas",Eh="bs.offcanvas",mt=`.${Eh}`,ka=".data-api",yh=`load${mt}${ka}`,bh="Escape",Zs="show",ea="showing",ta="hiding",_h="offcanvas-backdrop",Ha=".offcanvas.show",wh=`show${mt}`,xh=`shown${mt}`,Th=`hide${mt}`,na=`hidePrevented${mt}`,Ra=`hidden${mt}`,Ch=`resize${mt}`,Sh=`click${mt}${ka}`,Ah=`keydown.dismiss${mt}`,Dh='[data-bs-toggle="offcanvas"]',Oh={backdrop:!0,keyboard:!0,scroll:!1},Lh={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"},At=class n extends We{constructor(e,t){super(e,t),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Oh}static get DefaultType(){return Lh}static get NAME(){return vh}toggle(e){return this._isShown?this.hide():this.show(e)}show(e){if(this._isShown||x.trigger(this._element,wh,{relatedTarget:e}).defaultPrevented)return;this._isShown=!0,this._backdrop.show(),this._config.scroll||new ii().hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(ea);let i=()=>{(!this._config.scroll||this._config.backdrop)&&this._focustrap.activate(),this._element.classList.add(Zs),this._element.classList.remove(ea),x.trigger(this._element,xh,{relatedTarget:e})};this._queueCallback(i,this._element,!0)}hide(){if(!this._isShown||x.trigger(this._element,Th).defaultPrevented)return;this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(ta),this._backdrop.hide();let t=()=>{this._element.classList.remove(Zs,ta),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||new ii().reset(),x.trigger(this._element,Ra)};this._queueCallback(t,this._element,!0)}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){let e=()=>{if(this._config.backdrop==="static"){x.trigger(this._element,na);return}this.hide()},t=!!this._config.backdrop;return new tr({className:_h,isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?e:null})}_initializeFocusTrap(){return new ir({trapElement:this._element})}_addEventListeners(){x.on(this._element,Ah,e=>{if(e.key===bh){if(this._config.keyboard){this.hide();return}x.trigger(this._element,na)}})}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e](this)}})}};x.on(document,Sh,Dh,function(n){let e=V.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&n.preventDefault(),Tt(this))return;x.one(e,Ra,()=>{An(this)&&this.focus()});let t=V.findOne(Ha);t&&t!==e&&At.getInstance(t).hide(),At.getOrCreateInstance(e).toggle(this)});x.on(window,yh,()=>{for(let n of V.find(Ha))At.getOrCreateInstance(n).show()});x.on(window,Ch,()=>{for(let n of V.find("[aria-modal][class*=show][class*=offcanvas-]"))getComputedStyle(n).position!=="fixed"&&At.getOrCreateInstance(n).hide()});or(At);Je(At);var Mh=/^aria-[\w-]*$/i,Ia={"*":["class","dir","id","lang","role",Mh],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Nh=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),kh=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Hh=(n,e)=>{let t=n.nodeName.toLowerCase();return e.includes(t)?Nh.has(t)?!!kh.test(n.nodeValue):!0:e.filter(i=>i instanceof RegExp).some(i=>i.test(t))};function Rh(n,e,t){if(!n.length)return n;if(t&&typeof t=="function")return t(n);let r=new window.DOMParser().parseFromString(n,"text/html"),o=[].concat(...r.body.querySelectorAll("*"));for(let s of o){let a=s.nodeName.toLowerCase();if(!Object.keys(e).includes(a)){s.remove();continue}let l=[].concat(...s.attributes),c=[].concat(e["*"]||[],e[a]||[]);for(let u of l)Hh(u,c)||s.removeAttribute(u.nodeName)}return r.body.innerHTML}var Ih="TemplateFactory",Ph={allowList:Ia,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Fh={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},$h={entry:"(string|element|function|null)",selector:"(string|element)"},Lo=class extends Xt{constructor(e){super(),this._config=this._getConfig(e)}static get Default(){return Ph}static get DefaultType(){return Fh}static get NAME(){return Ih}getContent(){return Object.values(this._config.content).map(e=>this._resolvePossibleFunction(e)).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(e){return this._checkContent(e),this._config.content=O(O({},this._config.content),e),this}toHtml(){let e=document.createElement("div");e.innerHTML=this._maybeSanitize(this._config.template);for(let[r,o]of Object.entries(this._config.content))this._setContent(e,o,r);let t=e.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&t.classList.add(...i.split(" ")),t}_typeCheckConfig(e){super._typeCheckConfig(e),this._checkContent(e.content)}_checkContent(e){for(let[t,i]of Object.entries(e))super._typeCheckConfig({selector:t,entry:i},$h)}_setContent(e,t,i){let r=V.findOne(i,e);if(r){if(t=this._resolvePossibleFunction(t),!t){r.remove();return}if(dt(t)){this._putElementInTemplate(xt(t),r);return}if(this._config.html){r.innerHTML=this._maybeSanitize(t);return}r.textContent=t}}_maybeSanitize(e){return this._config.sanitize?Rh(e,this._config.allowList,this._config.sanitizeFn):e}_resolvePossibleFunction(e){return Pe(e,[void 0,this])}_putElementInTemplate(e,t){if(this._config.html){t.innerHTML="",t.append(e);return}t.textContent=e.textContent}},Bh="tooltip",Vh=new Set(["sanitize","allowList","sanitizeFn"]),yo="fade",zh="modal",Ui="show",jh=".tooltip-inner",ia=`.${zh}`,ra="hide.bs.modal",ti="hover",bo="focus",_o="click",qh="manual",Wh="hide",Uh="hidden",Yh="show",Gh="shown",Kh="inserted",Xh="click",Qh="focusin",Jh="focusout",Zh="mouseenter",ep="mouseleave",tp={AUTO:"auto",TOP:"top",RIGHT:Xe()?"left":"right",BOTTOM:"bottom",LEFT:Xe()?"right":"left"},np={allowList:Ia,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ip={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"},ht=class n extends We{constructor(e,t){if(typeof zi=="undefined")throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org/docs/v2/)");super(e,t),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return np}static get DefaultType(){return ip}static get NAME(){return Bh}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){if(this._isEnabled){if(this._isShown()){this._leave();return}this._enter()}}dispose(){clearTimeout(this._timeout),x.off(this._element.closest(ia),ra,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if(this._element.style.display==="none")throw new Error("Please use show on visible elements");if(!(this._isWithContent()&&this._isEnabled))return;let e=x.trigger(this._element,this.constructor.eventName(Yh)),i=(pa(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(e.defaultPrevented||!i)return;this._disposePopper();let r=this._getTipElement();this._element.setAttribute("aria-describedby",r.getAttribute("id"));let{container:o}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(o.append(r),x.trigger(this._element,this.constructor.eventName(Kh))),this._popper=this._createPopper(r),r.classList.add(Ui),"ontouchstart"in document.documentElement)for(let a of[].concat(...document.body.children))x.on(a,"mouseover",Qi);let s=()=>{x.trigger(this._element,this.constructor.eventName(Gh)),this._isHovered===!1&&this._leave(),this._isHovered=!1};this._queueCallback(s,this.tip,this._isAnimated())}hide(){if(!this._isShown()||x.trigger(this._element,this.constructor.eventName(Wh)).defaultPrevented)return;if(this._getTipElement().classList.remove(Ui),"ontouchstart"in document.documentElement)for(let r of[].concat(...document.body.children))x.off(r,"mouseover",Qi);this._activeTrigger[_o]=!1,this._activeTrigger[bo]=!1,this._activeTrigger[ti]=!1,this._isHovered=null;let i=()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),x.trigger(this._element,this.constructor.eventName(Uh)))};this._queueCallback(i,this.tip,this._isAnimated())}update(){this._popper&&this._popper.update()}_isWithContent(){return!!this._getTitle()}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(e){let t=this._getTemplateFactory(e).toHtml();if(!t)return null;t.classList.remove(yo,Ui),t.classList.add(`bs-${this.constructor.NAME}-auto`);let i=qu(this.constructor.NAME).toString();return t.setAttribute("id",i),this._isAnimated()&&t.classList.add(yo),t}setContent(e){this._newContent=e,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(e){return this._templateFactory?this._templateFactory.changeContent(e):this._templateFactory=new Lo(ae(O({},this._config),{content:e,extraClass:this._resolvePossibleFunction(this._config.customClass)})),this._templateFactory}_getContentForTemplate(){return{[jh]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(e){return this.constructor.getOrCreateInstance(e.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(yo)}_isShown(){return this.tip&&this.tip.classList.contains(Ui)}_createPopper(e){let t=Pe(this._config.placement,[this,e,this._element]),i=tp[t.toUpperCase()];return Zn(this._element,e,this._getPopperConfig(i))}_getOffset(){let{offset:e}=this._config;return typeof e=="string"?e.split(",").map(t=>Number.parseInt(t,10)):typeof e=="function"?t=>e(t,this._element):e}_resolvePossibleFunction(e){return Pe(e,[this._element,this._element])}_getPopperConfig(e){let t={placement:e,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:i=>{this._getTipElement().setAttribute("data-popper-placement",i.state.placement)}}]};return O(O({},t),Pe(this._config.popperConfig,[void 0,t]))}_setListeners(){let e=this._config.trigger.split(" ");for(let t of e)if(t==="click")x.on(this._element,this.constructor.eventName(Xh),this._config.selector,i=>{let r=this._initializeOnDelegatedTarget(i);r._activeTrigger[_o]=!(r._isShown()&&r._activeTrigger[_o]),r.toggle()});else if(t!==qh){let i=t===ti?this.constructor.eventName(Zh):this.constructor.eventName(Qh),r=t===ti?this.constructor.eventName(ep):this.constructor.eventName(Jh);x.on(this._element,i,this._config.selector,o=>{let s=this._initializeOnDelegatedTarget(o);s._activeTrigger[o.type==="focusin"?bo:ti]=!0,s._enter()}),x.on(this._element,r,this._config.selector,o=>{let s=this._initializeOnDelegatedTarget(o);s._activeTrigger[o.type==="focusout"?bo:ti]=s._element.contains(o.relatedTarget),s._leave()})}this._hideModalHandler=()=>{this._element&&this.hide()},x.on(this._element.closest(ia),ra,this._hideModalHandler)}_fixTitle(){let e=this._element.getAttribute("title");e&&(!this._element.getAttribute("aria-label")&&!this._element.textContent.trim()&&this._element.setAttribute("aria-label",e),this._element.setAttribute("data-bs-original-title",e),this._element.removeAttribute("title"))}_enter(){if(this._isShown()||this._isHovered){this._isHovered=!0;return}this._isHovered=!0,this._setTimeout(()=>{this._isHovered&&this.show()},this._config.delay.show)}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout(()=>{this._isHovered||this.hide()},this._config.delay.hide))}_setTimeout(e,t){clearTimeout(this._timeout),this._timeout=setTimeout(e,t)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(e){let t=ft.getDataAttributes(this._element);for(let i of Object.keys(t))Vh.has(i)&&delete t[i];return e=O(O({},t),typeof e=="object"&&e?e:{}),e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e.container=e.container===!1?document.body:xt(e.container),typeof e.delay=="number"&&(e.delay={show:e.delay,hide:e.delay}),typeof e.title=="number"&&(e.title=e.title.toString()),typeof e.content=="number"&&(e.content=e.content.toString()),e}_getDelegateConfig(){let e={};for(let[t,i]of Object.entries(this._config))this.constructor.Default[t]!==i&&(e[t]=i);return e.selector=!1,e.trigger="manual",e}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()}})}};Je(ht);var rp="popover",op=".popover-header",sp=".popover-body",ap=ae(O({},ht.Default),{content:"",offset:[0,8],placement:"right",template:'',trigger:"click"}),lp=ae(O({},ht.DefaultType),{content:"(null|string|element|function)"}),Sn=class n extends ht{static get Default(){return ap}static get DefaultType(){return lp}static get NAME(){return rp}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[op]:this._getTitle(),[sp]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()}})}};Je(Sn);var cp="scrollspy",up="bs.scrollspy",Ho=`.${up}`,dp=".data-api",fp=`activate${Ho}`,oa=`click${Ho}`,hp=`load${Ho}${dp}`,pp="dropdown-item",wn="active",mp='[data-bs-spy="scroll"]',wo="[href]",gp=".nav, .list-group",sa=".nav-link",vp=".nav-item",Ep=".list-group-item",yp=`${sa}, ${vp} > ${sa}, ${Ep}`,bp=".dropdown",_p=".dropdown-toggle",wp={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},xp={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"},rr=class n extends We{constructor(e,t){super(e,t),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement=getComputedStyle(this._element).overflowY==="visible"?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return wp}static get DefaultType(){return xp}static get NAME(){return cp}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(let e of this._observableSections.values())this._observer.observe(e)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(e){return e.target=xt(e.target)||document.body,e.rootMargin=e.offset?`${e.offset}px 0px -30%`:e.rootMargin,typeof e.threshold=="string"&&(e.threshold=e.threshold.split(",").map(t=>Number.parseFloat(t))),e}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(x.off(this._config.target,oa),x.on(this._config.target,oa,wo,e=>{let t=this._observableSections.get(e.target.hash);if(t){e.preventDefault();let i=this._rootElement||window,r=t.offsetTop-this._element.offsetTop;if(i.scrollTo){i.scrollTo({top:r,behavior:"smooth"});return}i.scrollTop=r}}))}_getNewObserver(){let e={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver(t=>this._observerCallback(t),e)}_observerCallback(e){let t=s=>this._targetLinks.get(`#${s.target.id}`),i=s=>{this._previousScrollData.visibleEntryTop=s.target.offsetTop,this._process(t(s))},r=(this._rootElement||document.documentElement).scrollTop,o=r>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=r;for(let s of e){if(!s.isIntersecting){this._activeTarget=null,this._clearActiveClass(t(s));continue}let a=s.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(o&&a){if(i(s),!r)return;continue}!o&&!a&&i(s)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;let e=V.find(wo,this._config.target);for(let t of e){if(!t.hash||Tt(t))continue;let i=V.findOne(decodeURI(t.hash),this._element);An(i)&&(this._targetLinks.set(decodeURI(t.hash),t),this._observableSections.set(t.hash,i))}}_process(e){this._activeTarget!==e&&(this._clearActiveClass(this._config.target),this._activeTarget=e,e.classList.add(wn),this._activateParents(e),x.trigger(this._element,fp,{relatedTarget:e}))}_activateParents(e){if(e.classList.contains(pp)){V.findOne(_p,e.closest(bp)).classList.add(wn);return}for(let t of V.parents(e,gp))for(let i of V.prev(t,yp))i.classList.add(wn)}_clearActiveClass(e){e.classList.remove(wn);let t=V.find(`${wo}.${wn}`,e);for(let i of t)i.classList.remove(wn)}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()}})}};x.on(window,hp,()=>{for(let n of V.find(mp))rr.getOrCreateInstance(n)});Je(rr);var Tp="tab",Cp="bs.tab",Zt=`.${Cp}`,Sp=`hide${Zt}`,Ap=`hidden${Zt}`,Dp=`show${Zt}`,Op=`shown${Zt}`,Lp=`click${Zt}`,Mp=`keydown${Zt}`,Np=`load${Zt}`,kp="ArrowLeft",aa="ArrowRight",Hp="ArrowUp",la="ArrowDown",xo="Home",ca="End",Kt="active",ua="fade",To="show",Rp="dropdown",Pa=".dropdown-toggle",Ip=".dropdown-menu",Co=`:not(${Pa})`,Pp='.list-group, .nav, [role="tablist"]',Fp=".nav-item, .list-group-item",$p=`.nav-link${Co}, .list-group-item${Co}, [role="tab"]${Co}`,Fa='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',So=`${$p}, ${Fa}`,Bp=`.${Kt}[data-bs-toggle="tab"], .${Kt}[data-bs-toggle="pill"], .${Kt}[data-bs-toggle="list"]`,Qt=class n extends We{constructor(e){super(e),this._parent=this._element.closest(Pp),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),x.on(this._element,Mp,t=>this._keydown(t)))}static get NAME(){return Tp}show(){let e=this._element;if(this._elemIsActive(e))return;let t=this._getActiveElem(),i=t?x.trigger(t,Sp,{relatedTarget:e}):null;x.trigger(e,Dp,{relatedTarget:t}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(t,e),this._activate(e,t))}_activate(e,t){if(!e)return;e.classList.add(Kt),this._activate(V.getElementFromSelector(e));let i=()=>{if(e.getAttribute("role")!=="tab"){e.classList.add(To);return}e.removeAttribute("tabindex"),e.setAttribute("aria-selected",!0),this._toggleDropDown(e,!0),x.trigger(e,Op,{relatedTarget:t})};this._queueCallback(i,e,e.classList.contains(ua))}_deactivate(e,t){if(!e)return;e.classList.remove(Kt),e.blur(),this._deactivate(V.getElementFromSelector(e));let i=()=>{if(e.getAttribute("role")!=="tab"){e.classList.remove(To);return}e.setAttribute("aria-selected",!1),e.setAttribute("tabindex","-1"),this._toggleDropDown(e,!1),x.trigger(e,Ap,{relatedTarget:t})};this._queueCallback(i,e,e.classList.contains(ua))}_keydown(e){if(![kp,aa,Hp,la,xo,ca].includes(e.key))return;e.stopPropagation(),e.preventDefault();let t=this._getChildren().filter(r=>!Tt(r)),i;if([xo,ca].includes(e.key))i=t[e.key===xo?0:t.length-1];else{let r=[aa,la].includes(e.key);i=Mo(t,e.target,r,!0)}i&&(i.focus({preventScroll:!0}),n.getOrCreateInstance(i).show())}_getChildren(){return V.find(So,this._parent)}_getActiveElem(){return this._getChildren().find(e=>this._elemIsActive(e))||null}_setInitialAttributes(e,t){this._setAttributeIfNotExists(e,"role","tablist");for(let i of t)this._setInitialAttributesOnChild(i)}_setInitialAttributesOnChild(e){e=this._getInnerElement(e);let t=this._elemIsActive(e),i=this._getOuterElement(e);e.setAttribute("aria-selected",t),i!==e&&this._setAttributeIfNotExists(i,"role","presentation"),t||e.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(e,"role","tab"),this._setInitialAttributesOnTargetPanel(e)}_setInitialAttributesOnTargetPanel(e){let t=V.getElementFromSelector(e);t&&(this._setAttributeIfNotExists(t,"role","tabpanel"),e.id&&this._setAttributeIfNotExists(t,"aria-labelledby",`${e.id}`))}_toggleDropDown(e,t){let i=this._getOuterElement(e);if(!i.classList.contains(Rp))return;let r=(o,s)=>{let a=V.findOne(o,i);a&&a.classList.toggle(s,t)};r(Pa,Kt),r(Ip,To),i.setAttribute("aria-expanded",t)}_setAttributeIfNotExists(e,t,i){e.hasAttribute(t)||e.setAttribute(t,i)}_elemIsActive(e){return e.classList.contains(Kt)}_getInnerElement(e){return e.matches(So)?e:V.findOne(So,e)}_getOuterElement(e){return e.closest(Fp)||e}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()}})}};x.on(document,Lp,Fa,function(n){["A","AREA"].includes(this.tagName)&&n.preventDefault(),!Tt(this)&&Qt.getOrCreateInstance(this).show()});x.on(window,Np,()=>{for(let n of V.find(Bp))Qt.getOrCreateInstance(n)});Je(Qt);var Vp="toast",zp="bs.toast",Ot=`.${zp}`,jp=`mouseover${Ot}`,qp=`mouseout${Ot}`,Wp=`focusin${Ot}`,Up=`focusout${Ot}`,Yp=`hide${Ot}`,Gp=`hidden${Ot}`,Kp=`show${Ot}`,Xp=`shown${Ot}`,Qp="fade",da="hide",Yi="show",Gi="showing",Jp={animation:"boolean",autohide:"boolean",delay:"number"},Zp={animation:!0,autohide:!0,delay:5e3},pt=class n extends We{constructor(e,t){super(e,t),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return Zp}static get DefaultType(){return Jp}static get NAME(){return Vp}show(){if(x.trigger(this._element,Kp).defaultPrevented)return;this._clearTimeout(),this._config.animation&&this._element.classList.add(Qp);let t=()=>{this._element.classList.remove(Gi),x.trigger(this._element,Xp),this._maybeScheduleHide()};this._element.classList.remove(da),ri(this._element),this._element.classList.add(Yi,Gi),this._queueCallback(t,this._element,this._config.animation)}hide(){if(!this.isShown()||x.trigger(this._element,Yp).defaultPrevented)return;let t=()=>{this._element.classList.add(da),this._element.classList.remove(Gi,Yi),x.trigger(this._element,Gp)};this._element.classList.add(Gi),this._queueCallback(t,this._element,this._config.animation)}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(Yi),super.dispose()}isShown(){return this._element.classList.contains(Yi)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout(()=>{this.hide()},this._config.delay)))}_onInteraction(e,t){switch(e.type){case"mouseover":case"mouseout":{this._hasMouseInteraction=t;break}case"focusin":case"focusout":{this._hasKeyboardInteraction=t;break}}if(t){this._clearTimeout();return}let i=e.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){x.on(this._element,jp,e=>this._onInteraction(e,!0)),x.on(this._element,qp,e=>this._onInteraction(e,!1)),x.on(this._element,Wp,e=>this._onInteraction(e,!0)),x.on(this._element,Up,e=>this._onInteraction(e,!1))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e](this)}})}};or(pt);Je(pt);var em=(function(){"use strict";let htmx={onLoad:null,process:null,on:null,off:null,trigger:null,ajax:null,find:null,findAll:null,closest:null,values:function(n,e){return getInputValues(n,e||"post").values},remove:null,addClass:null,removeClass:null,toggleClass:null,takeClass:null,swap:null,defineExtension:null,removeExtension:null,logAll:null,logNone:null,logger:null,config:{historyEnabled:!0,historyCacheSize:10,refreshOnHistoryMiss:!1,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:!0,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:!0,allowScriptTags:!0,inlineScriptNonce:"",inlineStyleNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",scrollBehavior:"instant",defaultFocusScroll:!1,getCacheBusterParam:!1,globalViewTransitions:!1,methodsThatUseUrlParams:["get","delete"],selfRequestsOnly:!0,ignoreTitle:!1,scrollIntoViewOnBoost:!0,triggerSpecsCache:null,disableInheritance:!1,responseHandling:[{code:"204",swap:!1},{code:"[23]..",swap:!0},{code:"[45]..",swap:!1,error:!0}],allowNestedOobSwaps:!0,historyRestoreAsHxRequest:!0,reportValidityOfForms:!1},parseInterval:null,location,_:null,version:"2.0.8"};htmx.onLoad=onLoadHelper,htmx.process=processNode,htmx.on=addEventListenerImpl,htmx.off=removeEventListenerImpl,htmx.trigger=triggerEvent,htmx.ajax=ajaxHelper,htmx.find=find,htmx.findAll=findAll,htmx.closest=closest,htmx.remove=removeElement,htmx.addClass=addClassToElement,htmx.removeClass=removeClassFromElement,htmx.toggleClass=toggleClassOnElement,htmx.takeClass=takeClassForElement,htmx.swap=swap,htmx.defineExtension=defineExtension,htmx.removeExtension=removeExtension,htmx.logAll=logAll,htmx.logNone=logNone,htmx.parseInterval=parseInterval,htmx._=internalEval;let internalAPI={addTriggerHandler,bodyContains,canAccessLocalStorage,findThisElement,filterValues,swap,hasAttribute,getAttributeValue,getClosestAttributeValue,getClosestMatch,getExpressionVars,getHeaders,getInputValues,getInternalData,getSwapSpecification,getTriggerSpecs,getTarget,makeFragment,mergeObjects,makeSettleInfo,oobSwap,querySelectorExt,settleImmediately,shouldCancel,triggerEvent,triggerErrorEvent,withExtensions},VERBS=["get","post","put","delete","patch"],VERB_SELECTOR=VERBS.map(function(n){return"[hx-"+n+"], [data-hx-"+n+"]"}).join(", ");function parseInterval(n){if(n==null)return;let e=NaN;return n.slice(-2)=="ms"?e=parseFloat(n.slice(0,-2)):n.slice(-1)=="s"?e=parseFloat(n.slice(0,-1))*1e3:n.slice(-1)=="m"?e=parseFloat(n.slice(0,-1))*1e3*60:e=parseFloat(n),isNaN(e)?void 0:e}function getRawAttribute(n,e){return n instanceof Element&&n.getAttribute(e)}function hasAttribute(n,e){return!!n.hasAttribute&&(n.hasAttribute(e)||n.hasAttribute("data-"+e))}function getAttributeValue(n,e){return getRawAttribute(n,e)||getRawAttribute(n,"data-"+e)}function parentElt(n){let e=n.parentElement;return!e&&n.parentNode instanceof ShadowRoot?n.parentNode:e}function getDocument(){return document}function getRootNode(n,e){return n.getRootNode?n.getRootNode({composed:e}):getDocument()}function getClosestMatch(n,e){for(;n&&!e(n);)n=parentElt(n);return n||null}function getAttributeValueWithDisinheritance(n,e,t){let i=getAttributeValue(e,t),r=getAttributeValue(e,"hx-disinherit");var o=getAttributeValue(e,"hx-inherit");if(n!==e){if(htmx.config.disableInheritance)return o&&(o==="*"||o.split(" ").indexOf(t)>=0)?i:null;if(r&&(r==="*"||r.split(" ").indexOf(t)>=0))return"unset"}return i}function getClosestAttributeValue(n,e){let t=null;if(getClosestMatch(n,function(i){return!!(t=getAttributeValueWithDisinheritance(n,asElement(i),e))}),t!=="unset")return t}function matches(n,e){return n instanceof Element&&n.matches(e)}function getStartTag(n){let t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i.exec(n);return t?t[1].toLowerCase():""}function parseHTML(n){return"parseHTMLUnsafe"in Document?Document.parseHTMLUnsafe(n):new DOMParser().parseFromString(n,"text/html")}function takeChildrenFor(n,e){for(;e.childNodes.length>0;)n.append(e.childNodes[0])}function duplicateScript(n){let e=getDocument().createElement("script");return forEach(n.attributes,function(t){e.setAttribute(t.name,t.value)}),e.textContent=n.textContent,e.async=!1,htmx.config.inlineScriptNonce&&(e.nonce=htmx.config.inlineScriptNonce),e}function isJavaScriptScriptNode(n){return n.matches("script")&&(n.type==="text/javascript"||n.type==="module"||n.type==="")}function normalizeScriptTags(n){Array.from(n.querySelectorAll("script")).forEach(e=>{if(isJavaScriptScriptNode(e)){let t=duplicateScript(e),i=e.parentNode;try{i.insertBefore(t,e)}catch(r){logError(r)}finally{e.remove()}}})}function makeFragment(n){let e=n.replace(/]*)?>[\s\S]*?<\/head>/i,""),t=getStartTag(e),i;if(t==="html"){i=new DocumentFragment;let o=parseHTML(n);takeChildrenFor(i,o.body),i.title=o.title}else if(t==="body"){i=new DocumentFragment;let o=parseHTML(e);takeChildrenFor(i,o.body),i.title=o.title}else{let o=parseHTML('");i=o.querySelector("template").content,i.title=o.title;var r=i.querySelector("title");r&&r.parentNode===i&&(r.remove(),i.title=r.innerText)}return i&&(htmx.config.allowScriptTags?normalizeScriptTags(i):i.querySelectorAll("script").forEach(o=>o.remove())),i}function maybeCall(n){n&&n()}function isType(n,e){return Object.prototype.toString.call(n)==="[object "+e+"]"}function isFunction(n){return typeof n=="function"}function isRawObject(n){return isType(n,"Object")}function getInternalData(n){let e="htmx-internal-data",t=n[e];return t||(t=n[e]={}),t}function toArray(n){let e=[];if(n)for(let t=0;t=0}function bodyContains(n){return n.getRootNode({composed:!0})===document}function splitOnWhitespace(n){return n.trim().split(/\s+/)}function mergeObjects(n,e){for(let t in e)e.hasOwnProperty(t)&&(n[t]=e[t]);return n}function parseJSON(n){try{return JSON.parse(n)}catch(e){return logError(e),null}}function canAccessLocalStorage(){let n="htmx:sessionStorageTest";try{return sessionStorage.setItem(n,n),sessionStorage.removeItem(n),!0}catch(e){return!1}}function normalizePath(n){let e=new URL(n,"http://x");return e&&(n=e.pathname+e.search),n!="/"&&(n=n.replace(/\/+$/,"")),n}function internalEval(str){return maybeEval(getDocument().body,function(){return eval(str)})}function onLoadHelper(n){return htmx.on("htmx:load",function(t){n(t.detail.elt)})}function logAll(){htmx.logger=function(n,e,t){console&&console.log(e,n,t)}}function logNone(){htmx.logger=null}function find(n,e){return typeof n!="string"?n.querySelector(e):find(getDocument(),n)}function findAll(n,e){return typeof n!="string"?n.querySelectorAll(e):findAll(getDocument(),n)}function getWindow(){return window}function removeElement(n,e){n=resolveTarget(n),e?getWindow().setTimeout(function(){removeElement(n),n=null},e):parentElt(n).removeChild(n)}function asElement(n){return n instanceof Element?n:null}function asHtmlElement(n){return n instanceof HTMLElement?n:null}function asString(n){return typeof n=="string"?n:null}function asParentNode(n){return n instanceof Element||n instanceof Document||n instanceof DocumentFragment?n:null}function addClassToElement(n,e,t){n=asElement(resolveTarget(n)),n&&(t?getWindow().setTimeout(function(){addClassToElement(n,e),n=null},t):n.classList&&n.classList.add(e))}function removeClassFromElement(n,e,t){let i=asElement(resolveTarget(n));i&&(t?getWindow().setTimeout(function(){removeClassFromElement(i,e),i=null},t):i.classList&&(i.classList.remove(e),i.classList.length===0&&i.removeAttribute("class")))}function toggleClassOnElement(n,e){n=resolveTarget(n),n.classList.toggle(e)}function takeClassForElement(n,e){n=resolveTarget(n),forEach(n.parentElement.children,function(t){removeClassFromElement(t,e)}),addClassToElement(asElement(n),e)}function closest(n,e){return n=asElement(resolveTarget(n)),n?n.closest(e):null}function startsWith(n,e){return n.substring(0,e.length)===e}function endsWith(n,e){return n.substring(n.length-e.length)===e}function normalizeSelector(n){let e=n.trim();return startsWith(e,"<")&&endsWith(e,"/>")?e.substring(1,e.length-2):e}function querySelectorAllExt(n,e,t){if(e.indexOf("global ")===0)return querySelectorAllExt(n,e.slice(7),!0);n=resolveTarget(n);let i=[];{let s=0,a=0;for(let l=0;l"&&s--}a0;){let s=normalizeSelector(i.shift()),a;s.indexOf("closest ")===0?a=closest(asElement(n),normalizeSelector(s.slice(8))):s.indexOf("find ")===0?a=find(asParentNode(n),normalizeSelector(s.slice(5))):s==="next"||s==="nextElementSibling"?a=asElement(n).nextElementSibling:s.indexOf("next ")===0?a=scanForwardQuery(n,normalizeSelector(s.slice(5)),!!t):s==="previous"||s==="previousElementSibling"?a=asElement(n).previousElementSibling:s.indexOf("previous ")===0?a=scanBackwardsQuery(n,normalizeSelector(s.slice(9)),!!t):s==="document"?a=document:s==="window"?a=window:s==="body"?a=document.body:s==="root"?a=getRootNode(n,!!t):s==="host"?a=n.getRootNode().host:o.push(s),a&&r.push(a)}if(o.length>0){let s=o.join(","),a=asParentNode(getRootNode(n,!!t));r.push(...toArray(a.querySelectorAll(s)))}return r}var scanForwardQuery=function(n,e,t){let i=asParentNode(getRootNode(n,t)).querySelectorAll(e);for(let r=0;r=0;r--){let o=i[r];if(o.compareDocumentPosition(n)===Node.DOCUMENT_POSITION_FOLLOWING)return o}};function querySelectorExt(n,e){return typeof n!="string"?querySelectorAllExt(n,e)[0]:querySelectorAllExt(getDocument().body,n)[0]}function resolveTarget(n,e){return typeof n=="string"?find(asParentNode(e)||document,n):n}function processEventArgs(n,e,t,i){return isFunction(e)?{target:getDocument().body,event:asString(n),listener:e,options:t}:{target:resolveTarget(n),event:asString(e),listener:t,options:i}}function addEventListenerImpl(n,e,t,i){return ready(function(){let o=processEventArgs(n,e,t,i);o.target.addEventListener(o.event,o.listener,o.options)}),isFunction(e)?e:t}function removeEventListenerImpl(n,e,t){return ready(function(){let i=processEventArgs(n,e,t);i.target.removeEventListener(i.event,i.listener)}),isFunction(e)?e:t}let DUMMY_ELT=getDocument().createElement("output");function findAttributeTargets(n,e){let t=getClosestAttributeValue(n,e);if(t){if(t==="this")return[findThisElement(n,e)];{let i=querySelectorAllExt(n,t);if(/(^|,)(\s*)inherit(\s*)($|,)/.test(t)){let o=asElement(getClosestMatch(n,function(s){return s!==n&&hasAttribute(asElement(s),e)}));o&&i.push(...findAttributeTargets(o,e))}return i.length===0?(logError('The selector "'+t+'" on '+e+" returned no matches!"),[DUMMY_ELT]):i}}}function findThisElement(n,e){return asElement(getClosestMatch(n,function(t){return getAttributeValue(asElement(t),e)!=null}))}function getTarget(n){let e=getClosestAttributeValue(n,"hx-target");return e?e==="this"?findThisElement(n,"hx-target"):querySelectorExt(n,e):getInternalData(n).boosted?getDocument().body:n}function shouldSettleAttribute(n){return htmx.config.attributesToSettle.includes(n)}function cloneAttributes(n,e){forEach(Array.from(n.attributes),function(t){!e.hasAttribute(t.name)&&shouldSettleAttribute(t.name)&&n.removeAttribute(t.name)}),forEach(e.attributes,function(t){shouldSettleAttribute(t.name)&&n.setAttribute(t.name,t.value)})}function isInlineSwap(n,e){let t=getExtensions(e);for(let i=0;i0?(o=n.substring(0,n.indexOf(":")),r=n.substring(n.indexOf(":")+1)):o=n),e.removeAttribute("hx-swap-oob"),e.removeAttribute("data-hx-swap-oob");let s=querySelectorAllExt(i,r,!1);return s.length?(forEach(s,function(a){let l,c=e.cloneNode(!0);l=getDocument().createDocumentFragment(),l.appendChild(c),isInlineSwap(o,a)||(l=asParentNode(c));let u={shouldSwap:!0,target:a,fragment:l};triggerEvent(a,"htmx:oobBeforeSwap",u)&&(a=u.target,u.shouldSwap&&(handlePreservedElements(l),swapWithStyle(o,a,a,l,t),restorePreservedElements()),forEach(t.elts,function(d){triggerEvent(d,"htmx:oobAfterSwap",u)}))}),e.parentNode.removeChild(e)):(e.parentNode.removeChild(e),triggerErrorEvent(getDocument().body,"htmx:oobErrorNoTarget",{content:e})),n}function restorePreservedElements(){let n=find("#--htmx-preserve-pantry--");if(n){for(let e of[...n.children]){let t=find("#"+e.id);t.parentNode.moveBefore(e,t),t.remove()}n.remove()}}function handlePreservedElements(n){forEach(findAll(n,"[hx-preserve], [data-hx-preserve]"),function(e){let t=getAttributeValue(e,"id"),i=getDocument().getElementById(t);if(i!=null)if(e.moveBefore){let r=find("#--htmx-preserve-pantry--");r==null&&(getDocument().body.insertAdjacentHTML("afterend","
"),r=find("#--htmx-preserve-pantry--")),r.moveBefore(i,null)}else e.parentNode.replaceChild(i,e)})}function handleAttributes(n,e,t){forEach(e.querySelectorAll("[id]"),function(i){let r=getRawAttribute(i,"id");if(r&&r.length>0){let o=r.replace("'","\\'"),s=i.tagName.replace(":","\\:"),a=asParentNode(n),l=a&&a.querySelector(s+"[id='"+o+"']");if(l&&l!==a){let c=i.cloneNode();cloneAttributes(i,l),t.tasks.push(function(){cloneAttributes(i,c)})}}})}function makeAjaxLoadTask(n){return function(){removeClassFromElement(n,htmx.config.addedClass),processNode(asElement(n)),processFocus(asParentNode(n)),triggerEvent(n,"htmx:load")}}function processFocus(n){let e="[autofocus]",t=asHtmlElement(matches(n,e)?n:n.querySelector(e));t!=null&&t.focus()}function insertNodesBefore(n,e,t,i){for(handleAttributes(n,t,i);t.childNodes.length>0;){let r=t.firstChild;addClassToElement(asElement(r),htmx.config.addedClass),n.insertBefore(r,e),r.nodeType!==Node.TEXT_NODE&&r.nodeType!==Node.COMMENT_NODE&&i.tasks.push(makeAjaxLoadTask(r))}}function stringHash(n,e){let t=0;for(;t0}function swap(n,e,t,i){i||(i={});let r=null,o=null,s=function(){maybeCall(i.beforeSwapCallback),n=resolveTarget(n);let c=i.contextElement?getRootNode(i.contextElement,!1):getDocument(),u=document.activeElement,d={};d={elt:u,start:u?u.selectionStart:null,end:u?u.selectionEnd:null};let p=makeSettleInfo(n);if(t.swapStyle==="textContent")n.textContent=e;else{let m=makeFragment(e);if(p.title=i.title||m.title,i.historyRequest&&(m=m.querySelector("[hx-history-elt],[data-hx-history-elt]")||m),i.selectOOB){let v=i.selectOOB.split(",");for(let _=0;_0?getWindow().setTimeout(E,t.settleDelay):E()},a=htmx.config.globalViewTransitions;t.hasOwnProperty("transition")&&(a=t.transition);let l=i.contextElement||getDocument();if(a&&triggerEvent(l,"htmx:beforeTransition",i.eventInfo)&&typeof Promise!="undefined"&&document.startViewTransition){let c=new Promise(function(d,p){r=d,o=p}),u=s;s=function(){document.startViewTransition(function(){return u(),c})}}try{t!=null&&t.swapDelay&&t.swapDelay>0?getWindow().setTimeout(s,t.swapDelay):s()}catch(c){throw triggerErrorEvent(l,"htmx:swapError",i.eventInfo),maybeCall(o),c}}function handleTriggerHeader(n,e,t){let i=n.getResponseHeader(e);if(i.indexOf("{")===0){let r=parseJSON(i);for(let o in r)if(r.hasOwnProperty(o)){let s=r[o];isRawObject(s)?t=s.target!==void 0?s.target:t:s={value:s},triggerEvent(t,o,s)}}else{let r=i.split(",");for(let o=0;o0;){let s=e[0];if(s==="]"){if(i--,i===0){o===null&&(r=r+"true"),e.shift(),r+=")})";try{let a=maybeEval(n,function(){return Function(r)()},function(){return!0});return a.source=r,a}catch(a){return triggerErrorEvent(getDocument().body,"htmx:syntax:error",{error:a,source:r}),null}}}else s==="["&&i++;isPossibleRelativeReference(s,o,t)?r+="(("+t+"."+s+") ? ("+t+"."+s+") : (window."+s+"))":r=r+s,o=e.shift()}}}function consumeUntil(n,e){let t="";for(;n.length>0&&!e.test(n[0]);)t+=n.shift();return t}function consumeCSSSelector(n){let e;return n.length>0&&COMBINED_SELECTOR_START.test(n[0])?(n.shift(),e=consumeUntil(n,COMBINED_SELECTOR_END).trim(),n.shift()):e=consumeUntil(n,WHITESPACE_OR_COMMA),e}let INPUT_SELECTOR="input, textarea, select";function parseAndCacheTrigger(n,e,t){let i=[],r=tokenizeString(e);do{consumeUntil(r,NOT_WHITESPACE);let a=r.length,l=consumeUntil(r,/[,\[\s]/);if(l!=="")if(l==="every"){let c={trigger:"every"};consumeUntil(r,NOT_WHITESPACE),c.pollInterval=parseInterval(consumeUntil(r,/[,\[\s]/)),consumeUntil(r,NOT_WHITESPACE);var o=maybeGenerateConditional(n,r,"event");o&&(c.eventFilter=o),i.push(c)}else{let c={trigger:l};var o=maybeGenerateConditional(n,r,"event");for(o&&(c.eventFilter=o),consumeUntil(r,NOT_WHITESPACE);r.length>0&&r[0]!==",";){let d=r.shift();if(d==="changed")c.changed=!0;else if(d==="once")c.once=!0;else if(d==="consume")c.consume=!0;else if(d==="delay"&&r[0]===":")r.shift(),c.delay=parseInterval(consumeUntil(r,WHITESPACE_OR_COMMA));else if(d==="from"&&r[0]===":"){if(r.shift(),COMBINED_SELECTOR_START.test(r[0]))var s=consumeCSSSelector(r);else{var s=consumeUntil(r,WHITESPACE_OR_COMMA);if(s==="closest"||s==="find"||s==="next"||s==="previous"){r.shift();let E=consumeCSSSelector(r);E.length>0&&(s+=" "+E)}}c.from=s}else d==="target"&&r[0]===":"?(r.shift(),c.target=consumeCSSSelector(r)):d==="throttle"&&r[0]===":"?(r.shift(),c.throttle=parseInterval(consumeUntil(r,WHITESPACE_OR_COMMA))):d==="queue"&&r[0]===":"?(r.shift(),c.queue=consumeUntil(r,WHITESPACE_OR_COMMA)):d==="root"&&r[0]===":"?(r.shift(),c[d]=consumeCSSSelector(r)):d==="threshold"&&r[0]===":"?(r.shift(),c[d]=consumeUntil(r,WHITESPACE_OR_COMMA)):triggerErrorEvent(n,"htmx:syntax:error",{token:r.shift()});consumeUntil(r,NOT_WHITESPACE)}i.push(c)}r.length===a&&triggerErrorEvent(n,"htmx:syntax:error",{token:r.shift()}),consumeUntil(r,NOT_WHITESPACE)}while(r[0]===","&&r.shift());return t&&(t[e]=i),i}function getTriggerSpecs(n){let e=getAttributeValue(n,"hx-trigger"),t=[];if(e){let i=htmx.config.triggerSpecsCache;t=i&&i[e]||parseAndCacheTrigger(n,e,i)}return t.length>0?t:matches(n,"form")?[{trigger:"submit"}]:matches(n,'input[type="button"], input[type="submit"]')?[{trigger:"click"}]:matches(n,INPUT_SELECTOR)?[{trigger:"change"}]:[{trigger:"click"}]}function cancelPolling(n){getInternalData(n).cancelled=!0}function processPolling(n,e,t){let i=getInternalData(n);i.timeout=getWindow().setTimeout(function(){bodyContains(n)&&i.cancelled!==!0&&(maybeFilterEvent(t,n,makeEvent("hx:poll:trigger",{triggerSpec:t,target:n}))||e(n),processPolling(n,e,t))},t.pollInterval)}function isLocalLink(n){return location.hostname===n.hostname&&getRawAttribute(n,"href")&&getRawAttribute(n,"href").indexOf("#")!==0}function eltIsDisabled(n){return closest(n,htmx.config.disableSelector)}function boostElement(n,e,t){if(n instanceof HTMLAnchorElement&&isLocalLink(n)&&(n.target===""||n.target==="_self")||n.tagName==="FORM"&&String(getRawAttribute(n,"method")).toLowerCase()!=="dialog"){e.boosted=!0;let i,r;if(n.tagName==="A")i="get",r=getRawAttribute(n,"href");else{let o=getRawAttribute(n,"method");i=o?o.toLowerCase():"get",r=getRawAttribute(n,"action"),(r==null||r==="")&&(r=location.href),i==="get"&&r.includes("?")&&(r=r.replace(/\?[^#]+/,""))}t.forEach(function(o){addEventListener(n,function(s,a){let l=asElement(s);if(eltIsDisabled(l)){cleanUpElement(l);return}issueAjaxRequest(i,r,l,a)},e,o,!0)})}}function shouldCancel(n,e){if(n.type==="submit"&&e.tagName==="FORM")return!0;if(n.type==="click"){let t=e.closest('input[type="submit"], button');if(t&&t.form&&t.type==="submit")return!0;let i=e.closest("a"),r=/^#.+/;if(i&&i.href&&!r.test(i.getAttribute("href")))return!0}return!1}function ignoreBoostedAnchorCtrlClick(n,e){return getInternalData(n).boosted&&n instanceof HTMLAnchorElement&&e.type==="click"&&(e.ctrlKey||e.metaKey)}function maybeFilterEvent(n,e,t){let i=n.eventFilter;if(i)try{return i.call(e,t)!==!0}catch(r){let o=i.source;return triggerErrorEvent(getDocument().body,"htmx:eventFilter:error",{error:r,source:o}),!0}return!1}function addEventListener(n,e,t,i,r){let o=getInternalData(n),s;i.from?s=querySelectorAllExt(n,i.from):s=[n],i.changed&&("lastValue"in o||(o.lastValue=new WeakMap),s.forEach(function(a){o.lastValue.has(i)||o.lastValue.set(i,new WeakMap),o.lastValue.get(i).set(a,a.value)})),forEach(s,function(a){let l=function(c){if(!bodyContains(n)){a.removeEventListener(i.trigger,l);return}if(ignoreBoostedAnchorCtrlClick(n,c)||((r||shouldCancel(c,a))&&c.preventDefault(),maybeFilterEvent(i,n,c)))return;let u=getInternalData(c);if(u.triggerSpec=i,u.handledFor==null&&(u.handledFor=[]),u.handledFor.indexOf(n)<0){if(u.handledFor.push(n),i.consume&&c.stopPropagation(),i.target&&c.target&&!matches(asElement(c.target),i.target))return;if(i.once){if(o.triggeredOnce)return;o.triggeredOnce=!0}if(i.changed){let d=c.target,p=d.value,E=o.lastValue.get(i);if(E.has(d)&&E.get(d)===p)return;E.set(d,p)}if(o.delayed&&clearTimeout(o.delayed),o.throttle)return;i.throttle>0?o.throttle||(triggerEvent(n,"htmx:trigger"),e(n,c),o.throttle=getWindow().setTimeout(function(){o.throttle=null},i.throttle)):i.delay>0?o.delayed=getWindow().setTimeout(function(){triggerEvent(n,"htmx:trigger"),e(n,c)},i.delay):(triggerEvent(n,"htmx:trigger"),e(n,c))}};t.listenerInfos==null&&(t.listenerInfos=[]),t.listenerInfos.push({trigger:i.trigger,listener:l,on:a}),a.addEventListener(i.trigger,l)})}let windowIsScrolling=!1,scrollHandler=null;function initScrollHandler(){scrollHandler||(scrollHandler=function(){windowIsScrolling=!0},window.addEventListener("scroll",scrollHandler),window.addEventListener("resize",scrollHandler),setInterval(function(){windowIsScrolling&&(windowIsScrolling=!1,forEach(getDocument().querySelectorAll("[hx-trigger*='revealed'],[data-hx-trigger*='revealed']"),function(n){maybeReveal(n)}))},200))}function maybeReveal(n){!hasAttribute(n,"data-hx-revealed")&&isScrolledIntoView(n)&&(n.setAttribute("data-hx-revealed","true"),getInternalData(n).initHash?triggerEvent(n,"revealed"):n.addEventListener("htmx:afterProcessNode",function(){triggerEvent(n,"revealed")},{once:!0}))}function loadImmediately(n,e,t,i){let r=function(){t.loaded||(t.loaded=!0,triggerEvent(n,"htmx:trigger"),e(n))};i>0?getWindow().setTimeout(r,i):r()}function processVerbs(n,e,t){let i=!1;return forEach(VERBS,function(r){if(hasAttribute(n,"hx-"+r)){let o=getAttributeValue(n,"hx-"+r);i=!0,e.path=o,e.verb=r,t.forEach(function(s){addTriggerHandler(n,s,e,function(a,l){let c=asElement(a);if(eltIsDisabled(c)){cleanUpElement(c);return}issueAjaxRequest(r,o,c,l)})})}}),i}function addTriggerHandler(n,e,t,i){if(e.trigger==="revealed")initScrollHandler(),addEventListener(n,i,t,e),maybeReveal(asElement(n));else if(e.trigger==="intersect"){let r={};e.root&&(r.root=querySelectorExt(n,e.root)),e.threshold&&(r.threshold=parseFloat(e.threshold)),new IntersectionObserver(function(s){for(let a=0;a0?(t.polling=!0,processPolling(asElement(n),i,e)):addEventListener(n,i,t,e)}function shouldProcessHxOn(n){let e=asElement(n);if(!e)return!1;let t=e.attributes;for(let i=0;i", "+o).join(""))}else return[]}function maybeSetLastButtonClicked(n){let e=getTargetButton(n.target),t=getRelatedFormData(n);t&&(t.lastButtonClicked=e)}function maybeUnsetLastButtonClicked(n){let e=getRelatedFormData(n);e&&(e.lastButtonClicked=null)}function getTargetButton(n){return closest(asElement(n),"button, input[type='submit']")}function getRelatedForm(n){return n.form||closest(n,"form")}function getRelatedFormData(n){let e=getTargetButton(n.target);if(!e)return;let t=getRelatedForm(e);if(t)return getInternalData(t)}function initButtonTracking(n){n.addEventListener("click",maybeSetLastButtonClicked),n.addEventListener("focusin",maybeSetLastButtonClicked),n.addEventListener("focusout",maybeUnsetLastButtonClicked)}function addHxOnEventHandler(n,e,t){let i=getInternalData(n);Array.isArray(i.onHandlers)||(i.onHandlers=[]);let r,o=function(s){maybeEval(n,function(){eltIsDisabled(n)||(r||(r=new Function("event",t)),r.call(n,s))})};n.addEventListener(e,o),i.onHandlers.push({event:e,listener:o})}function processHxOnWildcard(n){deInitOnHandlers(n);for(let e=0;ehtmx.config.historyCacheSize;)o.shift();for(;o.length>0;)try{sessionStorage.setItem("htmx-history-cache",JSON.stringify(o));break}catch(a){triggerErrorEvent(getDocument().body,"htmx:historyCacheError",{cause:a,cache:o}),o.shift()}}function getCachedHistory(n){if(!canAccessLocalStorage())return null;n=normalizePath(n);let e=parseJSON(sessionStorage.getItem("htmx-history-cache"))||[];for(let t=0;t=200&&this.status<400?(i.response=this.response,triggerEvent(getDocument().body,"htmx:historyCacheMissLoad",i),swap(i.historyElt,i.response,t,{contextElement:i.historyElt,historyRequest:!0}),setCurrentPathForHistory(i.path),triggerEvent(getDocument().body,"htmx:historyRestore",{path:n,cacheMiss:!0,serverResponse:i.response})):triggerErrorEvent(getDocument().body,"htmx:historyCacheMissLoadError",i)},triggerEvent(getDocument().body,"htmx:historyCacheMiss",i)&&e.send()}function restoreHistory(n){saveCurrentPageToHistory(),n=n||location.pathname+location.search;let e=getCachedHistory(n);if(e){let t={swapStyle:"innerHTML",swapDelay:0,settleDelay:0,scroll:e.scroll},i={path:n,item:e,historyElt:getHistoryElement(),swapSpec:t};triggerEvent(getDocument().body,"htmx:historyCacheHit",i)&&(swap(i.historyElt,e.content,t,{contextElement:i.historyElt,title:e.title}),setCurrentPathForHistory(i.path),triggerEvent(getDocument().body,"htmx:historyRestore",i))}else htmx.config.refreshOnHistoryMiss?htmx.location.reload(!0):loadHistoryFromServer(n)}function addRequestIndicatorClasses(n){let e=findAttributeTargets(n,"hx-indicator");return e==null&&(e=[n]),forEach(e,function(t){let i=getInternalData(t);i.requestCount=(i.requestCount||0)+1,t.classList.add.call(t.classList,htmx.config.requestClass)}),e}function disableElements(n){let e=findAttributeTargets(n,"hx-disabled-elt");return e==null&&(e=[]),forEach(e,function(t){let i=getInternalData(t);i.requestCount=(i.requestCount||0)+1,t.setAttribute("disabled",""),t.setAttribute("data-disabled-by-htmx","")}),e}function removeRequestIndicators(n,e){forEach(n.concat(e),function(t){let i=getInternalData(t);i.requestCount=(i.requestCount||1)-1}),forEach(n,function(t){getInternalData(t).requestCount===0&&t.classList.remove.call(t.classList,htmx.config.requestClass)}),forEach(e,function(t){getInternalData(t).requestCount===0&&(t.removeAttribute("disabled"),t.removeAttribute("data-disabled-by-htmx"))})}function haveSeenNode(n,e){for(let t=0;te.indexOf(r)<0):i=i.filter(r=>r!==e),t.delete(n),forEach(i,r=>t.append(n,r))}}function getValueFromInput(n){return n instanceof HTMLSelectElement&&n.multiple?toArray(n.querySelectorAll("option:checked")).map(function(e){return e.value}):n instanceof HTMLInputElement&&n.files?toArray(n.files):n.value}function processInputValue(n,e,t,i,r){if(!(i==null||haveSeenNode(n,i))){if(n.push(i),shouldInclude(i)){let o=getRawAttribute(i,"name");addValueToFormData(o,getValueFromInput(i),e),r&&validateElement(i,t)}i instanceof HTMLFormElement&&(forEach(i.elements,function(o){n.indexOf(o)>=0?removeValueFromFormData(o.name,getValueFromInput(o),e):n.push(o),r&&validateElement(o,t)}),new FormData(i).forEach(function(o,s){o instanceof File&&o.name===""||addValueToFormData(s,o,e)}))}}function validateElement(n,e){let t=n;t.willValidate&&(triggerEvent(t,"htmx:validation:validate"),t.checkValidity()||(triggerEvent(t,"htmx:validation:failed",{message:t.validationMessage,validity:t.validity})&&!e.length&&htmx.config.reportValidityOfForms&&t.reportValidity(),e.push({elt:t,message:t.validationMessage,validity:t.validity})))}function overrideFormData(n,e){for(let t of e.keys())n.delete(t);return e.forEach(function(t,i){n.append(i,t)}),n}function getInputValues(n,e){let t=[],i=new FormData,r=new FormData,o=[],s=getInternalData(n);s.lastButtonClicked&&!bodyContains(s.lastButtonClicked)&&(s.lastButtonClicked=null);let a=n instanceof HTMLFormElement&&n.noValidate!==!0||getAttributeValue(n,"hx-validate")==="true";if(s.lastButtonClicked&&(a=a&&s.lastButtonClicked.formNoValidate!==!0),e!=="get"&&processInputValue(t,r,o,getRelatedForm(n),a),processInputValue(t,i,o,n,a),s.lastButtonClicked||n.tagName==="BUTTON"||n.tagName==="INPUT"&&getRawAttribute(n,"type")==="submit"){let c=s.lastButtonClicked||n,u=getRawAttribute(c,"name");addValueToFormData(u,c.value,r)}let l=findAttributeTargets(n,"hx-include");return forEach(l,function(c){processInputValue(t,i,o,asElement(c),a),matches(c,"form")||forEach(asParentNode(c).querySelectorAll(INPUT_SELECTOR),function(u){processInputValue(t,i,o,u,a)})}),overrideFormData(i,r),{errors:o,formData:i,values:formDataProxy(i)}}function appendParam(n,e,t){n!==""&&(n+="&"),String(t)==="[object Object]"&&(t=JSON.stringify(t));let i=encodeURIComponent(t);return n+=encodeURIComponent(e)+"="+i,n}function urlEncode(n){n=formDataFromObject(n);let e="";return n.forEach(function(t,i){e=appendParam(e,i,t)}),e}function getHeaders(n,e,t){let i={"HX-Request":"true","HX-Trigger":getRawAttribute(n,"id"),"HX-Trigger-Name":getRawAttribute(n,"name"),"HX-Target":getAttributeValue(e,"id"),"HX-Current-URL":location.href};return getValuesForElement(n,"hx-headers",!1,i),t!==void 0&&(i["HX-Prompt"]=t),getInternalData(n).boosted&&(i["HX-Boosted"]="true"),i}function filterValues(n,e){let t=getClosestAttributeValue(e,"hx-params");if(t){if(t==="none")return new FormData;if(t==="*")return n;if(t.indexOf("not ")===0)return forEach(t.slice(4).split(","),function(i){i=i.trim(),n.delete(i)}),n;{let i=new FormData;return forEach(t.split(","),function(r){r=r.trim(),n.has(r)&&n.getAll(r).forEach(function(o){i.append(r,o)})}),i}}else return n}function isAnchorLink(n){return!!getRawAttribute(n,"href")&&getRawAttribute(n,"href").indexOf("#")>=0}function getSwapSpecification(n,e){let t=e||getClosestAttributeValue(n,"hx-swap"),i={swapStyle:getInternalData(n).boosted?"innerHTML":htmx.config.defaultSwapStyle,swapDelay:htmx.config.defaultSwapDelay,settleDelay:htmx.config.defaultSettleDelay};if(htmx.config.scrollIntoViewOnBoost&&getInternalData(n).boosted&&!isAnchorLink(n)&&(i.show="top"),t){let s=splitOnWhitespace(t);if(s.length>0)for(let a=0;a0?r.join(":"):null;i.scroll=u,i.scrollTarget=o}else if(l.indexOf("show:")===0){var r=l.slice(5).split(":");let d=r.pop();var o=r.length>0?r.join(":"):null;i.show=d,i.showTarget=o}else if(l.indexOf("focus-scroll:")===0){let c=l.slice(13);i.focusScroll=c=="true"}else a==0?i.swapStyle=l:logError("Unknown modifier in hx-swap: "+l)}}return i}function usesFormData(n){return getClosestAttributeValue(n,"hx-encoding")==="multipart/form-data"||matches(n,"form")&&getRawAttribute(n,"enctype")==="multipart/form-data"}function encodeParamsForBody(n,e,t){let i=null;return withExtensions(e,function(r){i==null&&(i=r.encodeParameters(n,t,e))}),i!=null?i:usesFormData(e)?overrideFormData(new FormData,formDataFromObject(t)):urlEncode(t)}function makeSettleInfo(n){return{tasks:[],elts:[n]}}function updateScrollState(n,e){let t=n[0],i=n[n.length-1];if(e.scroll){var r=null;e.scrollTarget&&(r=asElement(querySelectorExt(t,e.scrollTarget))),e.scroll==="top"&&(t||r)&&(r=r||t,r.scrollTop=0),e.scroll==="bottom"&&(i||r)&&(r=r||i,r.scrollTop=r.scrollHeight),typeof e.scroll=="number"&&getWindow().setTimeout(function(){window.scrollTo(0,e.scroll)},0)}if(e.show){var r=null;if(e.showTarget){let s=e.showTarget;e.showTarget==="window"&&(s="body"),r=asElement(querySelectorExt(t,s))}e.show==="top"&&(t||r)&&(r=r||t,r.scrollIntoView({block:"start",behavior:htmx.config.scrollBehavior})),e.show==="bottom"&&(i||r)&&(r=r||i,r.scrollIntoView({block:"end",behavior:htmx.config.scrollBehavior}))}}function getValuesForElement(n,e,t,i,r){if(i==null&&(i={}),n==null)return i;let o=getAttributeValue(n,e);if(o){let s=o.trim(),a=t;if(s==="unset")return null;s.indexOf("javascript:")===0?(s=s.slice(11),a=!0):s.indexOf("js:")===0&&(s=s.slice(3),a=!0),s.indexOf("{")!==0&&(s="{"+s+"}");let l;a?l=maybeEval(n,function(){return r?Function("event","return ("+s+")").call(n,r):Function("return ("+s+")").call(n)},{}):l=parseJSON(s);for(let c in l)l.hasOwnProperty(c)&&i[c]==null&&(i[c]=l[c])}return getValuesForElement(asElement(parentElt(n)),e,t,i,r)}function maybeEval(n,e,t){return htmx.config.allowEval?e():(triggerErrorEvent(n,"htmx:evalDisallowedError"),t)}function getHXVarsForElement(n,e,t){return getValuesForElement(n,"hx-vars",!0,t,e)}function getHXValsForElement(n,e,t){return getValuesForElement(n,"hx-vals",!1,t,e)}function getExpressionVars(n,e){return mergeObjects(getHXVarsForElement(n,e),getHXValsForElement(n,e))}function safelySetHeaderValue(n,e,t){if(t!==null)try{n.setRequestHeader(e,t)}catch(i){n.setRequestHeader(e,encodeURIComponent(t)),n.setRequestHeader(e+"-URI-AutoEncoded","true")}}function getPathFromResponse(n){if(n.responseURL)try{let e=new URL(n.responseURL);return e.pathname+e.search}catch(e){triggerErrorEvent(getDocument().body,"htmx:badResponseUrl",{url:n.responseURL})}}function hasHeader(n,e){return e.test(n.getAllResponseHeaders())}function ajaxHelper(n,e,t){if(n=n.toLowerCase(),t){if(t instanceof Element||typeof t=="string")return issueAjaxRequest(n,e,null,null,{targetOverride:resolveTarget(t)||DUMMY_ELT,returnPromise:!0});{let i=resolveTarget(t.target);return(t.target&&!i||t.source&&!i&&!resolveTarget(t.source))&&(i=DUMMY_ELT),issueAjaxRequest(n,e,resolveTarget(t.source),t.event,{handler:t.handler,headers:t.headers,values:t.values,targetOverride:i,swapOverride:t.swap,select:t.select,returnPromise:!0,push:t.push,replace:t.replace,selectOOB:t.selectOOB})}}else return issueAjaxRequest(n,e,null,null,{returnPromise:!0})}function hierarchyForElt(n){let e=[];for(;n;)e.push(n),n=n.parentElement;return e}function verifyPath(n,e,t){let i=new URL(e,location.protocol!=="about:"?location.href:window.origin),o=(location.protocol!=="about:"?location.origin:window.origin)===i.origin;return htmx.config.selfRequestsOnly&&!o?!1:triggerEvent(n,"htmx:validateUrl",mergeObjects({url:i,sameHost:o},t))}function formDataFromObject(n){if(n instanceof FormData)return n;let e=new FormData;for(let t in n)n.hasOwnProperty(t)&&(n[t]&&typeof n[t].forEach=="function"?n[t].forEach(function(i){e.append(t,i)}):typeof n[t]=="object"&&!(n[t]instanceof Blob)?e.append(t,JSON.stringify(n[t])):e.append(t,n[t]));return e}function formDataArrayProxy(n,e,t){return new Proxy(t,{get:function(i,r){return typeof r=="number"?i[r]:r==="length"?i.length:r==="push"?function(o){i.push(o),n.append(e,o)}:typeof i[r]=="function"?function(){i[r].apply(i,arguments),n.delete(e),i.forEach(function(o){n.append(e,o)})}:i[r]&&i[r].length===1?i[r][0]:i[r]},set:function(i,r,o){return i[r]=o,n.delete(e),i.forEach(function(s){n.append(e,s)}),!0}})}function formDataProxy(n){return new Proxy(n,{get:function(e,t){if(typeof t=="symbol"){let r=Reflect.get(e,t);return typeof r=="function"?function(){return r.apply(n,arguments)}:r}if(t==="toJSON")return()=>Object.fromEntries(n);if(t in e&&typeof e[t]=="function")return function(){return n[t].apply(n,arguments)};let i=n.getAll(t);if(i.length!==0)return i.length===1?i[0]:formDataArrayProxy(e,t,i)},set:function(e,t,i){return typeof t!="string"?!1:(e.delete(t),i&&typeof i.forEach=="function"?i.forEach(function(r){e.append(t,r)}):typeof i=="object"&&!(i instanceof Blob)?e.append(t,JSON.stringify(i)):e.append(t,i),!0)},deleteProperty:function(e,t){return typeof t=="string"&&e.delete(t),!0},ownKeys:function(e){return Reflect.ownKeys(Object.fromEntries(e))},getOwnPropertyDescriptor:function(e,t){return Reflect.getOwnPropertyDescriptor(Object.fromEntries(e),t)}})}function issueAjaxRequest(n,e,t,i,r,o){let s=null,a=null;if(r=r!=null?r:{},r.returnPromise&&typeof Promise!="undefined")var l=new Promise(function(U,ee){s=U,a=ee});t==null&&(t=getDocument().body);let c=r.handler||handleAjaxResponse,u=r.select||null;if(!bodyContains(t))return maybeCall(s),l;let d=r.targetOverride||asElement(getTarget(t));if(d==null||d==DUMMY_ELT)return triggerErrorEvent(t,"htmx:targetError",{target:getClosestAttributeValue(t,"hx-target")}),maybeCall(a),l;let p=getInternalData(t),E=p.lastButtonClicked;if(E){let U=getRawAttribute(E,"formaction");U!=null&&(e=U);let ee=getRawAttribute(E,"formmethod");if(ee!=null)if(VERBS.includes(ee.toLowerCase()))n=ee;else return maybeCall(s),l}let m=getClosestAttributeValue(t,"hx-confirm");if(o===void 0&&triggerEvent(t,"htmx:confirm",{target:d,elt:t,path:e,verb:n,triggeringEvent:i,etc:r,issueRequest:function(se){return issueAjaxRequest(n,e,t,i,r,!!se)},question:m})===!1)return maybeCall(s),l;let v=t,_=getClosestAttributeValue(t,"hx-sync"),C=null,D=!1;if(_){let U=_.split(":"),ee=U[0].trim();if(ee==="this"?v=findThisElement(t,"hx-sync"):v=asElement(querySelectorExt(t,ee)),_=(U[1]||"drop").trim(),p=getInternalData(v),_==="drop"&&p.xhr&&p.abortable!==!0)return maybeCall(s),l;if(_==="abort"){if(p.xhr)return maybeCall(s),l;D=!0}else _==="replace"?triggerEvent(v,"htmx:abort"):_.indexOf("queue")===0&&(C=(_.split(" ")[1]||"last").trim())}if(p.xhr)if(p.abortable)triggerEvent(v,"htmx:abort");else{if(C==null){if(i){let U=getInternalData(i);U&&U.triggerSpec&&U.triggerSpec.queue&&(C=U.triggerSpec.queue)}C==null&&(C="last")}return p.queuedRequests==null&&(p.queuedRequests=[]),C==="first"&&p.queuedRequests.length===0?p.queuedRequests.push(function(){issueAjaxRequest(n,e,t,i,r)}):C==="all"?p.queuedRequests.push(function(){issueAjaxRequest(n,e,t,i,r)}):C==="last"&&(p.queuedRequests=[],p.queuedRequests.push(function(){issueAjaxRequest(n,e,t,i,r)})),maybeCall(s),l}let w=new XMLHttpRequest;p.xhr=w,p.abortable=D;let T=function(){p.xhr=null,p.abortable=!1,p.queuedRequests!=null&&p.queuedRequests.length>0&&p.queuedRequests.shift()()},K=getClosestAttributeValue(t,"hx-prompt");if(K){var z=prompt(K);if(z===null||!triggerEvent(t,"htmx:prompt",{prompt:z,target:d}))return maybeCall(s),T(),l}if(m&&!o&&!confirm(m))return maybeCall(s),T(),l;let H=getHeaders(t,d,z);n!=="get"&&!usesFormData(t)&&(H["Content-Type"]="application/x-www-form-urlencoded"),r.headers&&(H=mergeObjects(H,r.headers));let R=getInputValues(t,n),L=R.errors,j=R.formData;r.values&&overrideFormData(j,formDataFromObject(r.values));let Y=formDataFromObject(getExpressionVars(t,i)),Q=overrideFormData(j,Y),Z=filterValues(Q,t);htmx.config.getCacheBusterParam&&n==="get"&&Z.set("org.htmx.cache-buster",getRawAttribute(d,"id")||"true"),(e==null||e==="")&&(e=location.href);let he=getValuesForElement(t,"hx-request"),Ce=getInternalData(t).boosted,re=htmx.config.methodsThatUseUrlParams.indexOf(n)>=0,ie={boosted:Ce,useUrlParams:re,formData:Z,parameters:formDataProxy(Z),unfilteredFormData:Q,unfilteredParameters:formDataProxy(Q),headers:H,elt:t,target:d,verb:n,errors:L,withCredentials:r.credentials||he.credentials||htmx.config.withCredentials,timeout:r.timeout||he.timeout||htmx.config.timeout,path:e,triggeringEvent:i};if(!triggerEvent(t,"htmx:configRequest",ie))return maybeCall(s),T(),l;if(e=ie.path,n=ie.verb,H=ie.headers,Z=formDataFromObject(ie.parameters),L=ie.errors,re=ie.useUrlParams,L&&L.length>0)return triggerEvent(t,"htmx:validation:halted",ie),maybeCall(s),T(),l;let Ue=e.split("#"),Ie=Ue[0],q=Ue[1],M=e;if(re&&(M=Ie,!Z.keys().next().done&&(M.indexOf("?")<0?M+="?":M+="&",M+=urlEncode(Z),q&&(M+="#"+q))),!verifyPath(t,M,ie))return triggerErrorEvent(t,"htmx:invalidPath",ie),maybeCall(a),T(),l;if(w.open(n.toUpperCase(),M,!0),w.overrideMimeType("text/html"),w.withCredentials=ie.withCredentials,w.timeout=ie.timeout,!he.noHeaders){for(let U in H)if(H.hasOwnProperty(U)){let ee=H[U];safelySetHeaderValue(w,U,ee)}}let A={xhr:w,target:d,requestConfig:ie,etc:r,boosted:Ce,select:u,pathInfo:{requestPath:e,finalRequestPath:M,responsePath:null,anchor:q}};if(w.onload=function(){try{let U=hierarchyForElt(t);if(A.pathInfo.responsePath=getPathFromResponse(w),c(t,A),A.keepIndicators!==!0&&removeRequestIndicators($,B),triggerEvent(t,"htmx:afterRequest",A),triggerEvent(t,"htmx:afterOnLoad",A),!bodyContains(t)){let ee=null;for(;U.length>0&&ee==null;){let se=U.shift();bodyContains(se)&&(ee=se)}ee&&(triggerEvent(ee,"htmx:afterRequest",A),triggerEvent(ee,"htmx:afterOnLoad",A))}maybeCall(s)}catch(U){throw triggerErrorEvent(t,"htmx:onLoadError",mergeObjects({error:U},A)),U}finally{T()}},w.onerror=function(){removeRequestIndicators($,B),triggerErrorEvent(t,"htmx:afterRequest",A),triggerErrorEvent(t,"htmx:sendError",A),maybeCall(a),T()},w.onabort=function(){removeRequestIndicators($,B),triggerErrorEvent(t,"htmx:afterRequest",A),triggerErrorEvent(t,"htmx:sendAbort",A),maybeCall(a),T()},w.ontimeout=function(){removeRequestIndicators($,B),triggerErrorEvent(t,"htmx:afterRequest",A),triggerErrorEvent(t,"htmx:timeout",A),maybeCall(a),T()},!triggerEvent(t,"htmx:beforeRequest",A))return maybeCall(s),T(),l;var $=addRequestIndicatorClasses(t),B=disableElements(t);forEach(["loadstart","loadend","progress","abort"],function(U){forEach([w,w.upload],function(ee){ee.addEventListener(U,function(se){triggerEvent(t,"htmx:xhr:"+U,{lengthComputable:se.lengthComputable,loaded:se.loaded,total:se.total})})})}),triggerEvent(t,"htmx:beforeSend",A);let W=re?null:encodeParamsForBody(w,t,Z);return w.send(W),l}function determineHistoryUpdates(n,e){let t=e.xhr,i=null,r=null;if(hasHeader(t,/HX-Push:/i)?(i=t.getResponseHeader("HX-Push"),r="push"):hasHeader(t,/HX-Push-Url:/i)?(i=t.getResponseHeader("HX-Push-Url"),r="push"):hasHeader(t,/HX-Replace-Url:/i)&&(i=t.getResponseHeader("HX-Replace-Url"),r="replace"),i)return i==="false"?{}:{type:r,path:i};let o=e.pathInfo.finalRequestPath,s=e.pathInfo.responsePath,a=e.etc.push||getClosestAttributeValue(n,"hx-push-url"),l=e.etc.replace||getClosestAttributeValue(n,"hx-replace-url"),c=getInternalData(n).boosted,u=null,d=null;return a?(u="push",d=a):l?(u="replace",d=l):c&&(u="push",d=s||o),d?d==="false"?{}:(d==="true"&&(d=s||o),e.pathInfo.anchor&&d.indexOf("#")===-1&&(d=d+"#"+e.pathInfo.anchor),{type:u,path:d}):{}}function codeMatches(n,e){var t=new RegExp(n.code);return t.test(e.toString(10))}function resolveResponseHandling(n){for(var e=0;e.${e}{opacity:0;visibility: hidden} .${t} .${e}, .${t}.${e}{opacity:1;visibility: visible;transition: opacity 200ms ease-in}`)}}function getMetaConfig(){let n=getDocument().querySelector('meta[name="htmx-config"]');return n?parseJSON(n.content):null}function mergeMetaConfig(){let n=getMetaConfig();n&&(htmx.config=mergeObjects(htmx.config,n))}return ready(function(){mergeMetaConfig(),insertIndicatorStyles();let n=getDocument().body;processNode(n);let e=getDocument().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");n.addEventListener("htmx:abort",function(i){let r=i.detail.elt||i.target,o=getInternalData(r);o&&o.xhr&&o.xhr.abort()});let t=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(i){i.state&&i.state.htmx?(restoreHistory(),forEach(e,function(r){triggerEvent(r,"htmx:restored",{document:getDocument(),triggerEvent})})):t&&t(i)},getWindow().setTimeout(function(){triggerEvent(n,"htmx:load",{}),n=null},0)}),htmx})(),M_=em;function Ro(n,e){n.split(/\s+/).forEach(t=>{e(t)})}var si=class{constructor(){this._events={}}on(e,t){Ro(e,i=>{let r=this._events[i]||[];r.push(t),this._events[i]=r})}off(e,t){var i=arguments.length;if(i===0){this._events={};return}Ro(e,r=>{if(i===1){delete this._events[r];return}let o=this._events[r];o!==void 0&&(o.splice(o.indexOf(t),1),this._events[r]=o)})}trigger(e,...t){var i=this;Ro(e,r=>{let o=i._events[r];o!==void 0&&o.forEach(s=>{s.apply(i,t)})})}};function Io(n){return n.plugins={},class extends n{constructor(){super(...arguments),this.plugins={names:[],settings:{},requested:{},loaded:{}}}static define(e,t){n.plugins[e]={name:e,fn:t}}initializePlugins(e){var t,i;let r=this,o=[];if(Array.isArray(e))e.forEach(s=>{typeof s=="string"?o.push(s):(r.plugins.settings[s.name]=s.options,o.push(s.name))});else if(e)for(t in e)e.hasOwnProperty(t)&&(r.plugins.settings[t]=e[t],o.push(t));for(;i=o.shift();)r.require(i)}loadPlugin(e){var t=this,i=t.plugins,r=n.plugins[e];if(!n.plugins.hasOwnProperty(e))throw new Error('Unable to find "'+e+'" plugin');i.requested[e]=!0,i.loaded[e]=r.fn.apply(t,[t.plugins.settings[e]||{}]),i.names.push(e)}require(e){var t=this,i=t.plugins;if(!t.plugins.loaded.hasOwnProperty(e)){if(i.requested[e])throw new Error('Plugin has circular dependency ("'+e+'")');t.loadPlugin(e)}return i.loaded[e]}}}var ai=n=>(n=n.filter(Boolean),n.length<2?n[0]||"":nm(n)==1?"["+n.join("")+"]":"(?:"+n.join("|")+")"),Po=n=>{if(!tm(n))return n.join("");let e="",t=0,i=()=>{t>1&&(e+="{"+t+"}")};return n.forEach((r,o)=>{if(r===n[o-1]){t++;return}i(),e+=r,t=1}),i(),e},Fo=n=>{let e=Array.from(n);return ai(e)},tm=n=>new Set(n).size!==n.length,Lt=n=>(n+"").replace(/([\$\(\)\*\+\.\?\[\]\^\{\|\}\\])/gu,"\\$1"),nm=n=>n.reduce((e,t)=>Math.max(e,im(t)),0),im=n=>Array.from(n).length;var $o=n=>{if(n.length===1)return[[n]];let e=[],t=n.substring(1);return $o(t).forEach(function(r){let o=r.slice(0);o[0]=n.charAt(0)+o[0],e.push(o),o=r.slice(0),o.unshift(n.charAt(0)),e.push(o)}),e};var rm=[[0,65535]],om="[\u0300-\u036F\xB7\u02BE\u02BC]",sr,za,sm=3,Bo={},$a={"/":"\u2044\u2215",0:"\u07C0",a:"\u2C65\u0250\u0251",aa:"\uA733",ae:"\xE6\u01FD\u01E3",ao:"\uA735",au:"\uA737",av:"\uA739\uA73B",ay:"\uA73D",b:"\u0180\u0253\u0183",c:"\uA73F\u0188\u023C\u2184",d:"\u0111\u0257\u0256\u1D05\u018C\uABB7\u0501\u0266",e:"\u025B\u01DD\u1D07\u0247",f:"\uA77C\u0192",g:"\u01E5\u0260\uA7A1\u1D79\uA77F\u0262",h:"\u0127\u2C68\u2C76\u0265",i:"\u0268\u0131",j:"\u0249\u0237",k:"\u0199\u2C6A\uA741\uA743\uA745\uA7A3",l:"\u0142\u019A\u026B\u2C61\uA749\uA747\uA781\u026D",m:"\u0271\u026F\u03FB",n:"\uA7A5\u019E\u0272\uA791\u1D0E\u043B\u0509",o:"\xF8\u01FF\u0254\u0275\uA74B\uA74D\u1D11",oe:"\u0153",oi:"\u01A3",oo:"\uA74F",ou:"\u0223",p:"\u01A5\u1D7D\uA751\uA753\uA755\u03C1",q:"\uA757\uA759\u024B",r:"\u024D\u027D\uA75B\uA7A7\uA783",s:"\xDF\u023F\uA7A9\uA785\u0282",t:"\u0167\u01AD\u0288\u2C66\uA787",th:"\xFE",tz:"\uA729",u:"\u0289",v:"\u028B\uA75F\u028C",vy:"\uA761",w:"\u2C73",y:"\u01B4\u024F\u1EFF",z:"\u01B6\u0225\u0240\u2C6C\uA763",hv:"\u0195"};for(let n in $a){let e=$a[n]||"";for(let t=0;t{sr===void 0&&(sr=fm(n||rm))},Ba=(n,e="NFKD")=>n.normalize(e),li=n=>Array.from(n).reduce((e,t)=>e+cm(t),""),cm=n=>(n=Ba(n).toLowerCase().replace(am,e=>Bo[e]||""),Ba(n,"NFC"));function*um(n){for(let[e,t]of n)for(let i=e;i<=t;i++){let r=String.fromCharCode(i),o=li(r);o!=r.toLowerCase()&&(o.length>sm||o.length!=0&&(yield{folded:o,composed:r,code_point:i}))}}var dm=n=>{let e={},t=(i,r)=>{let o=e[i]||new Set,s=new RegExp("^"+Fo(o)+"$","iu");r.match(s)||(o.add(Lt(r)),e[i]=o)};for(let i of um(n))t(i.folded,i.folded),t(i.folded,i.composed);return e},fm=n=>{let e=dm(n),t={},i=[];for(let o in e){let s=e[o];s&&(t[o]=Fo(s)),o.length>1&&i.push(Lt(o))}i.sort((o,s)=>s.length-o.length);let r=ai(i);return za=new RegExp("^"+r,"u"),t},hm=(n,e=1)=>{let t=0;return n=n.map(i=>(sr[i]&&(t+=i.length),sr[i]||i)),t>=e?Po(n):""},pm=(n,e=1)=>(e=Math.max(e,n.length-1),ai($o(n).map(t=>hm(t,e)))),Va=(n,e=!0)=>{let t=n.length>1?1:0;return ai(n.map(i=>{let r=[],o=e?i.length():i.length()-1;for(let s=0;s{for(let t of e){if(t.start!=n.start||t.end!=n.end||t.substrs.join("")!==n.substrs.join(""))continue;let i=n.parts,r=s=>{for(let a of i){if(a.start===s.start&&a.substr===s.substr)return!1;if(!(s.length==1||a.length==1)&&(s.starta.start||a.starts.start))return!0}return!1};if(!(t.parts.filter(r).length>0))return!0}return!1},ar=class n{constructor(){te(this,"parts");te(this,"substrs");te(this,"start");te(this,"end");this.parts=[],this.substrs=[],this.start=0,this.end=0}add(e){e&&(this.parts.push(e),this.substrs.push(e.substr),this.start=Math.min(e.start,this.start),this.end=Math.max(e.end,this.end))}last(){return this.parts[this.parts.length-1]}length(){return this.parts.length}clone(e,t){let i=new n,r=JSON.parse(JSON.stringify(this.parts)),o=r.pop();for(let l of r)i.add(l);let s=t.substr.substring(0,e-o.start),a=s.length;return i.add({start:o.start,end:o.start+a,length:a,substr:s}),i}},ja=n=>{lm(),n=li(n);let e="",t=[new ar];for(let i=0;i0){l=l.sort((u,d)=>u.length()-d.length());for(let u of l)mm(u,t)||t.push(u);continue}if(i>0&&c.size==1&&!c.has("3")){e+=Va(t,!1);let u=new ar,d=t[0];d&&u.add(d.last()),t=[u]}}return e+=Va(t,!0),e};var qa=(n,e)=>{if(n)return n[e]},Wa=(n,e)=>{if(n){for(var t,i=e.split(".");(t=i.shift())&&(n=n[t]););return n}},lr=(n,e,t)=>{var i,r;return!n||(n=n+"",e.regex==null)||(r=n.search(e.regex),r===-1)?0:(i=e.string.length/n.length,r===0&&(i+=.5),i*t)},cr=(n,e)=>{var t=n[e];if(typeof t=="function")return t;t&&!Array.isArray(t)&&(n[e]=[t])},ci=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},Ua=(n,e)=>typeof n=="number"&&typeof e=="number"?n>e?1:ne?1:e>n?-1:0);var ur=class{constructor(e,t){te(this,"items");te(this,"settings");this.items=e,this.settings=t||{diacritics:!0}}tokenize(e,t,i){if(!e||!e.length)return[];let r=[],o=e.split(/\s+/);var s;return i&&(s=new RegExp("^("+Object.keys(i).map(Lt).join("|")+"):(.*)$")),o.forEach(a=>{let l,c=null,u=null;s&&(l=a.match(s))&&(c=l[1],a=l[2]),a.length>0&&(this.settings.diacritics?u=ja(a)||null:u=Lt(a),u&&t&&(u="\\b"+u)),r.push({string:a,regex:u?new RegExp(u,"iu"):null,field:c})}),r}getScoreFunction(e,t){var i=this.prepareSearch(e,t);return this._getScoreFunction(i)}_getScoreFunction(e){let t=e.tokens,i=t.length;if(!i)return function(){return 0};let r=e.options.fields,o=e.weights,s=r.length,a=e.getAttrFn;if(!s)return function(){return 1};let l=(function(){return s===1?function(c,u){let d=r[0].field;return lr(a(u,d),c,o[d]||1)}:function(c,u){var d=0;if(c.field){let p=a(u,c.field);!c.regex&&p?d+=1/s:d+=lr(p,c,1)}else ci(o,(p,E)=>{d+=lr(a(u,E),c,p)});return d/s}})();return i===1?function(c){return l(t[0],c)}:e.options.conjunction==="and"?function(c){var u,d=0;for(let p of t){if(u=l(p,c),u<=0)return 0;d+=u}return d/i}:function(c){var u=0;return ci(t,d=>{u+=l(d,c)}),u/i}}getSortFunction(e,t){var i=this.prepareSearch(e,t);return this._getSortFunction(i)}_getSortFunction(e){var t,i=[];let r=this,o=e.options,s=!e.query&&o.sort_empty?o.sort_empty:o.sort;if(typeof s=="function")return s.bind(this);let a=function(c,u){return c==="$score"?u.score:e.getAttrFn(r.items[u.id],c)};if(s)for(let c of s)(e.query||c.field!=="$score")&&i.push(c);if(e.query){t=!0;for(let c of i)if(c.field==="$score"){t=!1;break}t&&i.unshift({field:"$score",direction:"desc"})}else i=i.filter(c=>c.field!=="$score");return i.length?function(c,u){var d,p;for(let E of i)if(p=E.field,d=(E.direction==="desc"?-1:1)*Ua(a(p,c),a(p,u)),d)return d;return 0}:null}prepareSearch(e,t){let i={};var r=Object.assign({},t);if(cr(r,"sort"),cr(r,"sort_empty"),r.fields){cr(r,"fields");let o=[];r.fields.forEach(s=>{typeof s=="string"&&(s={field:s,weight:1}),o.push(s),i[s.field]="weight"in s?s.weight:1}),r.fields=o}return{options:r,query:e.toLowerCase().trim(),tokens:this.tokenize(e,r.respect_word_boundaries,i),total:0,items:[],weights:i,getAttrFn:r.nesting?Wa:qa}}search(e,t){var i=this,r,o;o=this.prepareSearch(e,t),t=o.options,e=o.query;let s=t.score||i._getScoreFunction(o);e.length?ci(i.items,(l,c)=>{r=s(l),(t.filter===!1||r>0)&&o.items.push({score:r,id:c})}):ci(i.items,(l,c)=>{o.items.push({score:1,id:c})});let a=i._getSortFunction(o);return a&&o.items.sort(a),o.total=o.items.length,typeof t.limit=="number"&&(o.items=o.items.slice(0,t.limit)),o}};var Fe=n=>typeof n=="undefined"||n===null?null:ui(n),ui=n=>typeof n=="boolean"?n?"1":"0":n+"",dr=n=>(n+"").replace(/&/g,"&").replace(//g,">").replace(/"/g,"""),Ya=(n,e)=>e>0?window.setTimeout(n,e):(n.call(null),null),Ga=(n,e)=>{var t;return function(i,r){var o=this;t&&(o.loading=Math.max(o.loading-1,0),clearTimeout(t)),t=setTimeout(function(){t=null,o.loadedSearches[i]=!0,n.call(o,i,r)},e)}},Vo=(n,e,t)=>{var i,r=n.trigger,o={};n.trigger=function(){var s=arguments[0];if(e.indexOf(s)!==-1)o[s]=arguments;else return r.apply(n,arguments)},t.apply(n,[]),n.trigger=r;for(i of e)i in o&&r.apply(n,o[i])},Ka=n=>({start:n.selectionStart||0,length:(n.selectionEnd||0)-(n.selectionStart||0)}),fe=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},De=(n,e,t,i)=>{n.addEventListener(e,t,i)},Mt=(n,e)=>{if(!e||!e[n])return!1;var t=(e.altKey?1:0)+(e.ctrlKey?1:0)+(e.shiftKey?1:0)+(e.metaKey?1:0);return t===1},fr=(n,e)=>{let t=n.getAttribute("id");return t||(n.setAttribute("id",e),e)},zo=n=>n.replace(/[\\"']/g,"\\$&"),Nt=(n,e)=>{e&&n.append(e)},ve=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)};var nt=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(jo(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},jo=n=>typeof n=="string"&&n.indexOf("<")>-1,Xa=n=>n.replace(/['"\\]/g,"\\$&"),hr=(n,e)=>{var t=document.createEvent("HTMLEvents");t.initEvent(e,!0,!1),n.dispatchEvent(t)},di=(n,e)=>{Object.assign(n.style,e)},$e=(n,...e)=>{var t=Qa(e);n=Ja(n),n.map(i=>{t.map(r=>{i.classList.add(r)})})},gt=(n,...e)=>{var t=Qa(e);n=Ja(n),n.map(i=>{t.map(r=>{i.classList.remove(r)})})},Qa=n=>{var e=[];return ve(n,t=>{typeof t=="string"&&(t=t.trim().split(/[\t\n\f\r\s]/)),Array.isArray(t)&&(e=e.concat(t))}),e.filter(Boolean)},Ja=n=>(Array.isArray(n)||(n=[n]),n),pr=(n,e,t)=>{if(!(t&&!t.contains(n)))for(;n&&n.matches;){if(n.matches(e))return n;n=n.parentNode}},qo=(n,e=0)=>e>0?n[n.length-1]:n[0],Za=n=>Object.keys(n).length===0,Wo=(n,e)=>{if(!n)return-1;e=e||n.nodeName;for(var t=0;n=n.previousElementSibling;)n.matches(e)&&t++;return t},le=(n,e)=>{ve(e,(t,i)=>{t==null?n.removeAttribute(i):n.setAttribute(i,""+t)})},fi=(n,e)=>{n.parentNode&&n.parentNode.replaceChild(e,n)};var el=(n,e)=>{if(e===null)return;if(typeof e=="string"){if(!e.length)return;e=new RegExp(e,"i")}let t=o=>{var s=o.data.match(e);if(s&&o.data.length>0){var a=document.createElement("span");a.className="highlight";var l=o.splitText(s.index);l.splitText(s[0].length);var c=l.cloneNode(!0);return a.appendChild(c),fi(l,a),1}return 0},i=o=>{o.nodeType===1&&o.childNodes&&!/(script|style)/i.test(o.tagName)&&(o.className!=="highlight"||o.tagName!=="SPAN")&&Array.from(o.childNodes).forEach(s=>{r(s)})},r=o=>o.nodeType===3?t(o):(i(o),0);r(n)},tl=n=>{var e=n.querySelectorAll("span.highlight");Array.prototype.forEach.call(e,function(t){var i=t.parentNode;i.replaceChild(t.firstChild,t),i.normalize()})};var gm=typeof navigator=="undefined"?!1:/Mac/.test(navigator.userAgent),hi=gm?"metaKey":"ctrlKey";var Uo={options:[],optgroups:[],plugins:[],delimiter:",",splitOn:null,persist:!0,diacritics:!0,create:null,createOnBlur:!1,createFilter:null,clearAfterSelect:!1,highlight:!0,openOnFocus:!0,shouldOpen:null,maxOptions:50,maxItems:null,hideSelected:null,duplicates:!1,addPrecedence:!1,selectOnTab:!1,preload:null,allowEmptyOption:!1,refreshThrottle:300,loadThrottle:300,loadingClass:"loading",dataAttr:null,optgroupField:"optgroup",valueField:"value",labelField:"text",disabledField:"disabled",optgroupLabelField:"label",optgroupValueField:"value",lockOptgroupOrder:!1,sortField:"$order",searchField:["text"],searchConjunction:"and",mode:null,wrapperClass:"ts-wrapper",controlClass:"ts-control",dropdownClass:"ts-dropdown",dropdownContentClass:"ts-dropdown-content",itemClass:"item",optionClass:"option",dropdownParent:null,controlInput:'',copyClassesToDropdown:!1,placeholder:null,hidePlaceholder:null,shouldLoad:function(n){return n.length>0},render:{}};function mr(n,e){var t=Object.assign({},Uo,e),i=t.dataAttr,r=t.labelField,o=t.valueField,s=t.disabledField,a=t.optgroupField,l=t.optgroupLabelField,c=t.optgroupValueField,u=n.tagName.toLowerCase(),d=n.getAttribute("placeholder")||n.getAttribute("data-placeholder");if(!d&&!t.allowEmptyOption){let v=n.querySelector('option[value=""]');v&&(d=v.textContent)}var p={placeholder:d,options:[],optgroups:[],items:[],maxItems:null},E=()=>{var v,_=p.options,C={},D=1;let w=0;var T=H=>{var R=Object.assign({},H.dataset),L=i&&R[i];return typeof L=="string"&&L.length&&(R=Object.assign(R,JSON.parse(L))),R},K=(H,R)=>{var L=Fe(H.value);if(L!=null&&!(!L&&!t.allowEmptyOption)){if(C.hasOwnProperty(L)){if(R){var j=C[L][a];j?Array.isArray(j)?j.push(R):C[L][a]=[j,R]:C[L][a]=R}}else{var Y=T(H);Y[r]=Y[r]||H.textContent,Y[o]=Y[o]||L,Y[s]=Y[s]||H.disabled,Y[a]=Y[a]||R,Y.$option=H,Y.$order=Y.$order||++w,C[L]=Y,_.push(Y)}H.selected&&p.items.push(L)}},z=H=>{var R,L;L=T(H),L[l]=L[l]||H.getAttribute("label")||"",L[c]=L[c]||D++,L[s]=L[s]||H.disabled,L.$order=L.$order||++w,p.optgroups.push(L),R=L[c],ve(H.children,j=>{K(j,R)})};p.maxItems=n.hasAttribute("multiple")?null:1,ve(n.children,H=>{v=H.tagName.toLowerCase(),v==="optgroup"?z(H):v==="option"&&K(H)})},m=()=>{let v=n.getAttribute(i);if(v)p.options=JSON.parse(v),ve(p.options,C=>{p.items.push(C[o])});else{var _=n.value.trim()||"";if(!t.allowEmptyOption&&!_.length)return;let C=_.split(t.delimiter);ve(C,D=>{let w={};w[r]=D,w[o]=D,p.options.push(w)}),p.items=C}};return u==="select"?E():m(),Object.assign({},Uo,p,e)}var rl=0,we=class extends Io(si){constructor(e,t){super(),this.order=0,this.isOpen=!1,this.isDisabled=!1,this.isReadOnly=!1,this.isInvalid=!1,this.isValid=!0,this.isLocked=!1,this.isFocused=!1,this.isInputHidden=!1,this.isSetup=!1,this.isDropdownContentStale=!0,this.ignoreFocus=!1,this.ignoreHover=!1,this.hasOptions=!1,this.lastValue="",this.caretPos=0,this.loading=0,this.loadedSearches={},this.activeOption=null,this.activeItems=[],this.optgroups={},this.options={},this.userOptions={},this.items=[],this.refreshTimeout=null,rl++;var i,r=nt(e);if(r.tomselect)throw new Error("Tom Select already initialized on this element");r.tomselect=this;var o=window.getComputedStyle&&window.getComputedStyle(r,null);i=o.getPropertyValue("direction");let s=mr(r,t);this.settings=s,this.input=r,this.tabIndex=r.tabIndex||0,this.is_select_tag=r.tagName.toLowerCase()==="select",this.rtl=/rtl/i.test(i),this.inputId=fr(r,"tomselect-"+rl),this.isRequired=r.required,this.sifter=new ur(this.options,{diacritics:s.diacritics}),s.mode=s.mode||(s.maxItems===1?"single":"multi"),typeof s.hideSelected!="boolean"&&(s.hideSelected=s.mode==="multi"),typeof s.hidePlaceholder!="boolean"&&(s.hidePlaceholder=s.mode!=="multi");var a=s.createFilter;typeof a!="function"&&(typeof a=="string"&&(a=new RegExp(a)),a instanceof RegExp?s.createFilter=_=>a.test(_):s.createFilter=_=>this.settings.duplicates||!this.options[_]),this.initializePlugins(s.plugins),this.setupCallbacks(),this.setupTemplates();let l=nt("
"),c=nt("
"),u=this._render("dropdown"),d=nt('
'),p=this.input.getAttribute("class")||"",E=s.mode;var m;if($e(l,s.wrapperClass,p,E),$e(c,s.controlClass),Nt(l,c),$e(u,s.dropdownClass,E),s.copyClassesToDropdown&&$e(u,p),$e(d,s.dropdownContentClass),Nt(u,d),nt(s.dropdownParent||l).appendChild(u),jo(s.controlInput)){m=nt(s.controlInput);var v=["autocorrect","autocapitalize","autocomplete","spellcheck","aria-label"];ve(v,_=>{r.getAttribute(_)&&le(m,{[_]:r.getAttribute(_)})}),m.tabIndex=-1,c.appendChild(m),this.focus_node=m}else s.controlInput?(m=nt(s.controlInput),this.focus_node=m):(m=nt(""),this.focus_node=c);this.wrapper=l,this.dropdown=u,this.dropdown_content=d,this.control=c,this.control_input=m,this.setup()}setup(){let e=this,t=e.settings,i=e.control_input,r=e.dropdown,o=e.dropdown_content,s=e.wrapper,a=e.control,l=e.input,c=e.focus_node,u={passive:!0},d=e.inputId+"-ts-dropdown";le(o,{id:d}),le(c,{role:"combobox","aria-haspopup":"listbox","aria-expanded":"false","aria-controls":d});let p=fr(c,e.inputId+"-ts-control"),E="label[for='"+Xa(e.inputId)+"']",m=document.querySelector(E),v=e.focus.bind(e);if(m){De(m,"click",v),le(m,{for:p});let w=fr(m,e.inputId+"-ts-label");le(c,{"aria-labelledby":w}),le(o,{"aria-labelledby":w})}if(s.style.width=l.style.width,s.style.minWidth=l.style.minWidth,s.style.maxWidth=l.style.maxWidth,e.plugins.names.length){let w="plugin-"+e.plugins.names.join(" plugin-");$e([s,r],w)}(t.maxItems===null||t.maxItems>1)&&e.is_select_tag&&le(l,{multiple:"multiple"}),t.placeholder&&le(i,{placeholder:t.placeholder}),!t.splitOn&&t.delimiter&&(t.splitOn=new RegExp("\\s*"+Lt(t.delimiter)+"+\\s*")),t.load&&t.loadThrottle&&(t.load=Ga(t.load,t.loadThrottle)),De(r,"mousemove",()=>{e.ignoreHover=!1}),De(r,"mouseenter",w=>{var T=pr(w.target,"[data-selectable]",r);T&&e.onOptionHover(w,T)},{capture:!0}),De(r,"click",w=>{let T=pr(w.target,"[data-selectable]");T&&(e.onOptionSelect(w,T),fe(w,!0))}),De(a,"click",w=>{var T=pr(w.target,"[data-ts-item]",a);if(T&&e.onItemSelect(w,T)){fe(w,!0);return}i.value==""&&(e.onClick(),fe(w,!0))}),De(c,"keydown",w=>e.onKeyDown(w)),De(i,"keypress",w=>e.onKeyPress(w)),De(i,"input",w=>e.onInput(w)),De(c,"blur",w=>e.onBlur(w)),De(c,"focus",w=>e.onFocus(w)),De(i,"paste",w=>e.onPaste(w));let _=w=>{let T=w.composedPath()[0];if(!s.contains(T)&&!r.contains(T)){e.isFocused&&e.blur(),e.inputState();return}T==i&&e.isOpen?w.stopPropagation():fe(w,!0)},C=()=>{e.isOpen&&e.positionDropdown()},D=()=>{e.isValid&&(e.isValid=!1,e.isInvalid=!0,e.refreshState())};De(l,"invalid",D),De(document,"mousedown",_),De(window,"scroll",C,u),De(window,"resize",C,u),this._destroy=()=>{l.removeEventListener("invalid",D),document.removeEventListener("mousedown",_),window.removeEventListener("scroll",C),window.removeEventListener("resize",C),m&&m.removeEventListener("click",v)},this.revertSettings={innerHTML:l.innerHTML,tabIndex:l.tabIndex},l.tabIndex=-1,l.insertAdjacentElement("afterend",e.wrapper),e.sync(!1),t.items=[],delete t.optgroups,delete t.options,e.refreshItems(),e.close(!1),e.inputState(),e.isSetup=!0,l.disabled?e.disable():l.readOnly?e.setReadOnly(!0):e.enable(),e.on("change",this.onChange),$e(l,"tomselected","ts-hidden-accessible"),e.trigger("initialize"),t.preload===!0&&e.preload()}setupOptions(e=[],t=[]){this.addOptions(e),ve(t,i=>{this.registerOptionGroup(i)})}setupTemplates(){var e=this,t=e.settings.labelField,i=e.settings.optgroupLabelField,r={optgroup:o=>{let s=document.createElement("div");return s.className="optgroup",s.appendChild(o.options),s},optgroup_header:(o,s)=>'
'+s(o[i])+"
",option:(o,s)=>"
"+s(o[t])+"
",item:(o,s)=>"
"+s(o[t])+"
",option_create:(o,s)=>'
Add '+s(o.input)+"
",no_results:()=>'
No results found
',loading:()=>'
',not_loading:()=>{},dropdown:()=>"
"};e.settings.render=Object.assign({},r,e.settings.render)}setupCallbacks(){var e,t,i={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",item_select:"onItemSelect",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(e in i)t=this.settings[i[e]],t&&this.on(e,t)}sync(e=!0){let t=this,i=e?mr(t.input,{delimiter:t.settings.delimiter,allowEmptyOption:t.settings.allowEmptyOption}):t.settings;t.setupOptions(i.options,i.optgroups),t.setValue(i.items||[],!0)}onClick(){var e=this;if(e.activeItems.length>0){e.clearActiveItems(),e.focus();return}e.isFocused&&e.isOpen?e.blur():e.focus()}onMouseDown(){}onChange(){hr(this.input,"input"),hr(this.input,"change")}onPaste(e){var t=this;if(t.isInputHidden||t.isLocked){fe(e);return}t.settings.splitOn&&setTimeout(()=>{var i=t.inputValue();if(i.match(t.settings.splitOn)){var r=i.trim().split(t.settings.splitOn);ve(r,o=>{Fe(o)&&(this.options[o]?t.addItem(o):t.createItem(o))})}},0)}onKeyPress(e){var t=this;if(t.isLocked){fe(e);return}var i=String.fromCharCode(e.keyCode||e.which);if(t.settings.create&&t.settings.mode==="multi"&&i===t.settings.delimiter){t.createItem(),fe(e);return}}onKeyDown(e){var t=this;if(t.ignoreHover=!0,t.isLocked){e.keyCode!==9&&fe(e);return}switch(e.keyCode){case 65:if(Mt(hi,e)&&t.control_input.value==""){fe(e),t.selectAll();return}break;case 27:t.isOpen&&(fe(e,!0),t.close()),t.clearActiveItems();return;case 40:if(!t.isOpen&&t.hasOptions)t.open();else if(t.activeOption){let i=t.getAdjacent(t.activeOption,1);i&&t.setActiveOption(i)}fe(e);return;case 38:if(t.activeOption){let i=t.getAdjacent(t.activeOption,-1);i&&t.setActiveOption(i)}fe(e);return;case 13:t.canSelect(t.activeOption)?(t.onOptionSelect(e,t.activeOption),fe(e)):t.settings.create&&t.createItem()?fe(e):document.activeElement==t.control_input&&t.isOpen&&fe(e);return;case 37:t.advanceSelection(-1,e);return;case 39:t.advanceSelection(1,e);return;case 9:t.settings.selectOnTab&&(t.canSelect(t.activeOption)?(t.onOptionSelect(e,t.activeOption),fe(e)):t.settings.create&&t.createItem()&&fe(e));return;case 8:case 46:t.deleteSelection(e);return}t.isInputHidden&&!Mt(hi,e)&&fe(e)}onInput(e){if(this.isLocked)return;let t=this.inputValue();if(this.lastValue!==t){if(this.lastValue=t,t==""){this._onInput();return}this.refreshTimeout&&window.clearTimeout(this.refreshTimeout),this.refreshTimeout=Ya(()=>{this.refreshTimeout=null,this._onInput()},this.settings.refreshThrottle)}}_onInput(){let e=this.lastValue;this.settings.shouldLoad.call(this,e)&&this.load(e),this.refreshOptions(),this.trigger("type",e)}onOptionHover(e,t){this.ignoreHover||this.setActiveOption(t,!1)}onFocus(e){var t=this,i=t.isFocused;if(t.isDisabled||t.isReadOnly){t.blur(),fe(e);return}t.ignoreFocus||(t.isFocused=!0,t.settings.preload==="focus"&&t.preload(),i||t.trigger("focus"),t.activeItems.length||(t.inputState(),t.refreshOptions(!!t.settings.openOnFocus)),t.refreshState())}onBlur(e){if(document.hasFocus()!==!1){var t=this;if(t.isFocused){t.isFocused=!1,t.ignoreFocus=!1;var i=()=>{t.close(),t.setActiveItem(),t.setCaret(t.items.length),t.trigger("blur")};t.settings.create&&t.settings.createOnBlur?t.createItem(null,i):i()}}}onOptionSelect(e,t){var i,r=this;t.parentElement&&t.parentElement.matches("[data-disabled]")||(t.classList.contains("create")?r.createItem(null,()=>{r.settings.closeAfterSelect?r.close():r.settings.clearAfterSelect&&r.setTextboxValue()}):(i=t.dataset.value,typeof i!="undefined"&&(r.isDropdownContentStale=r.settings.hideSelected,r.addItem(i),r.settings.closeAfterSelect?r.close():r.settings.clearAfterSelect&&r.setTextboxValue(),!r.settings.hideSelected&&e.type&&/click/.test(e.type)&&r.setActiveOption(t))))}canSelect(e){return!!(this.isOpen&&e&&this.dropdown_content.contains(e))}onItemSelect(e,t){var i=this;return!i.isLocked&&i.settings.mode==="multi"?(fe(e),i.setActiveItem(t,e),!0):!1}canLoad(e){return!(!this.settings.load||this.loadedSearches.hasOwnProperty(e))}load(e){let t=this;if(!t.canLoad(e))return;$e(t.wrapper,t.settings.loadingClass),t.loading++;let i=t.loadCallback.bind(t);t.settings.load.call(t,e,i)}loadCallback(e,t){let i=this;i.loading=Math.max(i.loading-1,0),i.isDropdownContentStale=!0,i.clearActiveOption(),i.setupOptions(e,t),i.refreshOptions(i.isFocused&&!i.isInputHidden),i.loading||gt(i.wrapper,i.settings.loadingClass),i.trigger("load",e,t)}preload(){var e=this.wrapper.classList;e.contains("preloaded")||(e.add("preloaded"),this.load(""))}setTextboxValue(e=""){var t=this.control_input,i=t.value!==e;i&&(t.value=e,hr(t,"update"),this.lastValue=e)}getValue(){return this.is_select_tag&&this.input.hasAttribute("multiple")?this.items:this.items.join(this.settings.delimiter)}setValue(e,t){var i=t?[]:["change"];Vo(this,i,()=>{this.clear(t),this.addItems(e,t)})}setMaxItems(e){e===0&&(e=null),this.settings.maxItems=e,this.refreshState()}setActiveItem(e,t){var i=this,r,o,s,a,l,c;if(i.settings.mode!=="single"){if(!e){i.clearActiveItems(),i.isFocused&&i.inputState();return}if(r=t&&t.type.toLowerCase(),r==="click"&&Mt("shiftKey",t)&&i.activeItems.length){for(c=i.getLastActive(),s=Array.prototype.indexOf.call(i.control.children,c),a=Array.prototype.indexOf.call(i.control.children,e),s>a&&(l=s,s=a,a=l),o=s;o<=a;o++)e=i.control.children[o],i.activeItems.indexOf(e)===-1&&i.setActiveItemClass(e);fe(t)}else r==="click"&&Mt(hi,t)||r==="keydown"&&Mt("shiftKey",t)?e.classList.contains("active")?i.removeActiveItem(e):i.setActiveItemClass(e):(i.clearActiveItems(),i.setActiveItemClass(e));i.inputState(),i.isFocused||i.focus()}}setActiveItemClass(e){let t=this,i=t.control.querySelector(".last-active");i&>(i,"last-active"),$e(e,"active last-active"),t.trigger("item_select",e),t.activeItems.indexOf(e)==-1&&t.activeItems.push(e)}removeActiveItem(e){var t=this.activeItems.indexOf(e);this.activeItems.splice(t,1),gt(e,"active")}clearActiveItems(){gt(this.activeItems,"active"),this.activeItems=[]}setActiveOption(e,t=!0){e!==this.activeOption&&(this.clearActiveOption(),e&&(this.activeOption=e,le(this.focus_node,{"aria-activedescendant":e.getAttribute("id")}),le(e,{"aria-selected":"true"}),$e(e,"active"),t&&this.scrollToOption(e)))}scrollToOption(e,t){if(!e)return;let i=this.dropdown_content,r=i.clientHeight,o=i.scrollTop||0,s=e.offsetHeight,a=e.getBoundingClientRect().top-i.getBoundingClientRect().top+o;a+s>r+o?this.scroll(a-r+s,t):a{e.setActiveItemClass(i)}))}inputState(){var e=this;e.control.contains(e.control_input)&&(le(e.control_input,{placeholder:e.settings.placeholder}),e.activeItems.length>0||!e.isFocused&&e.settings.hidePlaceholder&&e.items.length>0?(e.setTextboxValue(),e.isInputHidden=!0):(e.settings.hidePlaceholder&&e.items.length>0&&le(e.control_input,{placeholder:""}),e.isInputHidden=!1),e.wrapper.classList.toggle("input-hidden",e.isInputHidden))}inputValue(){return this.control_input.value.trim()}focus(){var e=this;e.isDisabled||e.isReadOnly||(e.ignoreFocus=!0,e.control_input.offsetWidth?e.control_input.focus():e.focus_node.focus(),setTimeout(()=>{e.ignoreFocus=!1,e.onFocus()},0))}blur(){this.focus_node.blur(),this.onBlur()}getScoreFunction(e){return this.sifter.getScoreFunction(e,this.getSearchOptions())}getSearchOptions(){var e=this.settings,t=e.sortField;return typeof e.sortField=="string"&&(t=[{field:e.sortField}]),{fields:e.searchField,conjunction:e.searchConjunction,sort:t,nesting:e.nesting}}search(e){var t,i,r=this,o=this.getSearchOptions();if(r.settings.score&&(i=r.settings.score.call(r,e),typeof i!="function"))throw new Error('Tom Select "score" setting must be a function that returns a function');return r.isDropdownContentStale||e!==r.lastQuery?(r.lastQuery=e,/(.)\1{15,}/.test(e)&&(e=""),t=r.sifter.search(e,Object.assign(o,{score:i})),r.currentResults=t):t=Object.assign({},r.currentResults),r.settings.hideSelected&&(t.items=t.items.filter(s=>{let a=Fe(s.id);return!(a!==null&&r.items.indexOf(a)!==-1)})),t}refreshOptions(e=!0){var t,i,r,o,s,a,l,c,u,d;let p={},E=[];var m=this,v=m.inputValue();let _=v===m.lastQuery||v==""&&m.lastQuery==null;var C=m.search(v),D=null,w=m.settings.shouldOpen||!1,T=m.dropdown_content;_&&(D=m.activeOption,D&&(u=D.closest("[data-group]"))),o=C.items.length,typeof m.settings.maxOptions=="number"&&(o=Math.min(o,m.settings.maxOptions)),o>0&&(w=!0);let K=(R,L)=>{let j=p[R];if(j!==void 0){let Q=E[j];if(Q!==void 0)return[j,Q.fragment]}let Y=document.createDocumentFragment();return j=E.length,E.push({fragment:Y,order:L,optgroup:R}),[j,Y]};for(t=0;t0&&(Q=Q.cloneNode(!0),le(Q,{id:j.$id+"-clone-"+i,"aria-selected":null}),Q.classList.add("ts-cloned"),gt(Q,"active"),m.activeOption&&m.activeOption.dataset.value==L&&u&&u.dataset.group===s.toString()&&(D=Q)),re.appendChild(Q),s!=""&&(p[s]=Ce)}}m.settings.lockOptgroupOrder&&E.sort((R,L)=>R.order-L.order),l=document.createDocumentFragment(),ve(E,R=>{let L=R.fragment,j=R.optgroup;if(!L||!L.children.length)return;let Y=m.optgroups[j];if(Y!==void 0){let Q=document.createDocumentFragment(),Z=m.render("optgroup_header",Y);Nt(Q,Z),Nt(Q,L);let he=m.render("optgroup",{group:Y,options:Q});Nt(l,he)}else Nt(l,L)}),T.innerHTML="",Nt(T,l),m.isDropdownContentStale=!1,m.settings.highlight&&(tl(T),C.query.length&&C.tokens.length&&ve(C.tokens,R=>{el(T,R.regex)}));var H=R=>{let L=m.render(R,{input:v});return L&&(w=!0,T.insertBefore(L,T.firstChild)),L};if(m.loading?H("loading"):m.settings.shouldLoad.call(m,v)?C.items.length===0&&H("no_results"):H("not_loading"),c=m.canCreate(v),c&&(d=H("option_create")),m.hasOptions=C.items.length>0||c,w){if(C.items.length>0){if(!D&&m.settings.mode==="single"&&m.items[0]!=null&&(D=m.getOption(m.items[0])),!T.contains(D)){let R=0;d&&!m.settings.addPrecedence&&(R=1),D=m.selectable()[R]}}else d&&(D=d);e&&!m.isOpen&&(m.open(),m.scrollToOption(D,"auto")),m.setActiveOption(D)}else m.clearActiveOption(),e&&m.isOpen&&m.close(!1)}selectable(){return this.dropdown_content.querySelectorAll("[data-selectable]")}addOption(e,t=!1){let i=this;if(Array.isArray(e))return i.addOptions(e,t),!1;let r=Fe(e[i.settings.valueField]);return r===null||i.options.hasOwnProperty(r)?(i.updateOption(e[i.settings.valueField],e),!1):(e.$order=e.$order||++i.order,e.$id=i.inputId+"-opt-"+e.$order,i.options[r]=e,i.isDropdownContentStale=!0,t&&(i.userOptions[r]=t,i.trigger("option_add",r,e)),r)}addOptions(e,t=!1){ve(e,i=>{this.addOption(i,t)})}registerOption(e){return this.addOption(e)}registerOptionGroup(e){var t=Fe(e[this.settings.optgroupValueField]);return t===null?!1:(e.$order=e.$order||++this.order,this.optgroups[t]=e,t)}addOptionGroup(e,t){var i;t[this.settings.optgroupValueField]=e,(i=this.registerOptionGroup(t))&&this.trigger("optgroup_add",i,t)}removeOptionGroup(e){this.optgroups.hasOwnProperty(e)&&(delete this.optgroups[e],this.clearCache(),this.trigger("optgroup_remove",e))}clearOptionGroups(){this.optgroups={},this.clearCache(),this.trigger("optgroup_clear")}updateOption(e,t){let i=this;var r,o;let s=Fe(e),a=Fe(t[i.settings.valueField]);if(s===null)return;let l=i.options[s];if(l==null)return;if(typeof a!="string")throw new Error("Value must be set in option data");let c=i.getOption(s),u=i.getItem(s);if(t.$order=t.$order||l.$order,delete i.options[s],i.uncacheValue(a),i.options[a]=t,c){if(i.dropdown_content.contains(c)){let d=i._render("option",t);fi(c,d),i.activeOption===c&&i.setActiveOption(d)}c.remove()}u&&(o=i.items.indexOf(s),o!==-1&&i.items.splice(o,1,a),r=i._render("item",t),u.classList.contains("active")&&$e(r,"active"),fi(u,r)),i.isDropdownContentStale=!0}removeOption(e,t){let i=this;e=ui(e),i.uncacheValue(e),delete i.userOptions[e],delete i.options[e],i.isDropdownContentStale=!0,i.trigger("option_remove",e),i.removeItem(e,t)}clearOptions(e){let t=(e||this.clearFilter).bind(this);this.loadedSearches={},this.userOptions={},this.clearCache();let i={};ve(this.options,(r,o)=>{t(r,o)&&(i[o]=r)}),this.options=this.sifter.items=i,this.isDropdownContentStale=!0,this.trigger("option_clear")}clearFilter(e,t){return this.items.indexOf(t)>=0}getOption(e,t=!1){let i=Fe(e);if(i===null)return null;let r=this.options[i];if(r!=null){if(r.$div)return r.$div;if(t)return this._render("option",r)}return null}getAdjacent(e,t,i="option"){var r=this,o;if(!e)return null;i=="item"?o=r.controlChildren():o=r.dropdown_content.querySelectorAll("[data-selectable]");for(let s=0;s0?o[s+1]:o[s-1];return null}getItem(e){if(typeof e=="object")return e;var t=Fe(e);return t!==null?this.control.querySelector(`[data-value="${zo(t)}"]`):null}addItems(e,t){var i=this,r=Array.isArray(e)?e:[e];r=r.filter(s=>i.items.indexOf(s)===-1);let o=r[r.length-1];r.forEach(s=>{i.isPending=s!==o,i.addItem(s,t)})}addItem(e,t){var i=t?[]:["change","dropdown_close"];Vo(this,i,()=>{var r,o;let s=this,a=s.settings.mode,l=Fe(e);if(!(l&&s.items.indexOf(l)!==-1&&(a==="single"&&s.close(),a==="single"||!s.settings.duplicates))&&!(l===null||!s.options.hasOwnProperty(l))&&(a==="single"&&s.clear(t),!(a==="multi"&&s.isFull()))){if(r=s._render("item",s.options[l]),s.control.contains(r)&&(r=r.cloneNode(!0)),o=s.isFull(),s.items.splice(s.caretPos,0,l),s.insertAtCaret(r),s.isSetup){if(!s.isPending&&s.settings.hideSelected){let c=s.getOption(l),u=s.getAdjacent(c,1);u&&s.setActiveOption(u)}s.settings.clearAfterSelect&&s.setTextboxValue(),!s.isPending&&!s.settings.closeAfterSelect&&s.refreshOptions(s.isFocused&&a!=="single"),s.settings.closeAfterSelect!=!1&&s.isFull()?s.close():s.isPending||s.positionDropdown(),s.trigger("item_add",l,r),s.isPending||s.updateOriginalInput({silent:t})}(!s.isPending||!o&&s.isFull())&&(s.inputState(),s.refreshState())}})}removeItem(e=null,t){let i=this;if(e=i.getItem(e),!e)return;var r,o;let s=e.dataset.value;r=Wo(e),e.remove(),e.classList.contains("active")&&(o=i.activeItems.indexOf(e),i.activeItems.splice(o,1),gt(e,"active")),i.items.splice(r,1),i.isDropdownContentStale=!0,!i.settings.persist&&i.userOptions.hasOwnProperty(s)&&i.removeOption(s,t),r{}){arguments.length===3&&(t=arguments[2]),typeof t!="function"&&(t=()=>{});var i=this,r=i.caretPos,o;if(e=e||i.inputValue(),!i.canCreate(e))return Fe(e)&&this.options[e]&&i.addItem(e),t(),!1;i.lock();var s=!1,a=l=>{if(i.unlock(),!l||typeof l!="object")return t();var c=Fe(l[i.settings.valueField]);if(typeof c!="string")return t();i.setTextboxValue(),i.addOption(l,!0),i.setCaret(r),i.addItem(c),t(l),s=!0};return typeof i.settings.create=="function"?o=i.settings.create.call(this,e,a):o={[i.settings.labelField]:e,[i.settings.valueField]:e},s||a(o),!0}refreshItems(){var e=this;e.isDropdownContentStale=!0,e.isSetup&&e.addItems(e.items),e.updateOriginalInput(),e.refreshState()}refreshState(){let e=this;e.refreshValidityState();let t=e.isFull(),i=e.isLocked;e.wrapper.classList.toggle("rtl",e.rtl);let r=e.wrapper.classList;r.toggle("focus",e.isFocused),r.toggle("disabled",e.isDisabled),r.toggle("readonly",e.isReadOnly),r.toggle("required",e.isRequired),r.toggle("invalid",!e.isValid),r.toggle("locked",i),r.toggle("full",t),r.toggle("input-active",e.isFocused&&!e.isInputHidden),r.toggle("dropdown-active",e.isOpen),r.toggle("has-options",Za(e.options)),r.toggle("has-items",e.items.length>0)}refreshValidityState(){var e=this;e.input.validity&&(e.isValid=e.input.validity.valid,e.isInvalid=!e.isValid)}isFull(){return this.settings.maxItems!==null&&this.items.length>=this.settings.maxItems}updateOriginalInput(e={}){let t=this;var i,r;let o=t.input.querySelector('option[value=""]');if(t.is_select_tag){let l=function(c,u,d){return c||(c=nt('")),c!=o&&t.input.append(c),s.push(c),(c!=o||a>0)&&(c.selected=!0),c},s=[],a=t.input.querySelectorAll("option:checked").length;t.input.querySelectorAll("option:checked").forEach(c=>{c.selected=!1}),t.items.length==0&&t.settings.mode=="single"?l(o,"",""):t.items.forEach(c=>{if(i=t.options[c],r=i[t.settings.labelField]||"",s.includes(i.$option)){let u=t.input.querySelector(`option[value="${zo(c)}"]:not(:checked)`);l(u,c,r)}else i.$option=l(i.$option,c,r)})}else t.input.value=t.getValue();t.isSetup&&(e.silent||t.trigger("change",t.getValue()))}open(){var e=this;e.isLocked||e.isOpen||e.settings.mode==="multi"&&e.isFull()||(e.isOpen=!0,le(e.focus_node,{"aria-expanded":"true"}),e.refreshState(),di(e.dropdown,{visibility:"hidden",display:"block"}),e.positionDropdown(),di(e.dropdown,{visibility:"visible",display:"block"}),e.focus(),e.trigger("dropdown_open",e.dropdown))}close(e=!0){var t=this,i=t.isOpen;e&&(t.setTextboxValue(),t.settings.mode==="single"&&t.items.length&&t.inputState()),t.isOpen=!1,le(t.focus_node,{"aria-expanded":"false"}),di(t.dropdown,{display:"none"}),t.settings.hideSelected&&t.clearActiveOption(),t.refreshState(),i&&t.trigger("dropdown_close",t.dropdown)}positionDropdown(){if(this.settings.dropdownParent==="body"){var e=this.control,t=e.getBoundingClientRect(),i=e.offsetHeight+t.top+window.scrollY,r=t.left+window.scrollX;di(this.dropdown,{width:t.width+"px",top:i+"px",left:r+"px"})}}clear(e){var t=this;if(t.items.length){var i=t.controlChildren();ve(i,r=>{t.removeItem(r,!0)}),t.inputState(),e||t.updateOriginalInput(),t.trigger("clear")}}insertAtCaret(e){let t=this,i=t.caretPos,r=t.control;r.insertBefore(e,r.children[i]||null),t.setCaret(i+1)}deleteSelection(e){var t,i,r,o,s=this;t=e&&e.keyCode===8?-1:1,i=Ka(s.control_input);let a=[];if(s.activeItems.length)o=qo(s.activeItems,t),r=Wo(o),t>0&&r++,ve(s.activeItems,l=>a.push(l));else if((s.isFocused||s.settings.mode==="single")&&s.items.length){let l=s.controlChildren(),c;t<0&&i.start===0&&i.length===0?c=l[s.caretPos-1]:t>0&&i.start===s.inputValue().length&&(c=l[s.caretPos]),c!==void 0&&a.push(c)}if(!s.shouldDelete(a,e))return!1;for(fe(e,!0),typeof r!="undefined"&&s.setCaret(r);a.length;)s.removeItem(a.pop());return s.inputState(),s.positionDropdown(),s.refreshOptions(!1),!0}shouldDelete(e,t){let i=e.map(r=>r.dataset.value);return!(!i.length||typeof this.settings.onDelete=="function"&&this.settings.onDelete.call(this,i,t)===!1)}advanceSelection(e,t){var i,r,o=this;o.rtl&&(e*=-1),!o.inputValue().length&&(Mt(hi,t)||Mt("shiftKey",t)?(i=o.getLastActive(e),i?i.classList.contains("active")?r=o.getAdjacent(i,e,"item"):r=i:e>0?r=o.control_input.nextElementSibling:r=o.control_input.previousElementSibling,r&&(r.classList.contains("active")&&o.removeActiveItem(i),o.setActiveItemClass(r))):o.moveCaret(e))}moveCaret(e){}getLastActive(e){let t=this.control.querySelector(".last-active");if(t)return t;var i=this.control.querySelectorAll(".active");if(i)return qo(i,e)}setCaret(e){this.caretPos=this.items.length}controlChildren(){return Array.from(this.control.querySelectorAll("[data-ts-item]"))}lock(){this.setLocked(!0)}unlock(){this.setLocked(!1)}setLocked(e=this.isReadOnly||this.isDisabled){this.isLocked=e,this.refreshState()}disable(){this.setDisabled(!0),this.close()}enable(){this.setDisabled(!1)}setDisabled(e){this.focus_node.tabIndex=e?-1:this.tabIndex,this.isDisabled=e,this.input.disabled=e,this.control_input.disabled=e,this.setLocked()}setReadOnly(e){this.isReadOnly=e,this.input.readOnly=e,this.control_input.readOnly=e,this.setLocked()}destroy(){var e=this,t=e.revertSettings;e.trigger("destroy"),e.off(),e.wrapper.remove(),e.dropdown.remove(),e.input.innerHTML=t.innerHTML,e.input.tabIndex=t.tabIndex,gt(e.input,"tomselected","ts-hidden-accessible"),e._destroy(),delete e.input.tomselect}render(e,t){var i,r;let o=this;if(typeof this.settings.render[e]!="function"||(r=o.settings.render[e].call(this,t,dr),!r))return null;if(r=nt(r),e==="option"||e==="option_create"?t[o.settings.disabledField]?le(r,{"aria-disabled":"true"}):le(r,{"data-selectable":""}):e==="optgroup"&&(i=t.group[o.settings.optgroupValueField],le(r,{"data-group":i}),t.group[o.settings.disabledField]&&le(r,{"data-disabled":""})),e==="option"||e==="item"){let s=ui(t[o.settings.valueField]);le(r,{"data-value":s}),e==="item"?($e(r,o.settings.itemClass),le(r,{"data-ts-item":""})):($e(r,o.settings.optionClass),le(r,{role:"option",id:t.$id}),t.$div=r,o.options[s]=t)}return r}_render(e,t){let i=this.render(e,t);if(i==null)throw"HTMLElement expected";return i}clearCache(){ve(this.options,e=>{e.$div&&(e.$div.remove(),delete e.$div)})}uncacheValue(e){let t=this.getOption(e);t&&t.remove()}canCreate(e){return this.settings.create&&e.length>0&&this.settings.createFilter.call(this,e)}hook(e,t,i){var r=this,o=r[t];r[t]=function(){var s,a;return e==="after"&&(s=o.apply(r,arguments)),a=i.apply(r,arguments),e==="instead"?a:(e==="before"&&(s=o.apply(r,arguments)),s)}}};var Sm=(n,e,t,i)=>{n.addEventListener(e,t,i)};function ol(){Sm(this.input,"change",()=>{this.sync()})}var Am=n=>typeof n=="undefined"||n===null?null:Dm(n),Dm=n=>typeof n=="boolean"?n?"1":"0":n+"",sl=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},Om=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(Lm(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},Lm=n=>typeof n=="string"&&n.indexOf("<")>-1;function al(n){var e=this,t=e.onOptionSelect;e.settings.hideSelected=!1;let i=Object.assign({className:"tomselect-checkbox",checkedClassNames:void 0,uncheckedClassNames:void 0},n);var r=function(a,l){l?(a.checked=!0,i.uncheckedClassNames&&a.classList.remove(...i.uncheckedClassNames),i.checkedClassNames&&a.classList.add(...i.checkedClassNames)):(a.checked=!1,i.checkedClassNames&&a.classList.remove(...i.checkedClassNames),i.uncheckedClassNames&&a.classList.add(...i.uncheckedClassNames))},o=function(a){setTimeout(()=>{var l=a.querySelector("input."+i.className);l instanceof HTMLInputElement&&r(l,a.classList.contains("selected"))},1)};e.hook("after","setupTemplates",()=>{var s=e.settings.render.option;e.settings.render.option=(a,l)=>{var c=Om(s.call(e,a,l)),u=document.createElement("input");i.className&&u.classList.add(i.className),u.addEventListener("click",function(p){sl(p)}),u.type="checkbox";let d=Am(a[e.settings.valueField]);return r(u,!!(d&&e.items.indexOf(d)>-1)),c.prepend(u),c}}),e.on("item_remove",s=>{var a=e.getOption(s);a&&(a.classList.remove("selected"),o(a))}),e.on("item_add",s=>{var a=e.getOption(s);a&&o(a)}),e.hook("instead","onOptionSelect",(s,a)=>{if(a.classList.contains("selected")){a.classList.remove("selected"),e.removeItem(a.dataset.value),e.refreshOptions(),sl(s,!0);return}t.call(e,s,a),o(a)})}var Mm=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(Nm(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},Nm=n=>typeof n=="string"&&n.indexOf("<")>-1;function ll(n){let e=this,t=Object.assign({className:"clear-button",title:"Clear All",role:"button",tabindex:0,html:i=>`
×
`},n);e.on("initialize",()=>{var i=Mm(t.html(t));i.addEventListener("click",r=>{e.isLocked||(e.clear(),e.settings.mode==="single"&&e.settings.allowEmptyOption&&e.addItem(""),e.refreshOptions(!1),r.preventDefault(),r.stopPropagation())}),e.control.appendChild(i)})}var km=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},On=(n,e,t,i)=>{n.addEventListener(e,t,i)},Hm=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},Rm=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(Im(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},Im=n=>typeof n=="string"&&n.indexOf("<")>-1,Pm=(n,e)=>{Hm(e,(t,i)=>{t==null?n.removeAttribute(i):n.setAttribute(i,""+t)})},Fm=(n,e)=>{var t;(t=n.parentNode)==null||t.insertBefore(e,n.nextSibling)},$m=(n,e)=>{var t;(t=n.parentNode)==null||t.insertBefore(e,n)},Bm=(n,e)=>{do{var t;if(e=(t=e)==null?void 0:t.previousElementSibling,n==e)return!0}while(e&&e.previousElementSibling);return!1};function cl(){var n=this;if(n.settings.mode!=="multi")return;var e=n.lock,t=n.unlock;let i=!0,r;n.hook("after","setupTemplates",()=>{var o=n.settings.render.item;n.settings.render.item=(s,a)=>{let l=Rm(o.call(n,s,a));Pm(l,{draggable:"true"});let c=v=>{i||km(v),v.stopPropagation()},u=v=>{r=l,setTimeout(()=>{l.classList.add("ts-dragging")},0)},d=v=>{v.preventDefault(),l.classList.add("ts-drag-over"),E(l,r)},p=()=>{l.classList.remove("ts-drag-over")},E=(v,_)=>{_!==void 0&&(Bm(_,l)?Fm(v,_):$m(v,_))},m=()=>{var v;document.querySelectorAll(".ts-drag-over").forEach(C=>C.classList.remove("ts-drag-over")),(v=r)==null||v.classList.remove("ts-dragging"),r=void 0;var _=[];n.control.querySelectorAll("[data-value]").forEach(C=>{if(C.dataset.value){let D=C.dataset.value;D&&_.push(D)}}),n.setValue(_)};return On(l,"mousedown",c),On(l,"dragstart",u),On(l,"dragenter",d),On(l,"dragover",d),On(l,"dragleave",p),On(l,"dragend",m),l}}),n.hook("instead","lock",()=>(i=!1,e.call(n))),n.hook("instead","unlock",()=>(i=!0,t.call(n)))}var Vm=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},zm=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(jm(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},jm=n=>typeof n=="string"&&n.indexOf("<")>-1;function ul(n){let e=this,t=Object.assign({title:"Untitled",headerClass:"dropdown-header",titleRowClass:"dropdown-header-title",labelClass:"dropdown-header-label",closeClass:"dropdown-header-close",html:i=>'
'+i.title+'×
'},n);e.on("initialize",()=>{var i=zm(t.html(t)),r=i.querySelector("."+t.closeClass);r&&r.addEventListener("click",o=>{Vm(o,!0),e.close()}),e.dropdown.insertBefore(i,e.dropdown.firstChild)})}var qm=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},Wm=(n,...e)=>{var t=Um(e);n=Ym(n),n.map(i=>{t.map(r=>{i.classList.remove(r)})})},Um=n=>{var e=[];return qm(n,t=>{typeof t=="string"&&(t=t.trim().split(/[\t\n\f\r\s]/)),Array.isArray(t)&&(e=e.concat(t))}),e.filter(Boolean)},Ym=n=>(Array.isArray(n)||(n=[n]),n),Gm=(n,e)=>{if(!n)return-1;e=e||n.nodeName;for(var t=0;n=n.previousElementSibling;)n.matches(e)&&t++;return t};function dl(){var n=this;n.hook("instead","setCaret",e=>{n.settings.mode==="single"||!n.control.contains(n.control_input)?e=n.items.length:(e=Math.max(0,Math.min(n.items.length,e)),e!=n.caretPos&&!n.isPending&&n.controlChildren().forEach((t,i)=>{i{if(!n.isFocused)return;let t=n.getLastActive(e);if(t){let i=Gm(t);n.setCaret(e>0?i+1:i),n.setActiveItem(),Wm(t,"last-active")}else n.setCaret(n.caretPos+e)})}var Km=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},Xm=(n,e,t,i)=>{n.addEventListener(e,t,i)},Qm=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},fl=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(Jm(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},Jm=n=>typeof n=="string"&&n.indexOf("<")>-1,Zm=(n,...e)=>{var t=eg(e);n=tg(n),n.map(i=>{t.map(r=>{i.classList.add(r)})})},eg=n=>{var e=[];return Qm(n,t=>{typeof t=="string"&&(t=t.trim().split(/[\t\n\f\r\s]/)),Array.isArray(t)&&(e=e.concat(t))}),e.filter(Boolean)},tg=n=>(Array.isArray(n)||(n=[n]),n);function hl(){let n=this;n.settings.shouldOpen=!0,n.hook("before","setup",()=>{var e;n.focus_node=n.control,Zm(n.control_input,"dropdown-input");let t=fl('