diff --git a/.github/ISSUE_TEMPLATE/01-feature_request.yaml b/.github/ISSUE_TEMPLATE/01-feature_request.yaml index f0138a22a..ec755cd0c 100644 --- a/.github/ISSUE_TEMPLATE/01-feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/01-feature_request.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v4.1.4 + placeholder: v4.1.7 validations: required: true - type: dropdown @@ -36,9 +36,8 @@ body: options: - I volunteer to perform this work (if approved) - I'm a NetBox Labs customer - - This is a very minor change - N/A - default: 3 + default: 2 validations: required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/02-bug_report.yaml b/.github/ISSUE_TEMPLATE/02-bug_report.yaml index a8a2cc45b..3ae3cbd33 100644 --- a/.github/ISSUE_TEMPLATE/02-bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/02-bug_report.yaml @@ -31,16 +31,15 @@ body: options: - I volunteer to perform this work (if approved) - I'm a NetBox Labs customer - - This is preventing me from using NetBox - N/A - default: 3 + default: 2 validations: required: true - type: input attributes: label: NetBox Version description: What version of NetBox are you currently running? - placeholder: v4.1.4 + placeholder: v4.1.7 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 2ad52023e..efbf38932 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -7,6 +7,9 @@ contact_links: - name: ❓ Discussion url: https://github.com/netbox-community/netbox/discussions about: "If you're just looking for help, try starting a discussion instead." + - name: 👔 Professional Support + url: https://netboxlabs.com/netbox-enterprise/ + about: "Professional support is available for NetBox Enterprise or Cloud." - name: 🌎 Correct a Translation url: https://explore.transifex.com/netbox-community/netbox/ about: "Spot an incorrect translation? You can propose a fix on Transifex." diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d77da90e3..aab8bc34f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,11 @@ on: permissions: contents: read +# Add concurrency group to control job running +concurrency: + group: ${{ github.event_name }}-${{ github.ref }}-${{ github.actor }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest diff --git a/.tx/config b/.tx/config new file mode 100755 index 000000000..342331d4e --- /dev/null +++ b/.tx/config @@ -0,0 +1,12 @@ +[main] +host = https://app.transifex.com + +[o:netbox-community:p:netbox:r:9cbf4fcf95b3d92e4ebbf1a5e5d1caee] +file_filter = netbox/translations//LC_MESSAGES/django.po +source_file = netbox/translations/en/LC_MESSAGES/django.po +type = PO +minimum_perc = 0 +resource_name = django.po +replace_edited_strings = false +keep_translations = false + diff --git a/README.md b/README.md index 0c793b8a4..e3829c2cc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
- NetBox logo + NetBox logo

The cornerstone of every automated network

Latest release License diff --git a/base_requirements.txt b/base_requirements.txt index 76955a6e1..7a8f04a98 100644 --- a/base_requirements.txt +++ b/base_requirements.txt @@ -1,6 +1,6 @@ # The Python web framework on which NetBox is built # https://docs.djangoproject.com/en/stable/releases/ -Django<5.1 +Django<5.2 # Django middleware which permits cross-domain API requests # https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst @@ -116,6 +116,10 @@ PyYAML # https://github.com/psf/requests/blob/main/HISTORY.md requests +# rq +# https://github.com/rq/rq/blob/master/CHANGES.md +rq + # Social authentication framework # https://github.com/python-social-auth/social-core/blob/master/CHANGELOG.md social-auth-core diff --git a/contrib/generated_schema.json b/contrib/generated_schema.json index 56ddee50e..639f0df8d 100644 --- a/contrib/generated_schema.json +++ b/contrib/generated_schema.json @@ -329,6 +329,7 @@ "100base-tx", "100base-t1", "1000base-t", + "1000base-lx", "1000base-tx", "2.5gbase-t", "5gbase-t", diff --git a/docs/administration/authentication/google.md b/docs/administration/authentication/google.md new file mode 100644 index 000000000..456f3a457 --- /dev/null +++ b/docs/administration/authentication/google.md @@ -0,0 +1,52 @@ +# Google + +This guide explains how to configure single sign-on (SSO) support for NetBox using [Google OAuth2](https://developers.google.com/identity/protocols/oauth2/web-server) as an authentication backend. + +## Google OAuth2 Configuration + +1. Log into [console.cloud.google.com](https://console.cloud.google.com/). +2. Create new project for NetBox. +3. Under "APIs and Services" click "OAuth consent screen" and enter the required information. +4. Under "Credentials," click "Create Credentials" and select "OAuth 2.0 Client ID." Select type "Web application." + - "Authorized JavaScript origins" should follow the format `http[s]://[:]` + - "Authorized redirect URIs" should follow the format `http[s]://[:]/oauth/complete/google-oauth2/` +5. Copy the "Client ID" and "Client Secret" values somewhere convenient. + +!!! note + Google requires the NetBox hostname to use a public top-level-domain (e.g. `.com`, `.net`). The use of IP addresses is not permitted (except `127.0.0.1`). + +For more information, consult [Google's documentation](https://developers.google.com/identity/protocols/oauth2/web-server#prerequisites). + +## NetBox Configuration + +### 1. Enter configuration parameters + +Enter the following configuration parameters in `configuration.py`, substituting your own values: + +```python +REMOTE_AUTH_BACKEND = 'social_core.backends.google.GoogleOAuth2' +SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '{CLIENT_ID}' +SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = '{CLIENT_SECRET}' +``` + +### 2. Restart NetBox + +Restart the NetBox services so that the new configuration takes effect. This is typically done with the command below: + +```no-highlight +sudo systemctl restart netbox +``` + +## Testing + +Log out of NetBox if already authenticated, and click the "Log In" button at top right. You should see the normal login form as well as an option to authenticate using Google. Click that link. + +![NetBox Google login form](../../media/authentication/netbox_google_login.png) + +You should be redirected to Google's authentication portal. Enter the username/email and password of your test account to continue. You may also be prompted to grant this application access to your account. + +![NetBox Google login form](../../media/authentication/google_login_portal.png) + +If successful, you will be redirected back to the NetBox UI, and will be logged in as the Google user. You can verify this by navigating to your profile (using the button at top right). + +This user account has been replicated locally to NetBox, and can now be assigned groups and permissions. diff --git a/docs/administration/authentication/microsoft-entra-id.md b/docs/administration/authentication/microsoft-entra-id.md index 3451c656f..b44499fbe 100644 --- a/docs/administration/authentication/microsoft-entra-id.md +++ b/docs/administration/authentication/microsoft-entra-id.md @@ -16,7 +16,7 @@ Under the Azure Active Directory dashboard, navigate to **Add > App registration Enter a name for the registration (e.g. "NetBox") and ensure that the "single tenant" option is selected. -Under "Redirect URI", select "Web" for the platform and enter the path to your NetBox installation, ending with `/oauth/complete/entraid-oauth2/`. Note that this URI **must** begin with `https://` unless you are referencing localhost (for development purposes). +Under "Redirect URI", select "Web" for the platform and enter the path to your NetBox installation, ending with `/oauth/complete/azuread-oauth2/`. Note that this URI **must** begin with `https://` unless you are referencing localhost (for development purposes). ![App registration parameters](../../media/authentication/azure_ad_app_registration.png) diff --git a/docs/configuration/miscellaneous.md b/docs/configuration/miscellaneous.md index 576eb8739..c14c0ac77 100644 --- a/docs/configuration/miscellaneous.md +++ b/docs/configuration/miscellaneous.md @@ -106,6 +106,16 @@ By default, NetBox will prevent the creation of duplicate prefixes and IP addres --- +## EVENTS_PIPELINE + +!!! info "This parameter was introduced in NetBox v4.2." + +Default: `['extras.events.process_event_queue',]` + +NetBox will call dotted paths to the functions listed here for events (create, update, delete) on models as well as when custom EventRules are fired. + +--- + ## FILE_UPLOAD_MAX_MEMORY_SIZE Default: `2621440` (2.5 MB) diff --git a/docs/configuration/system.md b/docs/configuration/system.md index 25c724bc9..af3a6f5e6 100644 --- a/docs/configuration/system.md +++ b/docs/configuration/system.md @@ -89,8 +89,6 @@ addresses (and [`DEBUG`](./development.md#debug) is true). ## ISOLATED_DEPLOYMENT -!!! info "This feature was introduced in NetBox v4.1." - Default: False Set this configuration parameter to True for NetBox deployments which do not have Internet access. This will disable miscellaneous functionality which depends on access to the Internet. diff --git a/docs/customization/custom-scripts.md b/docs/customization/custom-scripts.md index 3fa6491d2..1051b31f6 100644 --- a/docs/customization/custom-scripts.md +++ b/docs/customization/custom-scripts.md @@ -72,6 +72,9 @@ script_order = (MyCustomScript, AnotherCustomScript) Script attributes are defined under a class named `Meta` within the script. These are optional, but encouraged. +!!! warning + These are also defined and used as properties on the base custom script class, so don't use the same names as variables or override them in your custom script. + ### `name` This is the human-friendly names of your script. If omitted, the class name will be used. diff --git a/docs/development/adding-models.md b/docs/development/adding-models.md index f3aa9cfcc..0bf020662 100644 --- a/docs/development/adding-models.md +++ b/docs/development/adding-models.md @@ -8,7 +8,7 @@ Each model should define, at a minimum: * A `Meta` class specifying a deterministic ordering (if ordered by fields other than the primary ID) * A `__str__()` method returning a user-friendly string representation of the instance -* A `get_absolute_url()` method returning an instance's direct URL (using `reverse()`) +* A `get_absolute_url()` method if necessary; a standard version of the method is defined in the `NetBoxFeatureSet` base class, but you will need to provide your own (returning an instance's direct URL using `reverse()`) if not subclassing that base class ## 2. Define field choices @@ -78,6 +78,8 @@ Create the following for each model: Create a GraphQL object type for the model in `graphql/types.py` by subclassing the appropriate class from `netbox.graphql.types`. +**Note:** GraphQL unit tests may fail citing null values on a non-nullable field if related objects are prefetched. You may need to fix this by setting the type annotation to be `= strawberry_django.field(select_related=["policy"])` or similar. + Also extend the schema class defined in `graphql/schema.py` with the individual object and object list fields per the established convention. ## 14. Add tests diff --git a/docs/development/release-checklist.md b/docs/development/release-checklist.md index 7c8c96f39..4e5fdeca8 100644 --- a/docs/development/release-checklist.md +++ b/docs/development/release-checklist.md @@ -90,7 +90,20 @@ This will automatically update the schema file at `contrib/generated_schema.json ### Update & Compile Translations -Updated language translations should be pulled from [Transifex](https://app.transifex.com/netbox-community/netbox/dashboard/) and re-compiled for each new release. Follow the documented process for [updating translated strings](./translations.md#updating-translated-strings) to do this. +Updated language translations should be pulled from [Transifex](https://app.transifex.com/netbox-community/netbox/dashboard/) and re-compiled for each new release. First, retrieve any updated translation files using the Transifex CLI client: + +```no-highlight +tx pull +``` + +Then, compile these portable (`.po`) files for use in the application: + +```no-highlight +./manage.py compilemessages +``` + +!!! tip + Consult the translation documentation for more detail on [updating translated strings](./translations.md#updating-translated-strings) if you've not set up the Transifex client already. ### Update Version and Changelog diff --git a/docs/development/style-guide.md b/docs/development/style-guide.md index 0d4caf395..9d6630de0 100644 --- a/docs/development/style-guide.md +++ b/docs/development/style-guide.md @@ -76,4 +76,4 @@ When adding a new dependency, a short description of the package and the URL of * When referring to NetBox in writing, use the proper form "NetBox," with the letters N and B capitalized. The lowercase form "netbox" should be used in code, filenames, etc. but never "Netbox" or any other deviation. -* There is an SVG form of the NetBox logo at [docs/netbox_logo.svg](../netbox_logo.svg). It is preferred to use this logo for all purposes as it scales to arbitrary sizes without loss of resolution. If a raster image is required, the SVG logo should be converted to a PNG image of the prescribed size. +* There are SVG forms of the NetBox logo for both [light mode](../netbox_logo_light.svg) and [dark mode](../netbox_logo_dark.svg) available. It is preferred to use the SVG logo for all purposes as it scales to arbitrary sizes without loss of resolution. If a raster image is required, the SVG logo should be converted to a PNG image of the desired size. diff --git a/docs/development/translations.md b/docs/development/translations.md index eca9ce71f..43733c6d1 100644 --- a/docs/development/translations.md +++ b/docs/development/translations.md @@ -16,26 +16,31 @@ To update the English `.po` file from which all translations are derived, use th Then, commit the change and push to the `develop` branch on GitHub. Any new strings will appear for translation on Transifex automatically. +!!! note + It is typically not necessary to update source strings manually, as this is done nightly by a [GitHub action](https://github.com/netbox-community/netbox/blob/develop/.github/workflows/update-translation-strings.yml). + ## Updating Translated Strings Typically, translated strings need to be updated only as part of the NetBox [release process](./release-checklist.md). Check the Transifex dashboard for languages that are not marked _ready for use_, being sure to click _Show all languages_ if it appears at the bottom of the list. Use machine translation to round out any not-ready languages. It's not necessary to review the machine translation immediately as the translation teams will handle that aspect; the goal at this stage is to get translations included in the Transifex pull request. -To update translated strings, start by initiating a sync from Transifex. From the Transifex dashboard, navigate to Settings > Integrations > GitHub > Manage, and click the **Manual Sync** button at top right. +To download translated strings automatically, you'll need to: -![Transifex manual sync](../media/development/transifex_sync.png) +1. Install the [Transifex CLI client](https://github.com/transifex/cli) +2. Generate a [Transifex API token](https://app.transifex.com/user/settings/api/) -Enter a threshold percentage of 1 (to ensure all translations are captured) and select the `develop` branch, then click **Sync**. This will initiate a pull request to GitHub to update any newly modified translation (`.po`) files. +Once you have the client set up, run the following command: -!!! tip - The new PR should appear within a few minutes. If it does not, check that there are in fact new translations to be added. +```no-highlight +TX_TOKEN=$TOKEN tx pull +``` -![Transifex pull request](../media/development/transifex_pull_request.png) +This will download all portable (`.po`) translation files from Transifex, updating them locally as needed. -Once the PR has been merged, the updated strings need to be compiled into new `.mo` files so they can be used by the application. Update the `develop` branch locally to pull in the changes from the Transifex PR, then run Django's [`compilemessages`](https://docs.djangoproject.com/en/stable/ref/django-admin/#django-admin-compilemessages) management command: +Once retrieved, the updated strings need to be compiled into new `.mo` files so they can be used by the application. Run Django's [`compilemessages`](https://docs.djangoproject.com/en/stable/ref/django-admin/#django-admin-compilemessages) management command to compile them: -```nohighlight +```no-highlight ./manage.py compilemessages ``` diff --git a/docs/extra.css b/docs/extra.css index e953fa14c..4b8cd87fe 100644 --- a/docs/extra.css +++ b/docs/extra.css @@ -5,6 +5,10 @@ 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/notifications.md b/docs/features/notifications.md index a28a17947..0567a6db6 100644 --- a/docs/features/notifications.md +++ b/docs/features/notifications.md @@ -1,7 +1,5 @@ # Notifications -!!! info "This feature was introduced in NetBox v4.1." - NetBox includes a system for generating user notifications, which can be marked as read or deleted by individual users. There are two built-in mechanisms for generating a notification: * A user can subscribe to an object. When that object is modified, a notification is created to inform the user of the change. diff --git a/docs/index.md b/docs/index.md index 5ef650ca6..a79ab03b4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,5 @@ -![NetBox](netbox_logo.svg "NetBox logo"){style="height: 100px; margin-bottom: 3em"} +![NetBox](netbox_logo_light.svg#only-light "NetBox logo"){style="height: 100px; margin-bottom: 3em; background: none;"} +![NetBox](netbox_logo_dark.svg#only-dark "NetBox logo"){style="height: 100px; margin-bottom: 3em; background: none;"} # The Premier Network Source of Truth diff --git a/docs/media/authentication/google_login_portal.png b/docs/media/authentication/google_login_portal.png new file mode 100644 index 000000000..55eefd872 Binary files /dev/null and b/docs/media/authentication/google_login_portal.png differ diff --git a/docs/media/authentication/netbox_google_login.png b/docs/media/authentication/netbox_google_login.png new file mode 100644 index 000000000..730173b5d Binary files /dev/null and b/docs/media/authentication/netbox_google_login.png differ diff --git a/docs/media/development/transifex_pull_request.png b/docs/media/development/transifex_pull_request.png deleted file mode 100644 index e3ae76991..000000000 Binary files a/docs/media/development/transifex_pull_request.png and /dev/null differ diff --git a/docs/media/development/transifex_sync.png b/docs/media/development/transifex_sync.png deleted file mode 100644 index 44022cc4d..000000000 Binary files a/docs/media/development/transifex_sync.png and /dev/null differ diff --git a/docs/models/circuits/circuitgroup.md b/docs/models/circuits/circuitgroup.md index faa9dbc14..6d1503509 100644 --- a/docs/models/circuits/circuitgroup.md +++ b/docs/models/circuits/circuitgroup.md @@ -1,7 +1,5 @@ # Circuit Groups -!!! info "This feature was introduced in NetBox v4.1." - [Circuits](./circuit.md) can be arranged into administrative groups for organization. The assignment of a circuit to a group is optional. ## Fields diff --git a/docs/models/circuits/circuittermination.md b/docs/models/circuits/circuittermination.md index c6aa966d0..4b1a16ded 100644 --- a/docs/models/circuits/circuittermination.md +++ b/docs/models/circuits/circuittermination.md @@ -21,13 +21,11 @@ Designates the termination as forming either the A or Z end of the circuit. If selected, the circuit termination will be considered "connected" even if no cable has been connected to it in NetBox. -### Site +### Termination -The [site](../dcim/site.md) with which this circuit termination is associated. Once created, a cable can be connected between the circuit termination and a device interface (or similar component). +!!! info "This field replaced the `site` and `provider_network` fields in NetBox v4.2." -### Provider Network - -Circuits which do not connect to a site modeled by NetBox can instead be terminated to a [provider network](./providernetwork.md) representing an unknown network operated by a [provider](./provider.md). +The [region](../dcim/region.md), [site group](../dcim/sitegroup.md), [site](../dcim/site.md), [location](../dcim/location.md) or [provider network](./providernetwork.md) with which this circuit termination is associated. Once created, a cable can be connected between the circuit termination and a device interface (or similar component). ### Port Speed diff --git a/docs/models/circuits/virtualcircuit.md b/docs/models/circuits/virtualcircuit.md new file mode 100644 index 000000000..c81c654c8 --- /dev/null +++ b/docs/models/circuits/virtualcircuit.md @@ -0,0 +1,35 @@ +# Virtual Circuits + +!!! info "This feature was introduced in NetBox v4.2." + +A virtual circuit can connect two or more interfaces atop a set of decoupled physical connections. For example, it's very common to form a virtual connection between two virtual interfaces, each of which is bound to a physical interface on its respective device and physically connected to a [provider network](./providernetwork.md) via an independent [physical circuit](./circuit.md). + +## Fields + +### Provider Network + +The [provider network](./providernetwork.md) across which the virtual circuit is formed. + +### Provider Account + +The [provider account](./provideraccount.md) with which the virtual circuit is associated (if any). + +### Circuit ID + +The unique identifier assigned to the virtual circuit by its [provider](./provider.md). + +### Status + +The operational status of the virtual circuit. By default, the following statuses are available: + +| Name | +|----------------| +| Planned | +| Provisioning | +| Active | +| Offline | +| Deprovisioning | +| Decommissioned | + +!!! tip "Custom circuit statuses" + Additional circuit statuses may be defined by setting `Circuit.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter. diff --git a/docs/models/circuits/virtualcircuittermination.md b/docs/models/circuits/virtualcircuittermination.md new file mode 100644 index 000000000..a7833e13c --- /dev/null +++ b/docs/models/circuits/virtualcircuittermination.md @@ -0,0 +1,23 @@ +# Virtual Circuit Terminations + +!!! info "This feature was introduced in NetBox v4.2." + +This model represents the connection of a virtual [interface](../dcim/interface.md) to a [virtual circuit](./virtualcircuit.md). + +## Fields + +### Virtual Circuit + +The [virtual circuit](./virtualcircuit.md) to which the interface is connected. + +### Interface + +The [interface](../dcim/interface.md) connected to the virtual circuit. + +### Role + +The functional role of the termination. This depends on the virtual circuit's topology, which is typically either peer-to-peer or hub-and-spoke (multipoint). Valid choices include: + +* Peer +* Hub +* Spoke diff --git a/docs/models/dcim/interface.md b/docs/models/dcim/interface.md index 3667dabd5..b7115050f 100644 --- a/docs/models/dcim/interface.md +++ b/docs/models/dcim/interface.md @@ -45,9 +45,12 @@ The operation duplex (full, half, or auto). The [virtual routing and forwarding](../ipam/vrf.md) instance to which this interface is assigned. -### MAC Address +### Primary MAC Address -The 48-bit MAC address (for Ethernet interfaces). +The [MAC address](./macaddress.md) assigned to this interface which is designated as its primary. + +!!! note "Changed in NetBox v4.2" + The MAC address of an interface (formerly a concrete database field) is available as a property, `mac_address`, which reflects the value of the primary linked [MAC address](./macaddress.md) object. ### WWN @@ -109,6 +112,7 @@ For switched Ethernet interfaces, this identifies the 802.1Q encapsulation strat * **Access:** All traffic is assigned to a single VLAN, with no tagging. * **Tagged:** One untagged "native" VLAN is allowed, as well as any number of tagged VLANs. * **Tagged (all):** Implies that all VLANs are carried by the interface. One untagged VLAN may be designated. +* **Q-in-Q:** Q-in-Q (IEEE 802.1ad) encapsulation is performed using the assigned SVLAN. This field must be left blank for routed interfaces which do employ 802.1Q encapsulation. @@ -120,6 +124,12 @@ The "native" (untagged) VLAN for the interface. Valid only when one of the above The tagged VLANs which are configured to be carried by this interface. Valid only for the "tagged" 802.1Q mode above. +### Q-in-Q SVLAN + +!!! info "This field was introduced in NetBox v4.2." + +The assigned service VLAN (for Q-in-Q/802.1ad interfaces). + ### Wireless Role Indicates the configured role for wireless interfaces (access point or station). @@ -142,3 +152,9 @@ The configured channel width of a wireless interface, in MHz. This is typically ### Wireless LANs The [wireless LANs](../wireless/wirelesslan.md) for which this interface carries traffic. (Valid for wireless interfaces only.) + +### VLAN Translation Policy + +!!! info "This field was introduced in NetBox v4.2." + +The [VLAN translation policy](../ipam/vlantranslationpolicy.md) that applies to this interface (optional). diff --git a/docs/models/dcim/inventoryitem.md b/docs/models/dcim/inventoryitem.md index a6dfa32db..2d648341b 100644 --- a/docs/models/dcim/inventoryitem.md +++ b/docs/models/dcim/inventoryitem.md @@ -25,6 +25,12 @@ The inventory item's name. If the inventory item is assigned to a parent item, i An alternative physical label identifying the inventory item. +### Status + +!!! info "This field was introduced in NetBox v4.2." + +The inventory item's operational status. + ### Role The functional [role](./inventoryitemrole.md) assigned to this inventory item. @@ -44,7 +50,3 @@ The serial number assigned by the manufacturer. ### Asset Tag A unique, locally-administered label used to identify hardware resources. - -### Status - -The inventory item's operational status. diff --git a/docs/models/dcim/macaddress.md b/docs/models/dcim/macaddress.md new file mode 100644 index 000000000..5b1dd93be --- /dev/null +++ b/docs/models/dcim/macaddress.md @@ -0,0 +1,13 @@ +# MAC Addresses + +!!! info "This feature was introduced in NetBox v4.2." + +A MAC address object in NetBox comprises a single Ethernet link layer address, and represents a MAC address as reported by or assigned to a network interface. MAC addresses can be assigned to [device](../dcim/device.md) and [virtual machine](../virtualization/virtualmachine.md) interfaces. A MAC address can be specified as the primary MAC address for a given device or VM interface. + +Most interfaces have only a single MAC address, hard-coded at the factory. However, on some devices (particularly virtual interfaces) it is possible to assign additional MAC addresses or change existing ones. For this reason NetBox allows multiple MACAddress objects to be assigned to a single interface. + +## Fields + +### MAC Address + +The 48-bit MAC address, in colon-hexadecimal notation (e.g. `aa:bb:cc:11:22:33`). diff --git a/docs/models/dcim/modulebay.md b/docs/models/dcim/modulebay.md index 1bff799c2..494012a7b 100644 --- a/docs/models/dcim/modulebay.md +++ b/docs/models/dcim/modulebay.md @@ -16,8 +16,6 @@ The device to which this module bay belongs. ### Module -!!! info "This feature was introduced in NetBox v4.1." - The module to which this bay belongs (optional). ### Name diff --git a/docs/models/dcim/moduletype.md b/docs/models/dcim/moduletype.md index 225873d61..7077e16c2 100644 --- a/docs/models/dcim/moduletype.md +++ b/docs/models/dcim/moduletype.md @@ -42,6 +42,4 @@ The numeric weight of the module, including a unit designation (e.g. 3 kilograms ### Airflow -!!! info "The `airflow` field was introduced in NetBox v4.1." - The direction in which air circulates through the device chassis for cooling. diff --git a/docs/models/dcim/poweroutlet.md b/docs/models/dcim/poweroutlet.md index fe9390056..a99f60b23 100644 --- a/docs/models/dcim/poweroutlet.md +++ b/docs/models/dcim/poweroutlet.md @@ -31,6 +31,8 @@ The type of power outlet. ### Color +!!! info "This field was introduced in NetBox v4.2." + The power outlet's color (optional). ### Power Port diff --git a/docs/models/dcim/racktype.md b/docs/models/dcim/racktype.md index eeb90bd29..b5f2d99e7 100644 --- a/docs/models/dcim/racktype.md +++ b/docs/models/dcim/racktype.md @@ -1,7 +1,5 @@ # Rack Types -!!! info "This feature was introduced in NetBox v4.1." - A rack type defines the physical characteristics of a particular model of [rack](./rack.md). ## Fields diff --git a/docs/models/extras/customfield.md b/docs/models/extras/customfield.md index 9aab66a36..a5d083492 100644 --- a/docs/models/extras/customfield.md +++ b/docs/models/extras/customfield.md @@ -44,8 +44,6 @@ For object and multiple-object fields only. Designates the type of NetBox object ### Related Object Filter -!!! info "This field was introduced in NetBox v4.1." - For object and multi-object custom fields, a filter may be defined to limit the available objects when populating a field value. This filter maps object attributes to values. For example, `{"status": "active"}` will include only objects with a status of "active." !!! warning diff --git a/docs/models/ipam/prefix.md b/docs/models/ipam/prefix.md index 2fb01daf0..939ca3ea5 100644 --- a/docs/models/ipam/prefix.md +++ b/docs/models/ipam/prefix.md @@ -34,9 +34,11 @@ Designates whether the prefix should be treated as a pool. If selected, the firs If selected, this prefix will report 100% utilization regardless of how many child objects have been defined within it. -### Site +### Scope -The [site](../dcim/site.md) to which this prefix is assigned (optional). +!!! info "This field replaced the `site` field in NetBox v4.2." + +The [region](../dcim/region.md), [site](../dcim/site.md), [site group](../dcim/sitegroup.md) or [location](../dcim/location.md) to which the prefix is assigned (optional). ### VLAN diff --git a/docs/models/ipam/vlan.md b/docs/models/ipam/vlan.md index 2dd5ec2d3..3c90d8cc9 100644 --- a/docs/models/ipam/vlan.md +++ b/docs/models/ipam/vlan.md @@ -26,3 +26,15 @@ The user-defined functional [role](./role.md) assigned to the VLAN. ### VLAN Group or Site The [VLAN group](./vlangroup.md) or [site](../dcim/site.md) to which the VLAN is assigned. + +### Q-in-Q Role + +!!! info "This field was introduced in NetBox v4.2." + +For VLANs which comprise a Q-in-Q/IEEE 802.1ad topology, this field indicates whether the VLAN is treated as a service or customer VLAN. + +### Q-in-Q Service VLAN + +!!! info "This field was introduced in NetBox v4.2." + +The designated parent service VLAN for a Q-in-Q customer VLAN. This may be set only for Q-in-Q custom VLANs. diff --git a/docs/models/ipam/vlangroup.md b/docs/models/ipam/vlangroup.md index 20989452f..67050ab4c 100644 --- a/docs/models/ipam/vlangroup.md +++ b/docs/models/ipam/vlangroup.md @@ -16,8 +16,6 @@ A unique URL-friendly identifier. (This value can be used for filtering.) ### VLAN ID Ranges -!!! info "This field replaced the legacy `min_vid` and `max_vid` fields in NetBox v4.1." - 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. ### Scope diff --git a/docs/models/ipam/vlantranslationpolicy.md b/docs/models/ipam/vlantranslationpolicy.md new file mode 100644 index 000000000..9e3e8de98 --- /dev/null +++ b/docs/models/ipam/vlantranslationpolicy.md @@ -0,0 +1,28 @@ +# VLAN Translation Policies + +!!! info "This feature was introduced in NetBox v4.2." + +VLAN translation is a feature that consists of VLAN translation policies and [VLAN translation rules](./vlantranslationrule.md). Many rules can belong to a policy, and each rule defines a mapping of a local to remote VLAN ID (VID). A policy can then be assigned to an [Interface](../dcim/interface.md) or [VMInterface](../virtualization/vminterface.md), and all VLAN translation rules associated with that policy will be visible in the interface details. + +There are uniqueness constraints on `(policy, local_vid)` and on `(policy, remote_vid)` in the `VLANTranslationRule` model. Thus, you cannot have multiple rules linked to the same policy that have the same local VID or the same remote VID. A set of policies and rules might look like this: + +Policy 1: +- Rule: 100 -> 200 +- Rule: 101 -> 201 + +Policy 2: +- Rule: 100 -> 300 +- Rule: 101 -> 301 + +However this is not allowed: + +Policy 3: +- Rule: 100 -> 200 +- Rule: 100 -> 300 + + +## Fields + +### Name + +A unique human-friendly name. diff --git a/docs/models/ipam/vlantranslationrule.md b/docs/models/ipam/vlantranslationrule.md new file mode 100644 index 000000000..eb356d0d0 --- /dev/null +++ b/docs/models/ipam/vlantranslationrule.md @@ -0,0 +1,21 @@ +# VLAN Translation Rules + +!!! info "This feature was introduced in NetBox v4.2." + +A VLAN translation rule represents a one-to-one mapping of a local VLAN ID (VID) to a remote VID. Many rules can belong to a single policy. + +See [VLAN translation policies](./vlantranslationpolicy.md) for an overview of the VLAN Translation feature. + +## Fields + +### Policy + +The [VLAN Translation Policy](./vlantranslationpolicy.md) to which this rule belongs. + +### Local VID + +VLAN ID (1-4094) in the local network which is to be translated to a remote VID. + +### Remote VID + +VLAN ID (1-4094) in the remote network to which the local VID will be translated. diff --git a/docs/models/virtualization/cluster.md b/docs/models/virtualization/cluster.md index 50b5dbd1d..b9e6b608f 100644 --- a/docs/models/virtualization/cluster.md +++ b/docs/models/virtualization/cluster.md @@ -23,6 +23,8 @@ The cluster's operational status. !!! tip Additional statuses may be defined by setting `Cluster.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter. -### Site +### Scope -The [site](../dcim/site.md) with which the cluster is associated. +!!! info "This field replaced the `site` field in NetBox v4.2." + +The [region](../dcim/region.md), [site](../dcim/site.md), [site group](../dcim/sitegroup.md) or [location](../dcim/location.md) with which this cluster is associated. diff --git a/docs/models/virtualization/virtualmachine.md b/docs/models/virtualization/virtualmachine.md index 7ea31111c..a90b2752d 100644 --- a/docs/models/virtualization/virtualmachine.md +++ b/docs/models/virtualization/virtualmachine.md @@ -57,6 +57,4 @@ The amount of disk storage provisioned, in megabytes. ### Serial Number -!!! info "This field was introduced in NetBox v4.1." - Optional serial number assigned to this virtual machine. Unlike devices, uniqueness is not enforced for virtual machine serial numbers. diff --git a/docs/models/virtualization/vminterface.md b/docs/models/virtualization/vminterface.md index d923bdd5d..ba0c68b15 100644 --- a/docs/models/virtualization/vminterface.md +++ b/docs/models/virtualization/vminterface.md @@ -27,9 +27,12 @@ An interface on the same VM with which this interface is bridged. If not selected, this interface will be treated as disabled/inoperative. -### MAC Address +### Primary MAC Address -The 48-bit MAC address (for Ethernet interfaces). +The [MAC address](../dcim/macaddress.md) assigned to this interface which is designated as its primary. + +!!! note "Changed in NetBox v4.2" + The MAC address of an interface (formerly a concrete database field) is available as a property, `mac_address`, which reflects the value of the primary linked [MAC address](../dcim/macaddress.md) object. ### MTU @@ -42,6 +45,7 @@ For switched Ethernet interfaces, this identifies the 802.1Q encapsulation strat * **Access:** All traffic is assigned to a single VLAN, with no tagging. * **Tagged:** One untagged "native" VLAN is allowed, as well as any number of tagged VLANs. * **Tagged (all):** Implies that all VLANs are carried by the interface. One untagged VLAN may be designated. +* **Q-in-Q:** Q-in-Q (IEEE 802.1ad) encapsulation is performed using the assigned SVLAN. This field must be left blank for routed interfaces which do employ 802.1Q encapsulation. @@ -53,6 +57,18 @@ The "native" (untagged) VLAN for the interface. Valid only when one of the above The tagged VLANs which are configured to be carried by this interface. Valid only for the "tagged" 802.1Q mode above. +### Q-in-Q SVLAN + +!!! info "This field was introduced in NetBox v4.2." + +The assigned service VLAN (for Q-in-Q/802.1ad interfaces). + ### VRF The [virtual routing and forwarding](../ipam/vrf.md) instance to which this interface is assigned. + +### VLAN Translation Policy + +!!! info "This field was introduced in NetBox v4.2." + +The [VLAN translation policy](../ipam/vlantranslationpolicy.md) that applies to this interface (optional). diff --git a/docs/models/vpn/ikepolicy.md b/docs/models/vpn/ikepolicy.md index d2da28d16..b78b0fe50 100644 --- a/docs/models/vpn/ikepolicy.md +++ b/docs/models/vpn/ikepolicy.md @@ -1,6 +1,6 @@ # IKE Policies -An [Internet Key Exhcnage (IKE)](https://en.wikipedia.org/wiki/Internet_Key_Exchange) policy defines an IKE version, mode, and set of [proposals](./ikeproposal.md) to be used in IKE negotiation. These policies are referenced by [IPSec profiles](./ipsecprofile.md). +An [Internet Key Exchange (IKE)](https://en.wikipedia.org/wiki/Internet_Key_Exchange) policy defines an IKE version, mode, and set of [proposals](./ikeproposal.md) to be used in IKE negotiation. These policies are referenced by [IPSec profiles](./ipsecprofile.md). ## Fields diff --git a/docs/models/wireless/wirelesslan.md b/docs/models/wireless/wirelesslan.md index 0f50fa75f..2ce673086 100644 --- a/docs/models/wireless/wirelesslan.md +++ b/docs/models/wireless/wirelesslan.md @@ -43,3 +43,9 @@ The security cipher used to apply wireless authentication. Options include: ### Pre-Shared Key The security key configured on each client to grant access to the secured wireless LAN. This applies only to certain authentication types. + +### Scope + +!!! info "This field was introduced in NetBox v4.2." + +The [region](../dcim/region.md), [site](../dcim/site.md), [site group](../dcim/sitegroup.md) or [location](../dcim/location.md) with which this wireless LAN is associated. diff --git a/docs/models/wireless/wirelesslink.md b/docs/models/wireless/wirelesslink.md index 7553902b0..a9fd6b4fc 100644 --- a/docs/models/wireless/wirelesslink.md +++ b/docs/models/wireless/wirelesslink.md @@ -22,8 +22,6 @@ The service set identifier (SSID) for the wireless link (optional). ### Distance -!!! info "This field was introduced in NetBox v4.1." - The distance between the link's two endpoints, including a unit designation (e.g. 100 meters or 25 feet). ### Authentication Type diff --git a/docs/netbox_logo_dark.svg b/docs/netbox_logo_dark.svg new file mode 100644 index 000000000..958a1d401 --- /dev/null +++ b/docs/netbox_logo_dark.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/netbox_logo.svg b/docs/netbox_logo_light.svg similarity index 100% rename from docs/netbox_logo.svg rename to docs/netbox_logo_light.svg diff --git a/docs/plugins/development/background-jobs.md b/docs/plugins/development/background-jobs.md index 873390a58..9be52c3ca 100644 --- a/docs/plugins/development/background-jobs.md +++ b/docs/plugins/development/background-jobs.md @@ -1,7 +1,5 @@ # Background Jobs -!!! info "This feature was introduced in NetBox v4.1." - NetBox plugins can defer certain operations by enqueuing [background jobs](../../features/background-jobs.md), which are executed asynchronously by background workers. This is helpful for decoupling long-running processes from the user-facing request-response cycle. For example, your plugin might need to fetch data from a remote system. Depending on the amount of data and the responsiveness of the remote server, this could take a few minutes. Deferring this task to a queued job ensures that it can be completed in the background, without interrupting the user. The data it fetches can be made available once the job has completed. @@ -29,6 +27,9 @@ class MyTestJob(JobRunner): You can schedule the background job from within your code (e.g. from a model's `save()` method or a view) by calling `MyTestJob.enqueue()`. This method passes through all arguments to `Job.enqueue()`. However, no `name` argument must be passed, as the background job name will be used instead. +!!! tip + A set of predefined intervals is available at `core.choices.JobIntervalChoices` for convenience. + ### Attributes `JobRunner` attributes are defined under a class named `Meta` within the job. These are optional, but encouraged. @@ -46,26 +47,57 @@ As described above, jobs can be scheduled for immediate execution or at any late #### Example +```python title="models.py" +from django.db import models +from core.choices import JobIntervalChoices +from netbox.models import NetBoxModel +from .jobs import MyTestJob + +class MyModel(NetBoxModel): + foo = models.CharField() + + def save(self, *args, **kwargs): + MyTestJob.enqueue_once(instance=self, interval=JobIntervalChoices.INTERVAL_HOURLY) + return super().save(*args, **kwargs) + + def sync(self): + MyTestJob.enqueue(instance=self) +``` + + +### System Jobs + +!!! info "This feature was introduced in NetBox v4.2." + +Some plugins may implement background jobs that are decoupled from the request/response cycle. Typical use cases would be housekeeping tasks or synchronization jobs. These can be registered as _system jobs_ using the `system_job()` decorator. The job interval must be passed as an integer (in minutes) when registering a system job. System jobs are scheduled automatically when the RQ worker (`manage.py rqworker`) is run. + +#### Example + ```python title="jobs.py" -from netbox.jobs import JobRunner - +from core.choices import JobIntervalChoices +from netbox.jobs import JobRunner, system_job +from .models import MyModel +# Specify a predefined choice or an integer indicating +# the number of minutes between job executions +@system_job(interval=JobIntervalChoices.INTERVAL_HOURLY) class MyHousekeepingJob(JobRunner): class Meta: - name = "Housekeeping" + name = "My Housekeeping Job" def run(self, *args, **kwargs): - # your logic goes here + MyModel.objects.filter(foo='bar').delete() ``` -```python title="__init__.py" -from netbox.plugins import PluginConfig +!!! note + Ensure that any system jobs are imported on initialization. Otherwise, they won't be registered. This can be achieved by extending the PluginConfig's `ready()` method. For example: -class MyPluginConfig(PluginConfig): + ```python def ready(self): + super().ready() + from .jobs import MyHousekeepingJob - MyHousekeepingJob.setup(interval=60) -``` + ``` ## Task queues diff --git a/docs/plugins/development/data-backends.md b/docs/plugins/development/data-backends.md index 8b7226a41..0c6d44d95 100644 --- a/docs/plugins/development/data-backends.md +++ b/docs/plugins/development/data-backends.md @@ -18,6 +18,6 @@ backends = [MyDataBackend] ``` !!! tip - The path to the list of search indexes can be modified by setting `data_backends` in the PluginConfig instance. + The path to the list of data backends can be modified by setting `data_backends` in the PluginConfig instance. ::: netbox.data_backends.DataBackend diff --git a/docs/plugins/development/event-types.md b/docs/plugins/development/event-types.md index 4bcdeea31..65e2bbc5c 100644 --- a/docs/plugins/development/event-types.md +++ b/docs/plugins/development/event-types.md @@ -1,7 +1,5 @@ # Event Types -!!! info "This feature was introduced in NetBox v4.1." - Plugins can register their own custom event types for use with NetBox [event rules](../../models/extras/eventrule.md). This is accomplished by calling the `register()` method on an instance of the `EventType` class. This can be done anywhere within the plugin. An example is provided below. ```python diff --git a/docs/plugins/development/index.md b/docs/plugins/development/index.md index f3f9a3e4f..246816349 100644 --- a/docs/plugins/development/index.md +++ b/docs/plugins/development/index.md @@ -98,28 +98,29 @@ NetBox looks for the `config` variable within a plugin's `__init__.py` to load i ### PluginConfig Attributes -| Name | Description | -|-----------------------|--------------------------------------------------------------------------------------------------------------------------| -| `name` | Raw plugin name; same as the plugin's source directory | -| `verbose_name` | Human-friendly name for the plugin | -| `version` | Current release ([semantic versioning](https://semver.org/) is encouraged) | -| `description` | Brief description of the plugin's purpose | -| `author` | Name of plugin's author | -| `author_email` | Author's public email address | -| `base_url` | Base path to use for plugin URLs (optional). If not specified, the project's `name` will be used. | -| `required_settings` | A list of any configuration parameters that **must** be defined by the user | -| `default_settings` | A dictionary of configuration parameters and their default values | -| `django_apps` | A list of additional Django apps to load alongside the plugin | -| `min_version` | Minimum version of NetBox with which the plugin is compatible | -| `max_version` | Maximum version of NetBox with which the plugin is compatible | -| `middleware` | A list of middleware classes to append after NetBox's build-in middleware | -| `queues` | A list of custom background task queues to create | -| `search_extensions` | The dotted path to the list of search index classes (default: `search.indexes`) | -| `data_backends` | The dotted path to the list of data source backend classes (default: `data_backends.backends`) | -| `template_extensions` | The dotted path to the list of template extension classes (default: `template_content.template_extensions`) | -| `menu_items` | The dotted path to the list of menu items provided by the plugin (default: `navigation.menu_items`) | -| `graphql_schema` | The dotted path to the plugin's GraphQL schema class, if any (default: `graphql.schema`) | -| `user_preferences` | The dotted path to the dictionary mapping of user preferences defined by the plugin (default: `preferences.preferences`) | +| Name | Description | +|-----------------------|------------------------------------------------------------------------------------------------------------------------------------| +| `name` | Raw plugin name; same as the plugin's source directory | +| `verbose_name` | Human-friendly name for the plugin | +| `version` | Current release ([semantic versioning](https://semver.org/) is encouraged) | +| `description` | Brief description of the plugin's purpose | +| `author` | Name of plugin's author | +| `author_email` | Author's public email address | +| `base_url` | Base path to use for plugin URLs (optional). If not specified, the project's `name` will be used. | +| `required_settings` | A list of any configuration parameters that **must** be defined by the user | +| `default_settings` | A dictionary of configuration parameters and their default values | +| `django_apps` | A list of additional Django apps to load alongside the plugin | +| `min_version` | Minimum version of NetBox with which the plugin is compatible | +| `max_version` | Maximum version of NetBox with which the plugin is compatible | +| `middleware` | A list of middleware classes to append after NetBox's build-in middleware | +| `queues` | A list of custom background task queues to create | +| `events_pipeline` | A list of handlers to add to [`EVENTS_PIPELINE`](../../configuration/miscellaneous.md#events_pipeline), identified by dotted paths | +| `search_extensions` | The dotted path to the list of search index classes (default: `search.indexes`) | +| `data_backends` | The dotted path to the list of data source backend classes (default: `data_backends.backends`) | +| `template_extensions` | The dotted path to the list of template extension classes (default: `template_content.template_extensions`) | +| `menu_items` | The dotted path to the list of menu items provided by the plugin (default: `navigation.menu_items`) | +| `graphql_schema` | The dotted path to the plugin's GraphQL schema class, if any (default: `graphql.schema`) | +| `user_preferences` | The dotted path to the dictionary mapping of user preferences defined by the plugin (default: `preferences.preferences`) | All required settings must be configured by the user. If a configuration parameter is listed in both `required_settings` and `default_settings`, the default setting will be ignored. diff --git a/docs/plugins/development/views.md b/docs/plugins/development/views.md index 1f5f164fd..e3740de59 100644 --- a/docs/plugins/development/views.md +++ b/docs/plugins/development/views.md @@ -185,6 +185,9 @@ class MyView(generic.ObjectView): ) ``` +!!! note "Changed in NetBox v4.2" + The `register_model_view()` function was extended in NetBox v4.2 to support registration of list views by passing `detail=False`. + ::: utilities.views.register_model_view ::: utilities.views.ViewTab @@ -203,8 +206,6 @@ Plugins can inject custom content into certain areas of core NetBox views. This | `right_page()` | Object view | Inject content on the right side of the page | | `full_width_page()` | Object view | Inject content across the entire bottom of the page | -!!! info "The `navbar()` and `alerts()` methods were introduced in NetBox v4.1." - Additionally, a `render()` method is available for convenience. This method accepts the name of a template to render, and any additional context data you want to pass. Its use is optional, however. To control where the custom content is injected, plugin authors can specify an iterable of models by overriding the `models` attribute on the subclass. Extensions which do not specify a set of models will be invoked on every view, where supported. diff --git a/docs/release-notes/index.md b/docs/release-notes/index.md index b1f23ae55..d996224c1 100644 --- a/docs/release-notes/index.md +++ b/docs/release-notes/index.md @@ -10,6 +10,23 @@ 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.2](./version-4.2.md) (January 2025) + +* Assign Multiple MAC Addresses per Interface ([#4867](https://github.com/netbox-community/netbox/issues/4867)) +* Quick Add UI Widget ([#5858](https://github.com/netbox-community/netbox/issues/5858)) +* VLAN Translation ([#7336](https://github.com/netbox-community/netbox/issues/7336)) +* Virtual Circuits ([#13086](https://github.com/netbox-community/netbox/issues/13086)) +* Q-in-Q Encapsulation ([#13428](https://github.com/netbox-community/netbox/issues/13428)) + +#### [Version 4.1](./version-4.1.md) (September 2024) + +* Circuit Groups ([#7025](https://github.com/netbox-community/netbox/issues/7025)) +* VLAN Group ID Ranges ([#9627](https://github.com/netbox-community/netbox/issues/9627)) +* Nested Device Modules ([#10500](https://github.com/netbox-community/netbox/issues/10500)) +* Rack Types ([#12826](https://github.com/netbox-community/netbox/issues/12826)) +* Plugins Catalog Integration ([#14731](https://github.com/netbox-community/netbox/issues/14731)) +* User Notifications ([#15621](https://github.com/netbox-community/netbox/issues/15621)) + #### [Version 4.0](./version-4.0.md) (April 2024) * Complete UI Refresh ([#12128](https://github.com/netbox-community/netbox/issues/12128)) diff --git a/docs/release-notes/version-4.1.md b/docs/release-notes/version-4.1.md index d48bb899f..397741171 100644 --- a/docs/release-notes/version-4.1.md +++ b/docs/release-notes/version-4.1.md @@ -1,5 +1,61 @@ # NetBox v4.1 +## v4.1.7 (FUTURE) + +### Enhancements + +* [#15239](https://github.com/netbox-community/netbox/issues/15239) - Enable adding/removing individual VLANs while bulk editing device interfaces +* [#17871](https://github.com/netbox-community/netbox/issues/17871) - Enable the assignment/removal of virtualization cluster via device bulk edit +* [#17934](https://github.com/netbox-community/netbox/issues/17934) - Add 1000Base-LX interface type +* [#18007](https://github.com/netbox-community/netbox/issues/18007) - Hide sensitive parameters under data source view (even for privileged users) + +### Bug Fixes + +* [#17459](https://github.com/netbox-community/netbox/issues/17459) - Correct help text on `name` field of module type component templates +* [#17901](https://github.com/netbox-community/netbox/issues/17901) - Ensure GraphiQL UI resources are served locally +* [#17921](https://github.com/netbox-community/netbox/issues/17921) - Fix scheduling of recurring custom scripts +* [#17923](https://github.com/netbox-community/netbox/issues/17923) - Fix the execution of custom scripts via REST API & management command +* [#17963](https://github.com/netbox-community/netbox/issues/17963) - Fix selection of all listed objects during bulk edit +* [#17969](https://github.com/netbox-community/netbox/issues/17969) - Fix system info export when a config revision exists +* [#17972](https://github.com/netbox-community/netbox/issues/17972) - Force evaluation of `LOGIN_REQUIRED` when requesting static media +* [#17986](https://github.com/netbox-community/netbox/issues/17986) - Correct labels for virtual machine & virtual disk size properties +* [#18037](https://github.com/netbox-community/netbox/issues/18037) - Fix validation of maximum VLAN ID value when defining VLAN groups +* [#18038](https://github.com/netbox-community/netbox/issues/18038) - The `to_grams()` utility function should always return an integer value + +--- + +## v4.1.6 (2024-10-31) + +### Bug Fixes + +* [#17700](https://github.com/netbox-community/netbox/issues/17700) - Fix warning when no scripts are found within a script module +* [#17884](https://github.com/netbox-community/netbox/issues/17884) - Fix translation support for certain tab headings +* [#17885](https://github.com/netbox-community/netbox/issues/17885) - Fix regression preventing custom scripts from executing + +## v4.1.5 (2024-10-28) + +### Enhancements + +* [#17789](https://github.com/netbox-community/netbox/issues/17789) - Provide a single "scope" field for bulk editing VLAN group scope assignments + +### Bug Fixes + +* [#17358](https://github.com/netbox-community/netbox/issues/17358) - Fix validation of overlapping IP ranges +* [#17374](https://github.com/netbox-community/netbox/issues/17374) - Fix styling of highlighted table rows in dark mode +* [#17460](https://github.com/netbox-community/netbox/issues/17460) - Ensure bulk action buttons are consistent for device type components +* [#17635](https://github.com/netbox-community/netbox/issues/17635) - Ensure AbortTransaction is caught when running a custom script with `commit=False` +* [#17685](https://github.com/netbox-community/netbox/issues/17685) - Ensure background jobs are validated before being scheduled +* [#17710](https://github.com/netbox-community/netbox/issues/17710) - Remove cached fields on CableTermination model from GraphQL API +* [#17740](https://github.com/netbox-community/netbox/issues/17740) - Ensure support for image attachments with a `.webp` file extension +* [#17749](https://github.com/netbox-community/netbox/issues/17749) - Restore missing `devicetypes` and `children` fields for several objects in GraphQL API +* [#17754](https://github.com/netbox-community/netbox/issues/17754) - Remove paginator from version history table under plugin view +* [#17759](https://github.com/netbox-community/netbox/issues/17759) - Retain `job_timeout` value when scheduling a recurring custom script +* [#17774](https://github.com/netbox-community/netbox/issues/17774) - Fix SSO login support for Entra ID (formerly Azure AD) +* [#17802](https://github.com/netbox-community/netbox/issues/17802) - Fix background color for bulk rename buttons in list views +* [#17838](https://github.com/netbox-community/netbox/issues/17838) - Adjust `manage.py` to reference `python3` executable + +--- + ## v4.1.4 (2024-10-15) ### Enhancements diff --git a/docs/release-notes/version-4.2.md b/docs/release-notes/version-4.2.md new file mode 100644 index 000000000..fe138c064 --- /dev/null +++ b/docs/release-notes/version-4.2.md @@ -0,0 +1,123 @@ +# NetBox v4.2 + +## v4.2-beta1 (2024-12-02) + +!!! danger "Not for Production Use" + This is a beta release of NetBox intended for testing and evaluation. **Do not use this software in production.** Also be aware that no upgrade path is provided to future releases. + +### Breaking Changes + +* Support for the Django admin UI has been completely removed. (The Django admin UI was disabled by default in NetBox v4.0.) +* NetBox has adopted collation-based natural ordering for many models. This may alter the order in which some objects are listed by default. +* Automatic redirects from pre-v4.1 UI views for virtual disks have been removed. +* The `site` and `provider_network` foreign key fields on `circuits.CircuitTermination` have been replaced by the `termination` generic foreign key. +* The `site` foreign key field on `ipam.Prefix` has been replaced by the `scope` generic foreign key. +* The `site` foreign key field on `virtualization.Cluster` has been replaced by the `scope` generic foreign key. +* Obsolete nested REST API serializers have been removed. These were deprecated in NetBox v4.1 under [#17143](https://github.com/netbox-community/netbox/issues/17143). + +### New Features + +#### Assign Multiple MAC Addresses per Interface ([#4867](https://github.com/netbox-community/netbox/issues/4867)) + +MAC addresses are now managed as independent objects, rather than attributes on device and VM interfaces. NetBox now supports the assignment of multiple MAC addresses per interface, and allows a primary MAC address to be designated for each. + +#### Quick Add UI Widget ([#5858](https://github.com/netbox-community/netbox/issues/5858)) + +A new UI widget has been introduced to enable conveniently creating new related objects while creating or editing an object. For instance, it is now possible to create and assign a new device role when creating or editing a device from within the device form. + +#### VLAN Translation ([#7336](https://github.com/netbox-community/netbox/issues/7336)) + +User can now define policies which track the translation of VLAN IDs on IEEE 802.1Q-encapsulated interfaces. Translation policies can be reused across multiple interfaces. + +#### Virtual Circuits ([#13086](https://github.com/netbox-community/netbox/issues/13086)) + +New models have been introduced to support the documentation of virtual circuits as an extension to the physical circuit modeling already supported. This enables users to accurately reflect point-to-point or multipoint virtual circuits atop infrastructure comprising physical circuits and cables. + +#### Q-in-Q Encapsulation ([#13428](https://github.com/netbox-community/netbox/issues/13428)) + +NetBox now supports the designation of customer VLANs (CVLANs) and service VLANs (SVLANs) to support IEEE 802.1ad/Q-in-Q encapsulation. Each interface can now have it mode designated "Q-in-Q" and be assigned an SVLAN. + +### Enhancements + +* [#6414](https://github.com/netbox-community/netbox/issues/6414) - Prefixes can now be scoped by region, site group, site, or location +* [#7699](https://github.com/netbox-community/netbox/issues/7699) - Virtualization clusters can now be scoped by region, site group, site, or location +* [#9604](https://github.com/netbox-community/netbox/issues/9604) - The scope of a circuit termination now include a region, site group, site, location, or provider network +* [#10711](https://github.com/netbox-community/netbox/issues/10711) - Wireless LANs can now be scoped by region, site group, site, or location +* [#11279](https://github.com/netbox-community/netbox/issues/11279) - Improved the use of natural ordering for various models throughout the application +* [#12596](https://github.com/netbox-community/netbox/issues/12596) - Extended the virtualization clusters REST API endpoint to report on allocated VM resources +* [#16547](https://github.com/netbox-community/netbox/issues/16547) - Add a geographic distance field for circuits +* [#16783](https://github.com/netbox-community/netbox/issues/16783) - Add an operational status field for inventory items +* [#17195](https://github.com/netbox-community/netbox/issues/17195) - Add a color field for power outlets + +### Plugins + +* [#15093](https://github.com/netbox-community/netbox/issues/15093) - Introduced the `events_pipeline` configuration parameter, which allows plugins to hook into NetBox event processing +* [#16546](https://github.com/netbox-community/netbox/issues/16546) - NetBoxModel now provides a default `get_absolute_url()` method +* [#16971](https://github.com/netbox-community/netbox/issues/16971) - Plugins can now easily register system jobs to perform background tasks +* [#17029](https://github.com/netbox-community/netbox/issues/17029) - Registering a `PluginTemplateExtension` subclass for a single model has been deprecated (replace `model` with `models`) +* [#18023](https://github.com/netbox-community/netbox/issues/18023) - Extend `register_model_view()` to handle list views + +### Other Changes + +* [#16136](https://github.com/netbox-community/netbox/issues/16136) - Removed support for the Django admin UI +* [#17165](https://github.com/netbox-community/netbox/issues/17165) - All obsolete nested REST API serializers have been removed +* [#17472](https://github.com/netbox-community/netbox/issues/17472) - The legacy staged changes API has been deprecated, and will be removed in Netbox v4.3 +* [#17476](https://github.com/netbox-community/netbox/issues/17476) - Upgrade to Django 5.1 +* [#17752](https://github.com/netbox-community/netbox/issues/17752) - Bulk object import URL paths have been renamed from `*_import` to `*_bulk_import` +* [#17761](https://github.com/netbox-community/netbox/issues/17761) - Optional choice fields now store empty values as null (rather than empty strings) in the database +* [#18093](https://github.com/netbox-community/netbox/issues/18093) - Redirects for pre-v4.1 virtual disk UI views have been removed + +### REST API Changes + +* Added the following endpoints: + * `/api/circuits/virtual-circuits/` + * `/api/circuits/virtual-circuit-terminations/` + * `/api/dcim/mac-addresses/` + * `/api/ipam/vlan-translation-policies/` + * `/api/ipam/vlan-translation-rules/` +* circuits.Circuit + * Added the optional `distance` and `distance_unit` fields +* circuits.CircuitTermination + * Removed the `site` & `provider_network` fields + * Added the `termination_type` & `termination_id` fields to facilitate termination assignment + * Added the read-only `termination` field +* dcim.Interface + * The `mac_address` field is now read-only + * Added the `primary_mac_address` relation to dcim.MACAddress + * Added the read-only `mac_addresses` list + * Added the `qinq_svlan` relation to ipam.VLAN + * Added the `vlan_translation_policy` relation to ipam.VLANTranslationPolicy + * Added `mode` choice "Q-in-Q" +* dcim.InventoryItem + * Added the optional `status` choice field +* dcim.Location + * Added the read-only `prefix_count` field +* dcim.PowerOutlet + * Added the optional `color` field +* dcim.Region + * Added the read-only `prefix_count` field +* dcim.SiteGroup + * Added the read-only `prefix_count` field +* ipam.Prefix + * Removed the `site` field + * Added the `scope_type` & `scope_id` fields to facilitate scope assignment + * Added the read-only `scope` field +* ipam.VLAN + * Added the optional `qinq_role` selection field + * Added the `qinq_svlan` recursive relation +* virtualization.Cluster + * Removed the `site` field + * Added the `scope_type` & `scope_id` fields to facilitate scope assignment + * Added the read-only `scope` field +* virtualization.Cluster + * Added the read-only fields `allocated_vcpus`, `allocated_memory`, and `allocated_disk` +* virtualization.VMInterface + * The `mac_address` field is now read-only + * Added the `primary_mac_address` relation to dcim.MACAddress + * Added the read-only `mac_addresses` list + * Added the `qinq_svlan` relation to ipam.VLAN + * Added the `vlan_translation_policy` relation to ipam.VLANTranslationPolicy + * Added `mode` choice "Q-in-Q" +* wireless.WirelessLAN + * Added the `scope_type` & `scope_id` fields to support scope assignment + * Added the read-only `scope` field diff --git a/mkdocs.yml b/mkdocs.yml index 94a4edcb3..f870b69d6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -156,6 +156,7 @@ nav: - Administration: - Authentication: - Overview: 'administration/authentication/overview.md' + - Google: 'administration/authentication/google.md' - Microsoft Entra ID: 'administration/authentication/microsoft-entra-id.md' - Okta: 'administration/authentication/okta.md' - Permissions: 'administration/permissions.md' @@ -173,6 +174,8 @@ nav: - Provider: 'models/circuits/provider.md' - Provider Account: 'models/circuits/provideraccount.md' - Provider Network: 'models/circuits/providernetwork.md' + - Virtual Circuit: 'models/circuits/virtualcircuit.md' + - Virtual Circuit Termination: 'models/circuits/virtualcircuittermination.md' - Core: - DataFile: 'models/core/datafile.md' - DataSource: 'models/core/datasource.md' @@ -196,6 +199,7 @@ nav: - InventoryItemRole: 'models/dcim/inventoryitemrole.md' - InventoryItemTemplate: 'models/dcim/inventoryitemtemplate.md' - Location: 'models/dcim/location.md' + - MACAddress: 'models/dcim/macaddress.md' - Manufacturer: 'models/dcim/manufacturer.md' - Module: 'models/dcim/module.md' - ModuleBay: 'models/dcim/modulebay.md' @@ -254,6 +258,8 @@ nav: - ServiceTemplate: 'models/ipam/servicetemplate.md' - VLAN: 'models/ipam/vlan.md' - VLANGroup: 'models/ipam/vlangroup.md' + - VLANTranslationPolicy: 'models/ipam/vlantranslationpolicy.md' + - VLANTranslationRule: 'models/ipam/vlantranslationrule.md' - VRF: 'models/ipam/vrf.md' - Tenancy: - Contact: 'models/tenancy/contact.md' @@ -305,6 +311,7 @@ nav: - git Cheat Sheet: 'development/git-cheat-sheet.md' - Release Notes: - Summary: 'release-notes/index.md' + - Version 4.2: 'release-notes/version-4.2.md' - Version 4.1: 'release-notes/version-4.1.md' - Version 4.0: 'release-notes/version-4.0.md' - Version 3.7: 'release-notes/version-3.7.md' diff --git a/netbox/circuits/api/nested_serializers.py b/netbox/circuits/api/nested_serializers.py deleted file mode 100644 index 487749872..000000000 --- a/netbox/circuits/api/nested_serializers.py +++ /dev/null @@ -1,79 +0,0 @@ -import warnings - -from drf_spectacular.utils import extend_schema_serializer - -from circuits.models import * -from netbox.api.fields import RelatedObjectCountField -from netbox.api.serializers import WritableNestedSerializer -from .serializers_.nested import NestedProviderAccountSerializer - -__all__ = [ - 'NestedCircuitSerializer', - 'NestedCircuitTerminationSerializer', - 'NestedCircuitTypeSerializer', - 'NestedProviderNetworkSerializer', - 'NestedProviderSerializer', - 'NestedProviderAccountSerializer', -] - -# TODO: Remove in v4.2 -warnings.warn( - "Dedicated nested serializers will be removed in NetBox v4.2. Use Serializer(nested=True) instead.", - DeprecationWarning -) - - -# -# Provider networks -# - -class NestedProviderNetworkSerializer(WritableNestedSerializer): - - class Meta: - model = ProviderNetwork - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -# -# Providers -# - -@extend_schema_serializer( - exclude_fields=('circuit_count',), -) -class NestedProviderSerializer(WritableNestedSerializer): - circuit_count = RelatedObjectCountField('circuits') - - class Meta: - model = Provider - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'circuit_count'] - - -# -# Circuits -# - -@extend_schema_serializer( - exclude_fields=('circuit_count',), -) -class NestedCircuitTypeSerializer(WritableNestedSerializer): - circuit_count = RelatedObjectCountField('circuits') - - class Meta: - model = CircuitType - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'circuit_count'] - - -class NestedCircuitSerializer(WritableNestedSerializer): - - class Meta: - model = Circuit - fields = ['id', 'url', 'display_url', 'display', 'cid'] - - -class NestedCircuitTerminationSerializer(WritableNestedSerializer): - circuit = NestedCircuitSerializer() - - class Meta: - model = CircuitTermination - fields = ['id', 'url', 'display_url', 'display', 'circuit', 'term_side', 'cable', '_occupied'] diff --git a/netbox/circuits/api/serializers_/circuits.py b/netbox/circuits/api/serializers_/circuits.py index 111fa6f87..0365dca33 100644 --- a/netbox/circuits/api/serializers_/circuits.py +++ b/netbox/circuits/api/serializers_/circuits.py @@ -1,11 +1,20 @@ -from circuits.choices import CircuitPriorityChoices, CircuitStatusChoices -from circuits.models import Circuit, CircuitGroup, CircuitGroupAssignment, CircuitTermination, CircuitType +from django.contrib.contenttypes.models import ContentType +from drf_spectacular.utils import extend_schema_field +from rest_framework import serializers + +from circuits.choices import CircuitPriorityChoices, CircuitStatusChoices, VirtualCircuitTerminationRoleChoices +from circuits.constants import CIRCUIT_TERMINATION_TERMINATION_TYPES +from circuits.models import ( + Circuit, CircuitGroup, CircuitGroupAssignment, CircuitTermination, CircuitType, VirtualCircuit, + VirtualCircuitTermination, +) +from dcim.api.serializers_.device_components import InterfaceSerializer from dcim.api.serializers_.cables import CabledObjectSerializer -from dcim.api.serializers_.sites import SiteSerializer -from netbox.api.fields import ChoiceField, RelatedObjectCountField +from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer from netbox.choices import DistanceUnitChoices from tenancy.api.serializers_.tenants import TenantSerializer +from utilities.api import get_serializer_for_model from .providers import ProviderAccountSerializer, ProviderNetworkSerializer, ProviderSerializer @@ -15,6 +24,8 @@ __all__ = ( 'CircuitGroupSerializer', 'CircuitTerminationSerializer', 'CircuitTypeSerializer', + 'VirtualCircuitSerializer', + 'VirtualCircuitTerminationSerializer', ) @@ -33,16 +44,32 @@ class CircuitTypeSerializer(NetBoxModelSerializer): class CircuitCircuitTerminationSerializer(WritableNestedSerializer): - site = SiteSerializer(nested=True, allow_null=True) - provider_network = ProviderNetworkSerializer(nested=True, allow_null=True) + termination_type = ContentTypeField( + queryset=ContentType.objects.filter( + model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES + ), + allow_null=True, + required=False, + default=None + ) + termination_id = serializers.IntegerField(allow_null=True, required=False, default=None) + termination = serializers.SerializerMethodField(read_only=True) class Meta: model = CircuitTermination fields = [ - 'id', 'url', 'display_url', 'display', 'site', 'provider_network', 'port_speed', 'upstream_speed', - 'xconnect_id', 'description', + 'id', 'url', 'display_url', 'display', 'termination_type', 'termination_id', 'termination', 'port_speed', + 'upstream_speed', 'xconnect_id', 'description', ] + @extend_schema_field(serializers.JSONField(allow_null=True)) + def get_termination(self, obj): + if obj.termination_id is None: + return None + serializer = get_serializer_for_model(obj.termination) + context = {'request': self.context['request']} + return serializer(obj.termination, nested=True, context=context).data + class CircuitGroupSerializer(NetBoxModelSerializer): tenant = TenantSerializer(nested=True, required=False, allow_null=True) @@ -77,36 +104,54 @@ class CircuitSerializer(NetBoxModelSerializer): provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True, default=None) status = ChoiceField(choices=CircuitStatusChoices, required=False) type = CircuitTypeSerializer(nested=True) + distance_unit = ChoiceField(choices=DistanceUnitChoices, allow_blank=True, required=False, allow_null=True) tenant = TenantSerializer(nested=True, required=False, allow_null=True) termination_a = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True) termination_z = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True) assignments = CircuitGroupAssignmentSerializer_(nested=True, many=True, required=False) - distance_unit = ChoiceField(choices=DistanceUnitChoices, allow_blank=True, required=False, allow_null=True) class Meta: model = Circuit fields = [ 'id', 'url', 'display_url', 'display', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', - 'install_date', 'termination_date', 'commit_rate', 'description', 'termination_a', 'termination_z', - 'distance', 'distance_unit', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'assignments', + 'install_date', 'termination_date', 'commit_rate', 'description', 'distance', 'distance_unit', + 'termination_a', 'termination_z', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', + 'assignments', ] brief_fields = ('id', 'url', 'display', 'provider', 'cid', 'description') class CircuitTerminationSerializer(NetBoxModelSerializer, CabledObjectSerializer): circuit = CircuitSerializer(nested=True) - site = SiteSerializer(nested=True, required=False, allow_null=True) - provider_network = ProviderNetworkSerializer(nested=True, required=False, allow_null=True) + termination_type = ContentTypeField( + queryset=ContentType.objects.filter( + model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES + ), + allow_null=True, + required=False, + default=None + ) + termination_id = serializers.IntegerField(allow_null=True, required=False, default=None) + termination = serializers.SerializerMethodField(read_only=True) class Meta: model = CircuitTermination fields = [ - 'id', 'url', 'display_url', 'display', 'circuit', 'term_side', 'site', 'provider_network', 'port_speed', - 'upstream_speed', 'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'cable_end', - 'link_peers', 'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', + 'id', 'url', 'display_url', 'display', 'circuit', 'term_side', 'termination_type', 'termination_id', + 'termination', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', 'description', 'mark_connected', + 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', + '_occupied', ] brief_fields = ('id', 'url', 'display', 'circuit', 'term_side', 'description', 'cable', '_occupied') + @extend_schema_field(serializers.JSONField(allow_null=True)) + def get_termination(self, obj): + if obj.termination_id is None: + return None + serializer = get_serializer_for_model(obj.termination) + context = {'request': self.context['request']} + return serializer(obj.termination, nested=True, context=context).data + class CircuitGroupAssignmentSerializer(CircuitGroupAssignmentSerializer_): circuit = CircuitSerializer(nested=True) @@ -117,3 +162,32 @@ class CircuitGroupAssignmentSerializer(CircuitGroupAssignmentSerializer_): 'id', 'url', 'display_url', 'display', 'group', 'circuit', 'priority', 'tags', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'group', 'circuit', 'priority') + + +class VirtualCircuitSerializer(NetBoxModelSerializer): + provider_network = ProviderNetworkSerializer(nested=True) + provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True, default=None) + status = ChoiceField(choices=CircuitStatusChoices, required=False) + tenant = TenantSerializer(nested=True, required=False, allow_null=True) + + class Meta: + model = VirtualCircuit + fields = [ + 'id', 'url', 'display_url', 'display', 'cid', 'provider_network', 'provider_account', 'status', 'tenant', + 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', + ] + brief_fields = ('id', 'url', 'display', 'provider_network', 'cid', 'description') + + +class VirtualCircuitTerminationSerializer(NetBoxModelSerializer, CabledObjectSerializer): + virtual_circuit = VirtualCircuitSerializer(nested=True) + role = ChoiceField(choices=VirtualCircuitTerminationRoleChoices, required=False) + interface = InterfaceSerializer(nested=True) + + class Meta: + model = VirtualCircuitTermination + fields = [ + 'id', 'url', 'display_url', 'display', 'virtual_circuit', 'role', 'interface', 'description', 'tags', + 'custom_fields', 'created', 'last_updated', + ] + brief_fields = ('id', 'url', 'display', 'virtual_circuit', 'role', 'interface', 'description') diff --git a/netbox/circuits/api/urls.py b/netbox/circuits/api/urls.py index 00af3dec6..6f257f694 100644 --- a/netbox/circuits/api/urls.py +++ b/netbox/circuits/api/urls.py @@ -17,5 +17,9 @@ router.register('circuit-terminations', views.CircuitTerminationViewSet) router.register('circuit-groups', views.CircuitGroupViewSet) router.register('circuit-group-assignments', views.CircuitGroupAssignmentViewSet) +# Virtual circuits +router.register('virtual-circuits', views.VirtualCircuitViewSet) +router.register('virtual-circuit-terminations', views.VirtualCircuitTerminationViewSet) + app_name = 'circuits-api' urlpatterns = router.urls diff --git a/netbox/circuits/api/views.py b/netbox/circuits/api/views.py index 8cce013d7..3b49075be 100644 --- a/netbox/circuits/api/views.py +++ b/netbox/circuits/api/views.py @@ -93,3 +93,23 @@ class ProviderNetworkViewSet(NetBoxModelViewSet): queryset = ProviderNetwork.objects.all() serializer_class = serializers.ProviderNetworkSerializer filterset_class = filtersets.ProviderNetworkFilterSet + + +# +# Virtual circuits +# + +class VirtualCircuitViewSet(NetBoxModelViewSet): + queryset = VirtualCircuit.objects.all() + serializer_class = serializers.VirtualCircuitSerializer + filterset_class = filtersets.VirtualCircuitFilterSet + + +# +# Virtual circuit terminations +# + +class VirtualCircuitTerminationViewSet(PassThroughPortMixin, NetBoxModelViewSet): + queryset = VirtualCircuitTermination.objects.all() + serializer_class = serializers.VirtualCircuitTerminationSerializer + filterset_class = filtersets.VirtualCircuitTerminationFilterSet diff --git a/netbox/circuits/choices.py b/netbox/circuits/choices.py index 8c25c7459..4d6132d7a 100644 --- a/netbox/circuits/choices.py +++ b/netbox/circuits/choices.py @@ -92,3 +92,19 @@ class CircuitPriorityChoices(ChoiceSet): (PRIORITY_TERTIARY, _('Tertiary')), (PRIORITY_INACTIVE, _('Inactive')), ] + + +# +# Virtual circuits +# + +class VirtualCircuitTerminationRoleChoices(ChoiceSet): + ROLE_PEER = 'peer' + ROLE_HUB = 'hub' + ROLE_SPOKE = 'spoke' + + CHOICES = [ + (ROLE_PEER, _('Peer'), 'green'), + (ROLE_HUB, _('Hub'), 'blue'), + (ROLE_SPOKE, _('Spoke'), 'orange'), + ] diff --git a/netbox/circuits/constants.py b/netbox/circuits/constants.py new file mode 100644 index 000000000..8119bc286 --- /dev/null +++ b/netbox/circuits/constants.py @@ -0,0 +1,4 @@ +# models values for ContentTypes which may be CircuitTermination termination types +CIRCUIT_TERMINATION_TERMINATION_TYPES = ( + 'region', 'sitegroup', 'site', 'location', 'providernetwork', +) diff --git a/netbox/circuits/filtersets.py b/netbox/circuits/filtersets.py index ebd1fe28d..825df9558 100644 --- a/netbox/circuits/filtersets.py +++ b/netbox/circuits/filtersets.py @@ -3,11 +3,11 @@ from django.db.models import Q from django.utils.translation import gettext as _ from dcim.filtersets import CabledObjectFilterSet -from dcim.models import Region, Site, SiteGroup +from dcim.models import Interface, Location, Region, Site, SiteGroup from ipam.models import ASN from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet -from utilities.filters import TreeNodeMultipleChoiceFilter +from utilities.filters import ContentTypeFilter, TreeNodeMultipleChoiceFilter from .choices import * from .models import * @@ -20,43 +20,45 @@ __all__ = ( 'ProviderNetworkFilterSet', 'ProviderAccountFilterSet', 'ProviderFilterSet', + 'VirtualCircuitFilterSet', + 'VirtualCircuitTerminationFilterSet', ) class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet): region_id = TreeNodeMultipleChoiceFilter( queryset=Region.objects.all(), - field_name='circuits__terminations__site__region', + field_name='circuits__terminations___region', lookup_expr='in', label=_('Region (ID)'), ) region = TreeNodeMultipleChoiceFilter( queryset=Region.objects.all(), - field_name='circuits__terminations__site__region', + field_name='circuits__terminations___region', lookup_expr='in', to_field_name='slug', label=_('Region (slug)'), ) site_group_id = TreeNodeMultipleChoiceFilter( queryset=SiteGroup.objects.all(), - field_name='circuits__terminations__site__group', + field_name='circuits__terminations___site_group', lookup_expr='in', label=_('Site group (ID)'), ) site_group = TreeNodeMultipleChoiceFilter( queryset=SiteGroup.objects.all(), - field_name='circuits__terminations__site__group', + field_name='circuits__terminations___site_group', lookup_expr='in', to_field_name='slug', label=_('Site group (slug)'), ) site_id = django_filters.ModelMultipleChoiceFilter( - field_name='circuits__terminations__site', + field_name='circuits__terminations___site', queryset=Site.objects.all(), label=_('Site'), ) site = django_filters.ModelMultipleChoiceFilter( - field_name='circuits__terminations__site__slug', + field_name='circuits__terminations___site__slug', queryset=Site.objects.all(), to_field_name='slug', label=_('Site (slug)'), @@ -173,7 +175,7 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte label=_('Provider account (account)'), ) provider_network_id = django_filters.ModelMultipleChoiceFilter( - field_name='terminations__provider_network', + field_name='terminations___provider_network', queryset=ProviderNetwork.objects.all(), label=_('Provider network (ID)'), ) @@ -193,37 +195,37 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte ) region_id = TreeNodeMultipleChoiceFilter( queryset=Region.objects.all(), - field_name='terminations__site__region', + field_name='terminations___region', lookup_expr='in', label=_('Region (ID)'), ) region = TreeNodeMultipleChoiceFilter( queryset=Region.objects.all(), - field_name='terminations__site__region', + field_name='terminations___region', lookup_expr='in', to_field_name='slug', label=_('Region (slug)'), ) site_group_id = TreeNodeMultipleChoiceFilter( queryset=SiteGroup.objects.all(), - field_name='terminations__site__group', + field_name='terminations___site_group', lookup_expr='in', label=_('Site group (ID)'), ) site_group = TreeNodeMultipleChoiceFilter( queryset=SiteGroup.objects.all(), - field_name='terminations__site__group', + field_name='terminations___site_group', lookup_expr='in', to_field_name='slug', label=_('Site group (slug)'), ) site_id = django_filters.ModelMultipleChoiceFilter( - field_name='terminations__site', + field_name='terminations___site', queryset=Site.objects.all(), label=_('Site (ID)'), ) site = django_filters.ModelMultipleChoiceFilter( - field_name='terminations__site__slug', + field_name='terminations___site__slug', queryset=Site.objects.all(), to_field_name='slug', label=_('Site (slug)'), @@ -239,7 +241,9 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte class Meta: model = Circuit - fields = ('id', 'cid', 'description', 'install_date', 'termination_date', 'commit_rate', 'distance', 'distance_unit') + fields = ( + 'id', 'cid', 'description', 'install_date', 'termination_date', 'commit_rate', 'distance', 'distance_unit', + ) def search(self, queryset, name, value): if not value.strip(): @@ -263,18 +267,60 @@ class CircuitTerminationFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet): queryset=Circuit.objects.all(), label=_('Circuit'), ) + termination_type = ContentTypeFilter() + region_id = TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='_region', + lookup_expr='in', + label=_('Region (ID)'), + ) + region = TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='_region', + lookup_expr='in', + to_field_name='slug', + label=_('Region (slug)'), + ) + site_group_id = TreeNodeMultipleChoiceFilter( + queryset=SiteGroup.objects.all(), + field_name='_site_group', + lookup_expr='in', + label=_('Site group (ID)'), + ) + site_group = TreeNodeMultipleChoiceFilter( + queryset=SiteGroup.objects.all(), + field_name='_site_group', + lookup_expr='in', + to_field_name='slug', + label=_('Site group (slug)'), + ) site_id = django_filters.ModelMultipleChoiceFilter( queryset=Site.objects.all(), + field_name='_site', label=_('Site (ID)'), ) site = django_filters.ModelMultipleChoiceFilter( - field_name='site__slug', + field_name='_site__slug', queryset=Site.objects.all(), to_field_name='slug', label=_('Site (slug)'), ) + location_id = TreeNodeMultipleChoiceFilter( + queryset=Location.objects.all(), + field_name='_location', + lookup_expr='in', + label=_('Location (ID)'), + ) + location = TreeNodeMultipleChoiceFilter( + queryset=Location.objects.all(), + field_name='_location', + lookup_expr='in', + to_field_name='slug', + label=_('Location (slug)'), + ) provider_network_id = django_filters.ModelMultipleChoiceFilter( queryset=ProviderNetwork.objects.all(), + field_name='_provider_network', label=_('ProviderNetwork (ID)'), ) provider_id = django_filters.ModelMultipleChoiceFilter( @@ -292,8 +338,8 @@ class CircuitTerminationFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet): class Meta: model = CircuitTermination fields = ( - 'id', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id', 'description', 'mark_connected', - 'pp_info', 'cable_end', + 'id', 'termination_id', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id', 'description', + 'mark_connected', 'pp_info', 'cable_end', ) def search(self, queryset, name, value): @@ -362,3 +408,108 @@ class CircuitGroupAssignmentFilterSet(NetBoxModelFilterSet): Q(circuit__cid__icontains=value) | Q(group__name__icontains=value) ) + + +class VirtualCircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet): + provider_id = django_filters.ModelMultipleChoiceFilter( + field_name='provider_network__provider', + queryset=Provider.objects.all(), + label=_('Provider (ID)'), + ) + provider = django_filters.ModelMultipleChoiceFilter( + field_name='provider_network__provider__slug', + queryset=Provider.objects.all(), + to_field_name='slug', + label=_('Provider (slug)'), + ) + provider_account_id = django_filters.ModelMultipleChoiceFilter( + field_name='provider_account', + queryset=ProviderAccount.objects.all(), + label=_('Provider account (ID)'), + ) + provider_account = django_filters.ModelMultipleChoiceFilter( + field_name='provider_account__account', + queryset=Provider.objects.all(), + to_field_name='account', + label=_('Provider account (account)'), + ) + provider_network_id = django_filters.ModelMultipleChoiceFilter( + queryset=ProviderNetwork.objects.all(), + label=_('Provider network (ID)'), + ) + status = django_filters.MultipleChoiceFilter( + choices=CircuitStatusChoices, + null_value=None + ) + + class Meta: + model = VirtualCircuit + fields = ('id', 'cid', 'description') + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(cid__icontains=value) | + Q(description__icontains=value) | + Q(comments__icontains=value) + ).distinct() + + +class VirtualCircuitTerminationFilterSet(NetBoxModelFilterSet): + q = django_filters.CharFilter( + method='search', + label=_('Search'), + ) + virtual_circuit_id = django_filters.ModelMultipleChoiceFilter( + queryset=VirtualCircuit.objects.all(), + label=_('Virtual circuit'), + ) + role = django_filters.MultipleChoiceFilter( + choices=VirtualCircuitTerminationRoleChoices, + null_value=None + ) + provider_id = django_filters.ModelMultipleChoiceFilter( + field_name='virtual_circuit__provider_network__provider', + queryset=Provider.objects.all(), + label=_('Provider (ID)'), + ) + provider = django_filters.ModelMultipleChoiceFilter( + field_name='virtual_circuit__provider_network__provider__slug', + queryset=Provider.objects.all(), + to_field_name='slug', + label=_('Provider (slug)'), + ) + provider_account_id = django_filters.ModelMultipleChoiceFilter( + field_name='virtual_circuit__provider_account', + queryset=ProviderAccount.objects.all(), + label=_('Provider account (ID)'), + ) + provider_account = django_filters.ModelMultipleChoiceFilter( + field_name='virtual_circuit__provider_account__account', + queryset=ProviderAccount.objects.all(), + to_field_name='account', + label=_('Provider account (account)'), + ) + provider_network_id = django_filters.ModelMultipleChoiceFilter( + queryset=ProviderNetwork.objects.all(), + field_name='virtual_circuit__provider_network', + label=_('Provider network (ID)'), + ) + interface_id = django_filters.ModelMultipleChoiceFilter( + queryset=Interface.objects.all(), + field_name='interface', + label=_('Interface (ID)'), + ) + + class Meta: + model = VirtualCircuitTermination + fields = ('id', 'interface_id', 'description') + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(virtual_circuit__cid__icontains=value) | + Q(description__icontains=value) + ).distinct() diff --git a/netbox/circuits/forms/bulk_edit.py b/netbox/circuits/forms/bulk_edit.py index 5cb7b5d30..021635a1a 100644 --- a/netbox/circuits/forms/bulk_edit.py +++ b/netbox/circuits/forms/bulk_edit.py @@ -1,17 +1,25 @@ from django import forms +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import gettext_lazy as _ -from circuits.choices import CircuitCommitRateChoices, CircuitPriorityChoices, CircuitStatusChoices +from circuits.choices import ( + CircuitCommitRateChoices, CircuitPriorityChoices, CircuitStatusChoices, VirtualCircuitTerminationRoleChoices, +) +from circuits.constants import CIRCUIT_TERMINATION_TERMINATION_TYPES from circuits.models import * from dcim.models import Site from ipam.models import ASN from netbox.choices import DistanceUnitChoices from netbox.forms import NetBoxModelBulkEditForm from tenancy.models import Tenant -from utilities.forms import add_blank_choice -from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField -from utilities.forms.rendering import FieldSet, TabbedGroups -from utilities.forms.widgets import BulkEditNullBooleanSelect, DatePicker, NumberWithOptions +from utilities.forms import add_blank_choice, get_field_value +from utilities.forms.fields import ( + ColorField, CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, +) +from utilities.forms.rendering import FieldSet +from utilities.forms.widgets import BulkEditNullBooleanSelect, DatePicker, HTMXSelect, NumberWithOptions +from utilities.templatetags.builtins.filters import bettertitle __all__ = ( 'CircuitBulkEditForm', @@ -22,6 +30,8 @@ __all__ = ( 'ProviderBulkEditForm', 'ProviderAccountBulkEditForm', 'ProviderNetworkBulkEditForm', + 'VirtualCircuitBulkEditForm', + 'VirtualCircuitTerminationBulkEditForm', ) @@ -197,15 +207,18 @@ class CircuitTerminationBulkEditForm(NetBoxModelBulkEditForm): max_length=200, required=False ) - site = DynamicModelChoiceField( - label=_('Site'), - queryset=Site.objects.all(), - required=False + termination_type = ContentTypeChoiceField( + queryset=ContentType.objects.filter(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES), + widget=HTMXSelect(method='post', attrs={'hx-select': '#form_fields'}), + required=False, + label=_('Termination type') ) - provider_network = DynamicModelChoiceField( - label=_('Provider Network'), - queryset=ProviderNetwork.objects.all(), - required=False + termination = DynamicModelChoiceField( + label=_('Termination'), + queryset=Site.objects.none(), # Initial queryset + required=False, + disabled=True, + selector=True ) port_speed = forms.IntegerField( required=False, @@ -225,15 +238,26 @@ class CircuitTerminationBulkEditForm(NetBoxModelBulkEditForm): fieldsets = ( FieldSet( 'description', - TabbedGroups( - FieldSet('site', name=_('Site')), - FieldSet('provider_network', name=_('Provider Network')), - ), + 'termination_type', 'termination', 'mark_connected', name=_('Circuit Termination') ), FieldSet('port_speed', 'upstream_speed', name=_('Termination Details')), ) - nullable_fields = ('description') + nullable_fields = ('description', 'termination') + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + if termination_type_id := get_field_value(self, 'termination_type'): + try: + termination_type = ContentType.objects.get(pk=termination_type_id) + model = termination_type.model_class() + self.fields['termination'].queryset = model.objects.all() + self.fields['termination'].widget.attrs['selector'] = model._meta.label_lower + self.fields['termination'].disabled = False + self.fields['termination'].label = _(bettertitle(model._meta.verbose_name)) + except ObjectDoesNotExist: + pass class CircuitGroupBulkEditForm(NetBoxModelBulkEditForm): @@ -271,3 +295,62 @@ class CircuitGroupAssignmentBulkEditForm(NetBoxModelBulkEditForm): FieldSet('circuit', 'priority'), ) nullable_fields = ('priority',) + + +class VirtualCircuitBulkEditForm(NetBoxModelBulkEditForm): + provider_network = DynamicModelChoiceField( + label=_('Provider network'), + queryset=ProviderNetwork.objects.all(), + required=False + ) + provider_account = DynamicModelChoiceField( + label=_('Provider account'), + queryset=ProviderAccount.objects.all(), + required=False + ) + status = forms.ChoiceField( + label=_('Status'), + choices=add_blank_choice(CircuitStatusChoices), + required=False, + initial='' + ) + tenant = DynamicModelChoiceField( + label=_('Tenant'), + queryset=Tenant.objects.all(), + required=False + ) + description = forms.CharField( + label=_('Description'), + max_length=100, + required=False + ) + comments = CommentField() + + model = VirtualCircuit + fieldsets = ( + FieldSet('provider_network', 'provider_account', 'status', 'description', name=_('Virtual circuit')), + FieldSet('tenant', name=_('Tenancy')), + ) + nullable_fields = ( + 'provider_account', 'tenant', 'description', 'comments', + ) + + +class VirtualCircuitTerminationBulkEditForm(NetBoxModelBulkEditForm): + role = forms.ChoiceField( + label=_('Role'), + choices=add_blank_choice(VirtualCircuitTerminationRoleChoices), + required=False, + initial='' + ) + description = forms.CharField( + label=_('Description'), + max_length=200, + required=False + ) + + model = VirtualCircuitTermination + fieldsets = ( + FieldSet('role', 'description'), + ) + nullable_fields = ('description',) diff --git a/netbox/circuits/forms/bulk_import.py b/netbox/circuits/forms/bulk_import.py index d5cdc00a7..7f5ffde6e 100644 --- a/netbox/circuits/forms/bulk_import.py +++ b/netbox/circuits/forms/bulk_import.py @@ -1,13 +1,15 @@ from django import forms +from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _ from circuits.choices import * +from circuits.constants import * from circuits.models import * -from dcim.models import Site +from dcim.models import Interface from netbox.choices import DistanceUnitChoices from netbox.forms import NetBoxModelImportForm from tenancy.models import Tenant -from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField +from utilities.forms.fields import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, SlugField __all__ = ( 'CircuitImportForm', @@ -19,6 +21,9 @@ __all__ = ( 'ProviderImportForm', 'ProviderAccountImportForm', 'ProviderNetworkImportForm', + 'VirtualCircuitImportForm', + 'VirtualCircuitTerminationImportForm', + 'VirtualCircuitTerminationImportRelatedForm', ) @@ -127,17 +132,10 @@ class BaseCircuitTerminationImportForm(forms.ModelForm): label=_('Termination'), choices=CircuitTerminationSideChoices, ) - site = CSVModelChoiceField( - label=_('Site'), - queryset=Site.objects.all(), - to_field_name='name', - required=False - ) - provider_network = CSVModelChoiceField( - label=_('Provider network'), - queryset=ProviderNetwork.objects.all(), - to_field_name='name', - required=False + termination_type = CSVContentTypeField( + queryset=ContentType.objects.filter(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES), + required=False, + label=_('Termination type (app & model)') ) @@ -145,9 +143,12 @@ class CircuitTerminationImportRelatedForm(BaseCircuitTerminationImportForm): class Meta: model = CircuitTermination fields = [ - 'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id', + 'circuit', 'term_side', 'termination_type', 'termination_id', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', 'description' ] + labels = { + 'termination_id': _('Termination ID'), + } class CircuitTerminationImportForm(NetBoxModelImportForm, BaseCircuitTerminationImportForm): @@ -155,9 +156,12 @@ class CircuitTerminationImportForm(NetBoxModelImportForm, BaseCircuitTermination class Meta: model = CircuitTermination fields = [ - 'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id', + 'circuit', 'term_side', 'termination_type', 'termination_id', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', 'description', 'tags' ] + labels = { + 'termination_id': _('Termination ID'), + } class CircuitGroupImportForm(NetBoxModelImportForm): @@ -179,3 +183,73 @@ class CircuitGroupAssignmentImportForm(NetBoxModelImportForm): class Meta: model = CircuitGroupAssignment fields = ('circuit', 'group', 'priority') + + +class VirtualCircuitImportForm(NetBoxModelImportForm): + provider_network = CSVModelChoiceField( + label=_('Provider network'), + queryset=ProviderNetwork.objects.all(), + to_field_name='name', + help_text=_('The network to which this virtual circuit belongs') + ) + provider_account = CSVModelChoiceField( + label=_('Provider account'), + queryset=ProviderAccount.objects.all(), + to_field_name='account', + help_text=_('Assigned provider account (if any)'), + required=False + ) + status = CSVChoiceField( + label=_('Status'), + choices=CircuitStatusChoices, + help_text=_('Operational status') + ) + tenant = CSVModelChoiceField( + label=_('Tenant'), + queryset=Tenant.objects.all(), + required=False, + to_field_name='name', + help_text=_('Assigned tenant') + ) + + class Meta: + model = VirtualCircuit + fields = [ + 'cid', 'provider_network', 'provider_account', 'status', 'tenant', 'description', 'comments', 'tags', + ] + + +class BaseVirtualCircuitTerminationImportForm(forms.ModelForm): + virtual_circuit = CSVModelChoiceField( + label=_('Virtual circuit'), + queryset=VirtualCircuit.objects.all(), + to_field_name='cid', + ) + role = CSVChoiceField( + label=_('Role'), + choices=VirtualCircuitTerminationRoleChoices, + help_text=_('Operational role') + ) + interface = CSVModelChoiceField( + label=_('Interface'), + queryset=Interface.objects.all(), + to_field_name='pk', + ) + + +class VirtualCircuitTerminationImportRelatedForm(BaseVirtualCircuitTerminationImportForm): + + class Meta: + model = VirtualCircuitTermination + fields = [ + 'virtual_circuit', 'role', 'interface', 'description', + ] + + +class VirtualCircuitTerminationImportForm(NetBoxModelImportForm, BaseVirtualCircuitTerminationImportForm): + + class Meta: + model = VirtualCircuitTermination + fields = [ + 'virtual_circuit', 'role', 'interface', 'description', 'tags', + ] diff --git a/netbox/circuits/forms/filtersets.py b/netbox/circuits/forms/filtersets.py index 2e9b358e8..47ce24d97 100644 --- a/netbox/circuits/forms/filtersets.py +++ b/netbox/circuits/forms/filtersets.py @@ -1,9 +1,12 @@ from django import forms from django.utils.translation import gettext as _ -from circuits.choices import CircuitCommitRateChoices, CircuitPriorityChoices, CircuitStatusChoices, CircuitTerminationSideChoices +from circuits.choices import ( + CircuitCommitRateChoices, CircuitPriorityChoices, CircuitStatusChoices, CircuitTerminationSideChoices, + VirtualCircuitTerminationRoleChoices, +) from circuits.models import * -from dcim.models import Region, Site, SiteGroup +from dcim.models import Location, Region, Site, SiteGroup from ipam.models import ASN from netbox.choices import DistanceUnitChoices from netbox.forms import NetBoxModelFilterSetForm @@ -22,6 +25,8 @@ __all__ = ( 'ProviderFilterForm', 'ProviderAccountFilterForm', 'ProviderNetworkFilterForm', + 'VirtualCircuitFilterForm', + 'VirtualCircuitTerminationFilterForm', ) @@ -116,7 +121,10 @@ class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFi fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('provider_id', 'provider_account_id', 'provider_network_id', name=_('Provider')), - FieldSet('type_id', 'status', 'install_date', 'termination_date', 'commit_rate', 'distance', 'distance_unit', name=_('Attributes')), + FieldSet( + 'type_id', 'status', 'install_date', 'termination_date', 'commit_rate', 'distance', 'distance_unit', + name=_('Attributes') + ), FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), @@ -207,18 +215,29 @@ class CircuitTerminationFilterForm(NetBoxModelFilterSetForm): fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('circuit_id', 'term_side', name=_('Circuit')), - FieldSet('provider_id', 'provider_network_id', name=_('Provider')), - FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')), + FieldSet('provider_id', name=_('Provider')), + FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Termination')), + ) + region_id = DynamicModelMultipleChoiceField( + queryset=Region.objects.all(), + required=False, + label=_('Region') + ) + site_group_id = DynamicModelMultipleChoiceField( + queryset=SiteGroup.objects.all(), + required=False, + label=_('Site group') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), required=False, - query_params={ - 'region_id': '$region_id', - 'site_group_id': '$site_group_id', - }, label=_('Site') ) + location_id = DynamicModelMultipleChoiceField( + queryset=Location.objects.all(), + required=False, + label=_('Location') + ) circuit_id = DynamicModelMultipleChoiceField( queryset=Circuit.objects.all(), required=False, @@ -281,3 +300,74 @@ class CircuitGroupAssignmentFilterForm(NetBoxModelFilterSetForm): required=False ) tag = TagFilterField(model) + + +class VirtualCircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm): + model = VirtualCircuit + fieldsets = ( + FieldSet('q', 'filter_id', 'tag'), + FieldSet('provider_id', 'provider_account_id', 'provider_network_id', name=_('Provider')), + FieldSet('status', name=_('Attributes')), + FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), + ) + selector_fields = ('filter_id', 'q', 'provider_id', 'provider_network_id') + provider_id = DynamicModelMultipleChoiceField( + queryset=Provider.objects.all(), + required=False, + label=_('Provider') + ) + provider_account_id = DynamicModelMultipleChoiceField( + queryset=ProviderAccount.objects.all(), + required=False, + query_params={ + 'provider_id': '$provider_id' + }, + label=_('Provider account') + ) + provider_network_id = DynamicModelMultipleChoiceField( + queryset=ProviderNetwork.objects.all(), + required=False, + query_params={ + 'provider_id': '$provider_id' + }, + label=_('Provider network') + ) + status = forms.MultipleChoiceField( + label=_('Status'), + choices=CircuitStatusChoices, + required=False + ) + tag = TagFilterField(model) + + +class VirtualCircuitTerminationFilterForm(NetBoxModelFilterSetForm): + model = VirtualCircuitTermination + fieldsets = ( + FieldSet('q', 'filter_id', 'tag'), + FieldSet('virtual_circuit_id', 'role', name=_('Virtual circuit')), + FieldSet('provider_id', 'provider_account_id', 'provider_network_id', name=_('Provider')), + ) + virtual_circuit_id = DynamicModelMultipleChoiceField( + queryset=VirtualCircuit.objects.all(), + required=False, + label=_('Virtual circuit') + ) + role = forms.MultipleChoiceField( + label=_('Role'), + choices=VirtualCircuitTerminationRoleChoices, + required=False + ) + provider_network_id = DynamicModelMultipleChoiceField( + queryset=ProviderNetwork.objects.all(), + required=False, + query_params={ + 'provider_id': '$provider_id' + }, + label=_('Provider network') + ) + provider_id = DynamicModelMultipleChoiceField( + queryset=Provider.objects.all(), + required=False, + label=_('Provider') + ) + tag = TagFilterField(model) diff --git a/netbox/circuits/forms/model_forms.py b/netbox/circuits/forms/model_forms.py index e00034a10..e43c37525 100644 --- a/netbox/circuits/forms/model_forms.py +++ b/netbox/circuits/forms/model_forms.py @@ -1,14 +1,24 @@ +from django import forms +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import gettext_lazy as _ -from circuits.choices import CircuitCommitRateChoices, CircuitTerminationPortSpeedChoices +from circuits.choices import ( + CircuitCommitRateChoices, CircuitTerminationPortSpeedChoices, VirtualCircuitTerminationRoleChoices, +) +from circuits.constants import * from circuits.models import * -from dcim.models import Site +from dcim.models import Interface, Site from ipam.models import ASN from netbox.forms import NetBoxModelForm from tenancy.forms import TenancyForm -from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField -from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups -from utilities.forms.widgets import DatePicker, NumberWithOptions +from utilities.forms import get_field_value +from utilities.forms.fields import ( + CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField, +) +from utilities.forms.rendering import FieldSet, InlineFields +from utilities.forms.widgets import DatePicker, HTMXSelect, NumberWithOptions +from utilities.templatetags.builtins.filters import bettertitle __all__ = ( 'CircuitForm', @@ -19,6 +29,8 @@ __all__ = ( 'ProviderForm', 'ProviderAccountForm', 'ProviderNetworkForm', + 'VirtualCircuitForm', + 'VirtualCircuitTerminationForm', ) @@ -45,7 +57,9 @@ class ProviderForm(NetBoxModelForm): class ProviderAccountForm(NetBoxModelForm): provider = DynamicModelChoiceField( label=_('Provider'), - queryset=Provider.objects.all() + queryset=Provider.objects.all(), + selector=True, + quick_add=True ) comments = CommentField() @@ -59,7 +73,9 @@ class ProviderAccountForm(NetBoxModelForm): class ProviderNetworkForm(NetBoxModelForm): provider = DynamicModelChoiceField( label=_('Provider'), - queryset=Provider.objects.all() + queryset=Provider.objects.all(), + selector=True, + quick_add=True ) comments = CommentField() @@ -92,7 +108,8 @@ class CircuitForm(TenancyForm, NetBoxModelForm): provider = DynamicModelChoiceField( label=_('Provider'), queryset=Provider.objects.all(), - selector=True + selector=True, + quick_add=True ) provider_account = DynamicModelChoiceField( label=_('Provider account'), @@ -103,7 +120,8 @@ class CircuitForm(TenancyForm, NetBoxModelForm): } ) type = DynamicModelChoiceField( - queryset=CircuitType.objects.all() + queryset=CircuitType.objects.all(), + quick_add=True ) comments = CommentField() @@ -144,26 +162,24 @@ class CircuitTerminationForm(NetBoxModelForm): queryset=Circuit.objects.all(), selector=True ) - site = DynamicModelChoiceField( - label=_('Site'), - queryset=Site.objects.all(), + termination_type = ContentTypeChoiceField( + queryset=ContentType.objects.filter(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES), + widget=HTMXSelect(), required=False, - selector=True + label=_('Termination type') ) - provider_network = DynamicModelChoiceField( - label=_('Provider network'), - queryset=ProviderNetwork.objects.all(), + termination = DynamicModelChoiceField( + label=_('Termination'), + queryset=Site.objects.none(), # Initial queryset required=False, + disabled=True, selector=True ) fieldsets = ( FieldSet( 'circuit', 'term_side', 'description', 'tags', - TabbedGroups( - FieldSet('site', name=_('Site')), - FieldSet('provider_network', name=_('Provider Network')), - ), + 'termination_type', 'termination', 'mark_connected', name=_('Circuit Termination') ), FieldSet('port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', name=_('Termination Details')), @@ -172,7 +188,7 @@ class CircuitTerminationForm(NetBoxModelForm): class Meta: model = CircuitTermination fields = [ - 'circuit', 'term_side', 'site', 'provider_network', 'mark_connected', 'port_speed', 'upstream_speed', + 'circuit', 'term_side', 'termination_type', 'mark_connected', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', 'description', 'tags', ] widgets = { @@ -184,6 +200,36 @@ class CircuitTerminationForm(NetBoxModelForm): ), } + def __init__(self, *args, **kwargs): + instance = kwargs.get('instance') + initial = kwargs.get('initial', {}) + + if instance is not None and instance.termination: + initial['termination'] = instance.termination + kwargs['initial'] = initial + + super().__init__(*args, **kwargs) + + if termination_type_id := get_field_value(self, 'termination_type'): + try: + termination_type = ContentType.objects.get(pk=termination_type_id) + model = termination_type.model_class() + self.fields['termination'].queryset = model.objects.all() + self.fields['termination'].widget.attrs['selector'] = model._meta.label_lower + self.fields['termination'].disabled = False + self.fields['termination'].label = _(bettertitle(model._meta.verbose_name)) + except ObjectDoesNotExist: + pass + + if self.instance and termination_type_id != self.instance.termination_type_id: + self.initial['termination'] = None + + def clean(self): + super().clean() + + # Assign the selected termination (if any) + self.instance.termination = self.cleaned_data.get('termination') + class CircuitGroupForm(TenancyForm, NetBoxModelForm): slug = SlugField() @@ -216,3 +262,66 @@ class CircuitGroupAssignmentForm(NetBoxModelForm): fields = [ 'group', 'circuit', 'priority', 'tags', ] + + +class VirtualCircuitForm(TenancyForm, NetBoxModelForm): + provider_network = DynamicModelChoiceField( + label=_('Provider network'), + queryset=ProviderNetwork.objects.all(), + selector=True + ) + provider_account = DynamicModelChoiceField( + label=_('Provider account'), + queryset=ProviderAccount.objects.all(), + required=False + ) + comments = CommentField() + + fieldsets = ( + FieldSet( + 'provider_network', 'provider_account', 'cid', 'status', 'description', 'tags', name=_('Virtual circuit'), + ), + FieldSet('tenant_group', 'tenant', name=_('Tenancy')), + ) + + class Meta: + model = VirtualCircuit + fields = [ + 'cid', 'provider_network', 'provider_account', 'status', 'description', 'tenant_group', 'tenant', + 'comments', 'tags', + ] + + +class VirtualCircuitTerminationForm(NetBoxModelForm): + virtual_circuit = DynamicModelChoiceField( + label=_('Virtual circuit'), + queryset=VirtualCircuit.objects.all(), + selector=True + ) + role = forms.ChoiceField( + choices=VirtualCircuitTerminationRoleChoices, + widget=HTMXSelect(), + label=_('Role') + ) + interface = DynamicModelChoiceField( + label=_('Interface'), + queryset=Interface.objects.all(), + selector=True, + query_params={ + 'kind': 'virtual', + 'virtual_circuit_termination_id': 'null', + }, + context={ + 'parent': 'device', + } + ) + + fieldsets = ( + FieldSet('virtual_circuit', 'role', 'interface', 'description', 'tags'), + ) + + class Meta: + model = VirtualCircuitTermination + fields = [ + 'virtual_circuit', 'role', 'interface', 'description', 'tags', + ] diff --git a/netbox/circuits/graphql/filters.py b/netbox/circuits/graphql/filters.py index b8398b2b9..36ddc25b2 100644 --- a/netbox/circuits/graphql/filters.py +++ b/netbox/circuits/graphql/filters.py @@ -4,14 +4,16 @@ from circuits import filtersets, models from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin __all__ = ( - 'CircuitTerminationFilter', 'CircuitFilter', 'CircuitGroupAssignmentFilter', 'CircuitGroupFilter', + 'CircuitTerminationFilter', 'CircuitTypeFilter', 'ProviderFilter', 'ProviderAccountFilter', 'ProviderNetworkFilter', + 'VirtualCircuitFilter', + 'VirtualCircuitTerminationFilter', ) @@ -61,3 +63,15 @@ class ProviderAccountFilter(BaseFilterMixin): @autotype_decorator(filtersets.ProviderNetworkFilterSet) class ProviderNetworkFilter(BaseFilterMixin): pass + + +@strawberry_django.filter(models.VirtualCircuit, lookups=True) +@autotype_decorator(filtersets.VirtualCircuitFilterSet) +class VirtualCircuitFilter(BaseFilterMixin): + pass + + +@strawberry_django.filter(models.VirtualCircuitTermination, lookups=True) +@autotype_decorator(filtersets.VirtualCircuitTerminationFilterSet) +class VirtualCircuitTerminationFilter(BaseFilterMixin): + pass diff --git a/netbox/circuits/graphql/schema.py b/netbox/circuits/graphql/schema.py index ac23421ce..9c683ce45 100644 --- a/netbox/circuits/graphql/schema.py +++ b/netbox/circuits/graphql/schema.py @@ -31,3 +31,9 @@ class CircuitsQuery: provider_network: ProviderNetworkType = strawberry_django.field() provider_network_list: List[ProviderNetworkType] = strawberry_django.field() + + virtual_circuit: VirtualCircuitType = strawberry_django.field() + virtual_circuit_list: List[VirtualCircuitType] = strawberry_django.field() + + virtual_circuit_termination: VirtualCircuitTerminationType = strawberry_django.field() + virtual_circuit_termination_list: List[VirtualCircuitTerminationType] = strawberry_django.field() diff --git a/netbox/circuits/graphql/types.py b/netbox/circuits/graphql/types.py index 45f0d065d..f2703b207 100644 --- a/netbox/circuits/graphql/types.py +++ b/netbox/circuits/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List +from typing import Annotated, List, Union import strawberry import strawberry_django @@ -19,6 +19,8 @@ __all__ = ( 'ProviderType', 'ProviderAccountType', 'ProviderNetworkType', + 'VirtualCircuitTerminationType', + 'VirtualCircuitType', ) @@ -59,13 +61,21 @@ class ProviderNetworkType(NetBoxObjectType): @strawberry_django.type( models.CircuitTermination, - fields='__all__', + exclude=('termination_type', 'termination_id', '_location', '_region', '_site', '_site_group', '_provider_network'), filters=CircuitTerminationFilter ) class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType): circuit: Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')] - provider_network: Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')] | None - site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] | None + + @strawberry_django.field + def termination(self) -> Annotated[Union[ + Annotated["LocationType", strawberry.lazy('dcim.graphql.types')], + Annotated["RegionType", strawberry.lazy('dcim.graphql.types')], + Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')], + Annotated["SiteType", strawberry.lazy('dcim.graphql.types')], + Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')], + ], strawberry.union("CircuitTerminationTerminationType")] | None: + return self.termination @strawberry_django.type( @@ -112,3 +122,32 @@ class CircuitGroupType(OrganizationalObjectType): class CircuitGroupAssignmentType(TagsMixin, BaseObjectType): group: Annotated["CircuitGroupType", strawberry.lazy('circuits.graphql.types')] circuit: Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')] + + +@strawberry_django.type( + models.VirtualCircuitTermination, + fields='__all__', + filters=VirtualCircuitTerminationFilter +) +class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): + virtual_circuit: Annotated[ + "VirtualCircuitType", + strawberry.lazy('circuits.graphql.types') + ] = strawberry_django.field(select_related=["virtual_circuit"]) + interface: Annotated[ + "InterfaceType", + strawberry.lazy('dcim.graphql.types') + ] = strawberry_django.field(select_related=["interface"]) + + +@strawberry_django.type( + models.VirtualCircuit, + fields='__all__', + filters=VirtualCircuitFilter +) +class VirtualCircuitType(NetBoxObjectType): + provider_network: ProviderNetworkType = strawberry_django.field(select_related=["provider_network"]) + provider_account: ProviderAccountType | None + tenant: TenantType | None + + terminations: List[VirtualCircuitTerminationType] diff --git a/netbox/circuits/migrations/0001_squashed.py b/netbox/circuits/migrations/0001_squashed.py index 96fa3c086..0b3d729e6 100644 --- a/netbox/circuits/migrations/0001_squashed.py +++ b/netbox/circuits/migrations/0001_squashed.py @@ -5,11 +5,9 @@ import django.db.models.deletion class Migration(migrations.Migration): - initial = True - dependencies = [ - ] + dependencies = [] replaces = [ ('circuits', '0001_initial'), @@ -98,7 +96,12 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=100)), ('description', models.CharField(blank=True, max_length=200)), ('comments', models.TextField(blank=True)), - ('provider', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='networks', to='circuits.provider')), + ( + 'provider', + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='networks', to='circuits.provider' + ), + ), ], options={ 'ordering': ('provider', 'name'), diff --git a/netbox/circuits/migrations/0002_squashed_0029.py b/netbox/circuits/migrations/0002_squashed_0029.py index 11fcbd6e6..cb61d8feb 100644 --- a/netbox/circuits/migrations/0002_squashed_0029.py +++ b/netbox/circuits/migrations/0002_squashed_0029.py @@ -4,7 +4,6 @@ import taggit.managers class Migration(migrations.Migration): - dependencies = [ ('dcim', '0001_initial'), ('contenttypes', '0002_remove_content_type_name'), @@ -58,32 +57,56 @@ class Migration(migrations.Migration): migrations.AddField( model_name='circuittermination', name='_cable_peer_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='circuittermination', name='cable', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable' + ), ), migrations.AddField( model_name='circuittermination', name='circuit', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='circuits.circuit'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='circuits.circuit' + ), ), migrations.AddField( model_name='circuittermination', name='provider_network', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='circuits.providernetwork'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='circuit_terminations', + to='circuits.providernetwork', + ), ), migrations.AddField( model_name='circuittermination', name='site', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='dcim.site'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='circuit_terminations', + to='dcim.site', + ), ), migrations.AddField( model_name='circuit', name='provider', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.provider'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.provider' + ), ), migrations.AddField( model_name='circuit', @@ -93,26 +116,50 @@ class Migration(migrations.Migration): migrations.AddField( model_name='circuit', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='circuits', + to='tenancy.tenant', + ), ), migrations.AddField( model_name='circuit', name='termination_a', - field=models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='circuits.circuittermination'), + field=models.ForeignKey( + blank=True, + editable=False, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='circuits.circuittermination', + ), ), migrations.AddField( model_name='circuit', name='termination_z', - field=models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='circuits.circuittermination'), + field=models.ForeignKey( + blank=True, + editable=False, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='circuits.circuittermination', + ), ), migrations.AddField( model_name='circuit', name='type', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.circuittype'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.circuittype' + ), ), migrations.AddConstraint( model_name='providernetwork', - constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_providernetwork_provider_name'), + constraint=models.UniqueConstraint( + fields=('provider', 'name'), name='circuits_providernetwork_provider_name' + ), ), migrations.AlterUniqueTogether( name='providernetwork', diff --git a/netbox/circuits/migrations/0003_squashed_0037.py b/netbox/circuits/migrations/0003_squashed_0037.py index 69c3e1c68..c536e422f 100644 --- a/netbox/circuits/migrations/0003_squashed_0037.py +++ b/netbox/circuits/migrations/0003_squashed_0037.py @@ -5,7 +5,6 @@ import utilities.json class Migration(migrations.Migration): - replaces = [ ('circuits', '0003_extend_tag_support'), ('circuits', '0004_rename_cable_peer'), @@ -14,7 +13,7 @@ class Migration(migrations.Migration): ('circuits', '0034_created_datetimefield'), ('circuits', '0035_provider_asns'), ('circuits', '0036_circuit_termination_date_tags_custom_fields'), - ('circuits', '0037_new_cabling_models') + ('circuits', '0037_new_cabling_models'), ] dependencies = [ diff --git a/netbox/circuits/migrations/0038_squashed_0042.py b/netbox/circuits/migrations/0038_squashed_0042.py index f57fde3db..fa944b763 100644 --- a/netbox/circuits/migrations/0038_squashed_0042.py +++ b/netbox/circuits/migrations/0038_squashed_0042.py @@ -6,13 +6,12 @@ import utilities.json class Migration(migrations.Migration): - replaces = [ ('circuits', '0038_cabling_cleanup'), ('circuits', '0039_unique_constraints'), ('circuits', '0040_provider_remove_deprecated_fields'), ('circuits', '0041_standardize_description_comments'), - ('circuits', '0042_provideraccount') + ('circuits', '0042_provideraccount'), ] dependencies = [ @@ -51,11 +50,15 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='circuittermination', - constraint=models.UniqueConstraint(fields=('circuit', 'term_side'), name='circuits_circuittermination_unique_circuit_term_side'), + constraint=models.UniqueConstraint( + fields=('circuit', 'term_side'), name='circuits_circuittermination_unique_circuit_term_side' + ), ), migrations.AddConstraint( model_name='providernetwork', - constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_providernetwork_unique_provider_name'), + constraint=models.UniqueConstraint( + fields=('provider', 'name'), name='circuits_providernetwork_unique_provider_name' + ), ), migrations.RemoveField( model_name='provider', @@ -84,12 +87,20 @@ class Migration(migrations.Migration): ('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)), + ( + '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)), ('account', models.CharField(max_length=100)), ('name', models.CharField(blank=True, max_length=100)), - ('provider', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='accounts', to='circuits.provider')), + ( + 'provider', + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='accounts', to='circuits.provider' + ), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), ], options={ @@ -98,11 +109,17 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='provideraccount', - constraint=models.UniqueConstraint(condition=models.Q(('name', ''), _negated=True), fields=('provider', 'name'), name='circuits_provideraccount_unique_provider_name'), + constraint=models.UniqueConstraint( + condition=models.Q(('name', ''), _negated=True), + fields=('provider', 'name'), + name='circuits_provideraccount_unique_provider_name', + ), ), migrations.AddConstraint( model_name='provideraccount', - constraint=models.UniqueConstraint(fields=('provider', 'account'), name='circuits_provideraccount_unique_provider_account'), + constraint=models.UniqueConstraint( + fields=('provider', 'account'), name='circuits_provideraccount_unique_provider_account' + ), ), migrations.RemoveField( model_name='provider', @@ -111,7 +128,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='circuit', name='provider_account', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.provideraccount'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='circuits', + to='circuits.provideraccount', + ), preserve_default=False, ), migrations.AlterModelOptions( @@ -120,6 +143,8 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='circuit', - constraint=models.UniqueConstraint(fields=('provider_account', 'cid'), name='circuits_circuit_unique_provideraccount_cid'), + constraint=models.UniqueConstraint( + fields=('provider_account', 'cid'), name='circuits_circuit_unique_provideraccount_cid' + ), ), ] diff --git a/netbox/circuits/migrations/0044_circuit_groups.py b/netbox/circuits/migrations/0044_circuit_groups.py index 98c3b8f3d..08f6bc158 100644 --- a/netbox/circuits/migrations/0044_circuit_groups.py +++ b/netbox/circuits/migrations/0044_circuit_groups.py @@ -5,7 +5,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('circuits', '0043_circuittype_color'), ('extras', '0119_notifications'), diff --git a/netbox/circuits/migrations/0045_circuit_distance.py b/netbox/circuits/migrations/0045_circuit_distance.py index 6c970339d..9e512e7ee 100644 --- a/netbox/circuits/migrations/0045_circuit_distance.py +++ b/netbox/circuits/migrations/0045_circuit_distance.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('circuits', '0044_circuit_groups'), ] diff --git a/netbox/circuits/migrations/0046_charfield_null_choices.py b/netbox/circuits/migrations/0046_charfield_null_choices.py new file mode 100644 index 000000000..2a8bcde90 --- /dev/null +++ b/netbox/circuits/migrations/0046_charfield_null_choices.py @@ -0,0 +1,39 @@ +from django.db import migrations, models + + +def set_null_values(apps, schema_editor): + """ + Replace empty strings with null values. + """ + Circuit = apps.get_model('circuits', 'Circuit') + CircuitGroupAssignment = apps.get_model('circuits', 'CircuitGroupAssignment') + CircuitTermination = apps.get_model('circuits', 'CircuitTermination') + + Circuit.objects.filter(distance_unit='').update(distance_unit=None) + CircuitGroupAssignment.objects.filter(priority='').update(priority=None) + CircuitTermination.objects.filter(cable_end='').update(cable_end=None) + + +class Migration(migrations.Migration): + dependencies = [ + ('circuits', '0045_circuit_distance'), + ] + + operations = [ + migrations.AlterField( + model_name='circuit', + name='distance_unit', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='circuitgroupassignment', + name='priority', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='circuittermination', + name='cable_end', + field=models.CharField(blank=True, max_length=1, null=True), + ), + migrations.RunPython(code=set_null_values, reverse_code=migrations.RunPython.noop), + ] diff --git a/netbox/circuits/migrations/0047_circuittermination__termination.py b/netbox/circuits/migrations/0047_circuittermination__termination.py new file mode 100644 index 000000000..f78e17ec3 --- /dev/null +++ b/netbox/circuits/migrations/0047_circuittermination__termination.py @@ -0,0 +1,53 @@ +import django.db.models.deletion +from django.db import migrations, models + + +def copy_site_assignments(apps, schema_editor): + """ + Copy site ForeignKey values to the Termination GFK. + """ + ContentType = apps.get_model('contenttypes', 'ContentType') + CircuitTermination = apps.get_model('circuits', 'CircuitTermination') + Site = apps.get_model('dcim', 'Site') + + CircuitTermination.objects.filter(site__isnull=False).update( + termination_type=ContentType.objects.get_for_model(Site), termination_id=models.F('site_id') + ) + + ProviderNetwork = apps.get_model('circuits', 'ProviderNetwork') + CircuitTermination.objects.filter(provider_network__isnull=False).update( + termination_type=ContentType.objects.get_for_model(ProviderNetwork), + termination_id=models.F('provider_network_id'), + ) + + +class Migration(migrations.Migration): + dependencies = [ + ('circuits', '0046_charfield_null_choices'), + ('contenttypes', '0002_remove_content_type_name'), + ('dcim', '0193_poweroutlet_color'), + ] + + operations = [ + migrations.AddField( + model_name='circuittermination', + name='termination_id', + field=models.PositiveBigIntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='circuittermination', + name='termination_type', + field=models.ForeignKey( + blank=True, + limit_choices_to=models.Q( + ('model__in', ('region', 'sitegroup', 'site', 'location', 'providernetwork')) + ), + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), + ), + # Copy over existing site assignments + migrations.RunPython(code=copy_site_assignments, reverse_code=migrations.RunPython.noop), + ] diff --git a/netbox/circuits/migrations/0048_circuitterminations_cached_relations.py b/netbox/circuits/migrations/0048_circuitterminations_cached_relations.py new file mode 100644 index 000000000..fc1cef0e5 --- /dev/null +++ b/netbox/circuits/migrations/0048_circuitterminations_cached_relations.py @@ -0,0 +1,84 @@ +# Generated by Django 5.0.9 on 2024-10-21 17:34 +import django.db.models.deletion +from django.db import migrations, models + + +def populate_denormalized_fields(apps, schema_editor): + """ + Copy site ForeignKey values to the Termination GFK. + """ + CircuitTermination = apps.get_model('circuits', 'CircuitTermination') + + terminations = CircuitTermination.objects.filter(site__isnull=False).prefetch_related('site') + for termination in terminations: + termination._region_id = termination.site.region_id + termination._site_group_id = termination.site.group_id + termination._site_id = termination.site_id + # Note: Location cannot be set prior to migration + + CircuitTermination.objects.bulk_update(terminations, ['_region', '_site_group', '_site']) + + +class Migration(migrations.Migration): + dependencies = [ + ('circuits', '0047_circuittermination__termination'), + ] + + operations = [ + migrations.AddField( + model_name='circuittermination', + name='_location', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='circuit_terminations', + to='dcim.location', + ), + ), + migrations.AddField( + model_name='circuittermination', + name='_region', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='circuit_terminations', + to='dcim.region', + ), + ), + migrations.AddField( + model_name='circuittermination', + name='_site', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='circuit_terminations', + to='dcim.site', + ), + ), + migrations.AddField( + model_name='circuittermination', + name='_site_group', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='circuit_terminations', + to='dcim.sitegroup', + ), + ), + # Populate denormalized FK values + migrations.RunPython(code=populate_denormalized_fields, reverse_code=migrations.RunPython.noop), + # Delete the site ForeignKey + migrations.RemoveField( + model_name='circuittermination', + name='site', + ), + migrations.RenameField( + model_name='circuittermination', + old_name='provider_network', + new_name='_provider_network', + ), + ] diff --git a/netbox/circuits/migrations/0049_natural_ordering.py b/netbox/circuits/migrations/0049_natural_ordering.py new file mode 100644 index 000000000..556d6ec7c --- /dev/null +++ b/netbox/circuits/migrations/0049_natural_ordering.py @@ -0,0 +1,21 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('circuits', '0048_circuitterminations_cached_relations'), + ('dcim', '0197_natural_sort_collation'), + ] + + operations = [ + migrations.AlterField( + model_name='provider', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=100, unique=True), + ), + migrations.AlterField( + model_name='providernetwork', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=100), + ), + ] diff --git a/netbox/circuits/migrations/0050_virtual_circuits.py b/netbox/circuits/migrations/0050_virtual_circuits.py new file mode 100644 index 000000000..eb451b4ec --- /dev/null +++ b/netbox/circuits/migrations/0050_virtual_circuits.py @@ -0,0 +1,115 @@ +import django.db.models.deletion +import taggit.managers +from django.db import migrations, models + +import utilities.json + + +class Migration(migrations.Migration): + dependencies = [ + ('circuits', '0049_natural_ordering'), + ('dcim', '0196_qinq_svlan'), + ('extras', '0122_charfield_null_choices'), + ('tenancy', '0016_charfield_null_choices'), + ] + + operations = [ + migrations.CreateModel( + name='VirtualCircuit', + 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)), + ('cid', models.CharField(max_length=100)), + ('status', models.CharField(default='active', max_length=50)), + ( + 'provider_account', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='virtual_circuits', + to='circuits.provideraccount', + ), + ), + ( + 'provider_network', + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name='virtual_circuits', + to='circuits.providernetwork', + ), + ), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ( + 'tenant', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='virtual_circuits', + to='tenancy.tenant', + ), + ), + ], + options={ + 'verbose_name': 'circuit', + 'verbose_name_plural': 'circuits', + 'ordering': ['provider_network', 'provider_account', 'cid'], + }, + ), + migrations.CreateModel( + name='VirtualCircuitTermination', + 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), + ), + ('role', models.CharField(default='peer', max_length=50)), + ('description', models.CharField(blank=True, max_length=200)), + ( + 'interface', + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name='virtual_circuit_termination', + to='dcim.interface', + ), + ), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ( + 'virtual_circuit', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='terminations', + to='circuits.virtualcircuit', + ), + ), + ], + options={ + 'verbose_name': 'virtual circuit termination', + 'verbose_name_plural': 'virtual circuit terminations', + 'ordering': ['virtual_circuit', 'role', 'pk'], + }, + ), + migrations.AddConstraint( + model_name='virtualcircuit', + constraint=models.UniqueConstraint( + fields=('provider_network', 'cid'), name='circuits_virtualcircuit_unique_provider_network_cid' + ), + ), + migrations.AddConstraint( + model_name='virtualcircuit', + constraint=models.UniqueConstraint( + fields=('provider_account', 'cid'), name='circuits_virtualcircuit_unique_provideraccount_cid' + ), + ), + ] diff --git a/netbox/circuits/models/__init__.py b/netbox/circuits/models/__init__.py index 7bbaf75d3..77382358b 100644 --- a/netbox/circuits/models/__init__.py +++ b/netbox/circuits/models/__init__.py @@ -1,2 +1,3 @@ from .circuits import * from .providers import * +from .virtual_circuits import * diff --git a/netbox/circuits/models/circuits.py b/netbox/circuits/models/circuits.py index 2df83e97e..5e910b5d5 100644 --- a/netbox/circuits/models/circuits.py +++ b/netbox/circuits/models/circuits.py @@ -1,13 +1,19 @@ +from django.apps import apps +from django.contrib.contenttypes.fields import GenericForeignKey from django.core.exceptions import ValidationError from django.db import models +from django.db.models import Q from django.urls import reverse from django.utils.translation import gettext_lazy as _ from circuits.choices import * +from circuits.constants import * from dcim.models import CabledObjectModel from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel from netbox.models.mixins import DistanceMixin -from netbox.models.features import ContactsMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, ImageAttachmentsMixin, TagsMixin +from netbox.models.features import ( + ContactsMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, ImageAttachmentsMixin, TagsMixin, +) from utilities.fields import ColorField __all__ = ( @@ -187,7 +193,8 @@ class CircuitGroupAssignment(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, verbose_name=_('priority'), max_length=50, choices=CircuitPriorityChoices, - blank=True + blank=True, + null=True ) prerequisite_models = ( 'circuits.Circuit', @@ -229,22 +236,24 @@ class CircuitTermination( term_side = models.CharField( max_length=1, choices=CircuitTerminationSideChoices, - verbose_name=_('termination') + verbose_name=_('termination side') ) - site = models.ForeignKey( - to='dcim.Site', + termination_type = models.ForeignKey( + to='contenttypes.ContentType', on_delete=models.PROTECT, - related_name='circuit_terminations', + limit_choices_to=Q(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES), + related_name='+', blank=True, null=True ) - provider_network = models.ForeignKey( - to='circuits.ProviderNetwork', - on_delete=models.PROTECT, - related_name='circuit_terminations', + termination_id = models.PositiveBigIntegerField( blank=True, null=True ) + termination = GenericForeignKey( + ct_field='termination_type', + fk_field='termination_id' + ) port_speed = models.PositiveIntegerField( verbose_name=_('port speed (Kbps)'), blank=True, @@ -275,6 +284,43 @@ class CircuitTermination( blank=True ) + # Cached associations to enable efficient filtering + _provider_network = models.ForeignKey( + to='circuits.ProviderNetwork', + on_delete=models.PROTECT, + related_name='circuit_terminations', + blank=True, + null=True + ) + _location = models.ForeignKey( + to='dcim.Location', + on_delete=models.CASCADE, + related_name='circuit_terminations', + blank=True, + null=True + ) + _site = models.ForeignKey( + to='dcim.Site', + on_delete=models.CASCADE, + related_name='circuit_terminations', + blank=True, + null=True + ) + _region = models.ForeignKey( + to='dcim.Region', + on_delete=models.CASCADE, + related_name='circuit_terminations', + blank=True, + null=True + ) + _site_group = models.ForeignKey( + to='dcim.SiteGroup', + on_delete=models.CASCADE, + related_name='circuit_terminations', + blank=True, + null=True + ) + class Meta: ordering = ['circuit', 'term_side'] constraints = ( @@ -296,10 +342,35 @@ class CircuitTermination( super().clean() # Must define either site *or* provider network - if self.site is None and self.provider_network is None: - raise ValidationError(_("A circuit termination must attach to either a site or a provider network.")) - if self.site and self.provider_network: - raise ValidationError(_("A circuit termination cannot attach to both a site and a provider network.")) + if self.termination is None: + raise ValidationError(_("A circuit termination must attach to termination.")) + + def save(self, *args, **kwargs): + # Cache objects associated with the terminating object (for filtering) + self.cache_related_objects() + + super().save(*args, **kwargs) + + def cache_related_objects(self): + self._provider_network = self._region = self._site_group = self._site = self._location = None + if self.termination_type: + termination_type = self.termination_type.model_class() + if termination_type == apps.get_model('dcim', 'region'): + self._region = self.termination + elif termination_type == apps.get_model('dcim', 'sitegroup'): + self._site_group = self.termination + elif termination_type == apps.get_model('dcim', 'site'): + self._region = self.termination.region + self._site_group = self.termination.group + self._site = self.termination + elif termination_type == apps.get_model('dcim', 'location'): + self._region = self.termination.site.region + self._site_group = self.termination.site.group + self._site = self.termination.site + self._location = self.termination + elif termination_type == apps.get_model('circuits', 'providernetwork'): + self._provider_network = self.termination + cache_related_objects.alters_data = True def to_objectchange(self, action): objectchange = super().to_objectchange(action) @@ -313,7 +384,7 @@ class CircuitTermination( def get_peer_termination(self): peer_side = 'Z' if self.term_side == 'A' else 'A' try: - return CircuitTermination.objects.prefetch_related('site').get( + return CircuitTermination.objects.prefetch_related('termination').get( circuit=self.circuit, term_side=peer_side ) diff --git a/netbox/circuits/models/providers.py b/netbox/circuits/models/providers.py index f0fe77b1a..be81caa54 100644 --- a/netbox/circuits/models/providers.py +++ b/netbox/circuits/models/providers.py @@ -21,7 +21,8 @@ class Provider(ContactsMixin, PrimaryModel): verbose_name=_('name'), max_length=100, unique=True, - help_text=_('Full name of the provider') + help_text=_('Full name of the provider'), + db_collation="natural_sort" ) slug = models.SlugField( verbose_name=_('slug'), @@ -95,7 +96,8 @@ class ProviderNetwork(PrimaryModel): """ name = models.CharField( verbose_name=_('name'), - max_length=100 + max_length=100, + db_collation="natural_sort" ) provider = models.ForeignKey( to='circuits.Provider', diff --git a/netbox/circuits/models/virtual_circuits.py b/netbox/circuits/models/virtual_circuits.py new file mode 100644 index 000000000..ced68c105 --- /dev/null +++ b/netbox/circuits/models/virtual_circuits.py @@ -0,0 +1,164 @@ +from functools import cached_property + +from django.core.exceptions import ValidationError +from django.db import models +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ + +from circuits.choices import * +from netbox.models import ChangeLoggedModel, PrimaryModel +from netbox.models.features import CustomFieldsMixin, CustomLinksMixin, TagsMixin + +__all__ = ( + 'VirtualCircuit', + 'VirtualCircuitTermination', +) + + +class VirtualCircuit(PrimaryModel): + """ + A virtual connection between two or more endpoints, delivered across one or more physical circuits. + """ + cid = models.CharField( + max_length=100, + verbose_name=_('circuit ID'), + help_text=_('Unique circuit ID') + ) + provider_network = models.ForeignKey( + to='circuits.ProviderNetwork', + on_delete=models.PROTECT, + related_name='virtual_circuits' + ) + provider_account = models.ForeignKey( + to='circuits.ProviderAccount', + on_delete=models.PROTECT, + related_name='virtual_circuits', + blank=True, + null=True + ) + status = models.CharField( + verbose_name=_('status'), + max_length=50, + choices=CircuitStatusChoices, + default=CircuitStatusChoices.STATUS_ACTIVE + ) + tenant = models.ForeignKey( + to='tenancy.Tenant', + on_delete=models.PROTECT, + related_name='virtual_circuits', + blank=True, + null=True + ) + + clone_fields = ( + 'provider_network', 'provider_account', 'status', 'tenant', 'description', + ) + prerequisite_models = ( + 'circuits.ProviderNetwork', + ) + + class Meta: + ordering = ['provider_network', 'provider_account', 'cid'] + constraints = ( + models.UniqueConstraint( + fields=('provider_network', 'cid'), + name='%(app_label)s_%(class)s_unique_provider_network_cid' + ), + models.UniqueConstraint( + fields=('provider_account', 'cid'), + name='%(app_label)s_%(class)s_unique_provideraccount_cid' + ), + ) + verbose_name = _('virtual circuit') + verbose_name_plural = _('virtual circuits') + + def __str__(self): + return self.cid + + def get_status_color(self): + return CircuitStatusChoices.colors.get(self.status) + + def clean(self): + super().clean() + + if self.provider_account and self.provider_network.provider != self.provider_account.provider: + raise ValidationError({ + 'provider_account': "The assigned account must belong to the provider of the assigned network." + }) + + @property + def provider(self): + return self.provider_network.provider + + +class VirtualCircuitTermination( + CustomFieldsMixin, + CustomLinksMixin, + TagsMixin, + ChangeLoggedModel +): + virtual_circuit = models.ForeignKey( + to='circuits.VirtualCircuit', + on_delete=models.CASCADE, + related_name='terminations' + ) + role = models.CharField( + verbose_name=_('role'), + max_length=50, + choices=VirtualCircuitTerminationRoleChoices, + default=VirtualCircuitTerminationRoleChoices.ROLE_PEER + ) + interface = models.OneToOneField( + to='dcim.Interface', + on_delete=models.CASCADE, + related_name='virtual_circuit_termination' + ) + description = models.CharField( + verbose_name=_('description'), + max_length=200, + blank=True + ) + + class Meta: + ordering = ['virtual_circuit', 'role', 'pk'] + verbose_name = _('virtual circuit termination') + verbose_name_plural = _('virtual circuit terminations') + + def __str__(self): + return f'{self.virtual_circuit}: {self.get_role_display()} termination' + + def get_absolute_url(self): + return reverse('circuits:virtualcircuittermination', args=[self.pk]) + + def get_role_color(self): + return VirtualCircuitTerminationRoleChoices.colors.get(self.role) + + def to_objectchange(self, action): + objectchange = super().to_objectchange(action) + objectchange.related_object = self.virtual_circuit + return objectchange + + @property + def parent_object(self): + return self.virtual_circuit + + @cached_property + def peer_terminations(self): + if self.role == VirtualCircuitTerminationRoleChoices.ROLE_PEER: + return self.virtual_circuit.terminations.exclude(pk=self.pk).filter( + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER + ) + if self.role == VirtualCircuitTerminationRoleChoices.ROLE_HUB: + return self.virtual_circuit.terminations.filter( + role=VirtualCircuitTerminationRoleChoices.ROLE_SPOKE + ) + if self.role == VirtualCircuitTerminationRoleChoices.ROLE_SPOKE: + return self.virtual_circuit.terminations.filter( + role=VirtualCircuitTerminationRoleChoices.ROLE_HUB + ) + + def clean(self): + super().clean() + + if self.interface and not self.interface.is_virtual: + raise ValidationError("Virtual circuits may be terminated only to virtual interfaces.") diff --git a/netbox/circuits/search.py b/netbox/circuits/search.py index 7a5711f03..80725c1b8 100644 --- a/netbox/circuits/search.py +++ b/netbox/circuits/search.py @@ -80,3 +80,23 @@ class ProviderNetworkIndex(SearchIndex): ('comments', 5000), ) display_attrs = ('provider', 'service_id', 'description') + + +@register_search +class VirtualCircuitIndex(SearchIndex): + model = models.VirtualCircuit + fields = ( + ('cid', 100), + ('description', 500), + ('comments', 5000), + ) + display_attrs = ('provider', 'provider_network', 'provider_account', 'status', 'tenant', 'description') + + +@register_search +class VirtualCircuitTerminationIndex(SearchIndex): + model = models.VirtualCircuitTermination + fields = ( + ('description', 500), + ) + display_attrs = ('virtual_circuit', 'role', 'description') diff --git a/netbox/circuits/tables/__init__.py b/netbox/circuits/tables/__init__.py index b61c13cae..a436eb88d 100644 --- a/netbox/circuits/tables/__init__.py +++ b/netbox/circuits/tables/__init__.py @@ -1,3 +1,4 @@ from .circuits import * from .columns import * from .providers import * +from .virtual_circuits import * diff --git a/netbox/circuits/tables/circuits.py b/netbox/circuits/tables/circuits.py index e79212a14..dedb1534b 100644 --- a/netbox/circuits/tables/circuits.py +++ b/netbox/circuits/tables/circuits.py @@ -18,10 +18,8 @@ __all__ = ( CIRCUITTERMINATION_LINK = """ -{% if value.site %} - {{ value.site }} -{% elif value.provider_network %} - {{ value.provider_network }} +{% if value.termination %} + {{ value.termination }} {% endif %} """ @@ -44,7 +42,8 @@ class CircuitTypeTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = CircuitType fields = ( - 'pk', 'id', 'name', 'circuit_count', 'color', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions', + 'pk', 'id', 'name', 'circuit_count', 'color', 'description', 'slug', 'tags', 'created', 'last_updated', + 'actions', ) default_columns = ('pk', 'name', 'circuit_count', 'description', 'slug') @@ -63,12 +62,12 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): verbose_name=_('Account') ) status = columns.ChoiceFieldColumn() - termination_a = tables.TemplateColumn( + termination_a = columns.TemplateColumn( template_code=CIRCUITTERMINATION_LINK, orderable=False, verbose_name=_('Side A') ) - termination_z = tables.TemplateColumn( + termination_z = columns.TemplateColumn( template_code=CIRCUITTERMINATION_LINK, orderable=False, verbose_name=_('Side Z') @@ -110,22 +109,54 @@ class CircuitTerminationTable(NetBoxTable): linkify=True, accessor='circuit.provider' ) + term_side = tables.Column( + verbose_name=_('Side') + ) + termination_type = columns.ContentTypeColumn( + verbose_name=_('Termination Type'), + ) + termination = tables.Column( + verbose_name=_('Termination Point'), + linkify=True + ) + + # Termination types site = tables.Column( verbose_name=_('Site'), - linkify=True + linkify=True, + accessor='_site' + ) + site_group = tables.Column( + verbose_name=_('Site Group'), + linkify=True, + accessor='_sitegroup' + ) + region = tables.Column( + verbose_name=_('Region'), + linkify=True, + accessor='_region' + ) + location = tables.Column( + verbose_name=_('Location'), + linkify=True, + accessor='_location' ) provider_network = tables.Column( verbose_name=_('Provider Network'), - linkify=True + linkify=True, + accessor='_provider_network' ) class Meta(NetBoxTable.Meta): model = CircuitTermination fields = ( - 'pk', 'id', 'circuit', 'provider', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed', - 'xconnect_id', 'pp_info', 'description', 'created', 'last_updated', 'actions', + 'pk', 'id', 'circuit', 'provider', 'term_side', 'termination_type', 'termination', 'site_group', 'region', + 'site', 'location', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', + 'description', 'created', 'last_updated', 'actions', + ) + default_columns = ( + 'pk', 'id', 'circuit', 'provider', 'term_side', 'termination_type', 'termination', 'description', ) - default_columns = ('pk', 'id', 'circuit', 'provider', 'term_side', 'description') class CircuitGroupTable(NetBoxTable): diff --git a/netbox/circuits/tables/virtual_circuits.py b/netbox/circuits/tables/virtual_circuits.py new file mode 100644 index 000000000..b7617f297 --- /dev/null +++ b/netbox/circuits/tables/virtual_circuits.py @@ -0,0 +1,95 @@ +import django_tables2 as tables +from django.utils.translation import gettext_lazy as _ + +from circuits.models import * +from netbox.tables import NetBoxTable, columns +from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin + +__all__ = ( + 'VirtualCircuitTable', + 'VirtualCircuitTerminationTable', +) + + +class VirtualCircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): + cid = tables.Column( + linkify=True, + verbose_name=_('Circuit ID') + ) + provider = tables.Column( + accessor=tables.A('provider_network__provider'), + verbose_name=_('Provider'), + linkify=True + ) + provider_network = tables.Column( + linkify=True, + verbose_name=_('Provider network') + ) + provider_account = tables.Column( + linkify=True, + verbose_name=_('Account') + ) + status = columns.ChoiceFieldColumn() + termination_count = columns.LinkedCountColumn( + viewname='circuits:virtualcircuittermination_list', + url_params={'virtual_circuit_id': 'pk'}, + verbose_name=_('Terminations') + ) + comments = columns.MarkdownColumn( + verbose_name=_('Comments') + ) + tags = columns.TagColumn( + url_name='circuits:virtualcircuit_list' + ) + + class Meta(NetBoxTable.Meta): + model = VirtualCircuit + fields = ( + 'pk', 'id', 'cid', 'provider', 'provider_account', 'provider_network', 'status', 'tenant', 'tenant_group', + 'description', 'comments', 'tags', 'created', 'last_updated', + ) + default_columns = ( + 'pk', 'cid', 'provider', 'provider_account', 'provider_network', 'status', 'tenant', 'termination_count', + 'description', + ) + + +class VirtualCircuitTerminationTable(NetBoxTable): + virtual_circuit = tables.Column( + verbose_name=_('Virtual circuit'), + linkify=True + ) + provider = tables.Column( + accessor=tables.A('virtual_circuit__provider_network__provider'), + verbose_name=_('Provider'), + linkify=True + ) + provider_network = tables.Column( + accessor=tables.A('virtual_circuit__provider_network'), + linkify=True, + verbose_name=_('Provider network') + ) + provider_account = tables.Column( + linkify=True, + verbose_name=_('Account') + ) + role = columns.ChoiceFieldColumn() + device = tables.Column( + accessor=tables.A('interface__device'), + linkify=True, + verbose_name=_('Device') + ) + interface = tables.Column( + verbose_name=_('Interface'), + linkify=True + ) + + class Meta(NetBoxTable.Meta): + model = VirtualCircuitTermination + fields = ( + 'pk', 'id', 'virtual_circuit', 'provider', 'provider_network', 'provider_account', 'role', 'interfaces', + 'description', 'created', 'last_updated', 'actions', + ) + default_columns = ( + 'pk', 'id', 'virtual_circuit', 'role', 'device', 'interface', 'description', + ) diff --git a/netbox/circuits/tests/test_api.py b/netbox/circuits/tests/test_api.py index 1edcd531b..92fbbdedf 100644 --- a/netbox/circuits/tests/test_api.py +++ b/netbox/circuits/tests/test_api.py @@ -2,7 +2,8 @@ from django.urls import reverse from circuits.choices import * from circuits.models import * -from dcim.models import Site +from dcim.choices import InterfaceTypeChoices +from dcim.models import Device, DeviceRole, DeviceType, Interface, Manufacturer, Site from ipam.models import ASN, RIR from utilities.testing import APITestCase, APIViewTestCases @@ -120,9 +121,15 @@ class CircuitTest(APIViewTestCases.APIViewTestCase): CircuitType.objects.bulk_create(circuit_types) circuits = ( - Circuit(cid='Circuit 1', provider=providers[0], provider_account=provider_accounts[0], type=circuit_types[0]), - Circuit(cid='Circuit 2', provider=providers[0], provider_account=provider_accounts[0], type=circuit_types[0]), - Circuit(cid='Circuit 3', provider=providers[0], provider_account=provider_accounts[0], type=circuit_types[0]), + Circuit( + cid='Circuit 1', provider=providers[0], provider_account=provider_accounts[0], type=circuit_types[0] + ), + Circuit( + cid='Circuit 2', provider=providers[0], provider_account=provider_accounts[0], type=circuit_types[0] + ), + Circuit( + cid='Circuit 3', provider=providers[0], provider_account=provider_accounts[0], type=circuit_types[0] + ), ) Circuit.objects.bulk_create(circuits) @@ -181,10 +188,10 @@ class CircuitTerminationTest(APIViewTestCases.APIViewTestCase): Circuit.objects.bulk_create(circuits) circuit_terminations = ( - CircuitTermination(circuit=circuits[0], term_side=SIDE_A, site=sites[0]), - CircuitTermination(circuit=circuits[0], term_side=SIDE_Z, provider_network=provider_networks[0]), - CircuitTermination(circuit=circuits[1], term_side=SIDE_A, site=sites[1]), - CircuitTermination(circuit=circuits[1], term_side=SIDE_Z, provider_network=provider_networks[1]), + CircuitTermination(circuit=circuits[0], term_side=SIDE_A, termination=sites[0]), + CircuitTermination(circuit=circuits[0], term_side=SIDE_Z, termination=provider_networks[0]), + CircuitTermination(circuit=circuits[1], term_side=SIDE_A, termination=sites[1]), + CircuitTermination(circuit=circuits[1], term_side=SIDE_Z, termination=provider_networks[1]), ) CircuitTermination.objects.bulk_create(circuit_terminations) @@ -192,13 +199,15 @@ class CircuitTerminationTest(APIViewTestCases.APIViewTestCase): { 'circuit': circuits[2].pk, 'term_side': SIDE_A, - 'site': sites[0].pk, + 'termination_type': 'dcim.site', + 'termination_id': sites[0].pk, 'port_speed': 200000, }, { 'circuit': circuits[2].pk, 'term_side': SIDE_Z, - 'provider_network': provider_networks[0].pk, + 'termination_type': 'circuits.providernetwork', + 'termination_id': provider_networks[0].pk, 'port_speed': 200000, }, ] @@ -395,3 +404,240 @@ class ProviderNetworkTest(APIViewTestCases.APIViewTestCase): 'provider': providers[1].pk, 'description': 'New description', } + + +class VirtualCircuitTest(APIViewTestCases.APIViewTestCase): + model = VirtualCircuit + brief_fields = ['cid', 'description', 'display', 'id', 'provider_network', 'url'] + bulk_update_data = { + 'status': 'planned', + } + + @classmethod + def setUpTestData(cls): + provider = Provider.objects.create(name='Provider 1', slug='provider-1') + provider_network = ProviderNetwork.objects.create(provider=provider, name='Provider Network 1') + provider_account = ProviderAccount.objects.create(provider=provider, account='Provider Account 1') + + virtual_circuits = ( + VirtualCircuit( + provider_network=provider_network, + provider_account=provider_account, + cid='Virtual Circuit 1' + ), + VirtualCircuit( + provider_network=provider_network, + provider_account=provider_account, + cid='Virtual Circuit 2' + ), + VirtualCircuit( + provider_network=provider_network, + provider_account=provider_account, + cid='Virtual Circuit 3' + ), + ) + VirtualCircuit.objects.bulk_create(virtual_circuits) + + cls.create_data = [ + { + 'cid': 'Virtual Circuit 4', + 'provider_network': provider_network.pk, + 'provider_account': provider_account.pk, + 'status': CircuitStatusChoices.STATUS_PLANNED, + }, + { + 'cid': 'Virtual Circuit 5', + 'provider_network': provider_network.pk, + 'provider_account': provider_account.pk, + 'status': CircuitStatusChoices.STATUS_PLANNED, + }, + { + 'cid': 'Virtual Circuit 6', + 'provider_network': provider_network.pk, + 'provider_account': provider_account.pk, + 'status': CircuitStatusChoices.STATUS_PLANNED, + }, + ] + + +class VirtualCircuitTerminationTest(APIViewTestCases.APIViewTestCase): + model = VirtualCircuitTermination + brief_fields = ['description', 'display', 'id', 'interface', 'role', 'url', 'virtual_circuit'] + bulk_update_data = { + 'description': 'New description', + } + + @classmethod + def setUpTestData(cls): + manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1') + device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1') + device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1') + site = Site.objects.create(name='Site 1', slug='site-1') + + devices = ( + Device(site=site, name='hub', device_type=device_type, role=device_role), + Device(site=site, name='spoke1', device_type=device_type, role=device_role), + Device(site=site, name='spoke2', device_type=device_type, role=device_role), + Device(site=site, name='spoke3', device_type=device_type, role=device_role), + ) + Device.objects.bulk_create(devices) + + physical_interfaces = ( + Interface(device=devices[0], name='eth0', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface(device=devices[1], name='eth0', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface(device=devices[2], name='eth0', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface(device=devices[3], name='eth0', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + ) + Interface.objects.bulk_create(physical_interfaces) + + virtual_interfaces = ( + # Point-to-point VCs + Interface( + device=devices[0], + name='eth0.1', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[0], + name='eth0.2', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[0], + name='eth0.3', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[1], + name='eth0.1', + parent=physical_interfaces[1], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[2], + name='eth0.1', + parent=physical_interfaces[2], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[3], + name='eth0.1', + parent=physical_interfaces[3], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + + # Hub and spoke VCs + Interface( + device=devices[0], + name='eth0.9', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[1], + name='eth0.9', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[2], + name='eth0.9', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[3], + name='eth0.9', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + ) + Interface.objects.bulk_create(virtual_interfaces) + + provider = Provider.objects.create(name='Provider 1', slug='provider-1') + provider_network = ProviderNetwork.objects.create(provider=provider, name='Provider Network 1') + provider_account = ProviderAccount.objects.create(provider=provider, account='Provider Account 1') + + virtual_circuits = ( + VirtualCircuit( + provider_network=provider_network, + provider_account=provider_account, + cid='Virtual Circuit 1' + ), + VirtualCircuit( + provider_network=provider_network, + provider_account=provider_account, + cid='Virtual Circuit 2' + ), + VirtualCircuit( + provider_network=provider_network, + provider_account=provider_account, + cid='Virtual Circuit 3' + ), + VirtualCircuit( + provider_network=provider_network, + provider_account=provider_account, + cid='Virtual Circuit 4' + ), + ) + VirtualCircuit.objects.bulk_create(virtual_circuits) + + virtual_circuit_terminations = ( + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[0], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[0] + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[0], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[3] + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[1], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[1] + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[1], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[4] + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[2], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[2] + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[2], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[5] + ), + ) + VirtualCircuitTermination.objects.bulk_create(virtual_circuit_terminations) + + cls.create_data = [ + { + 'virtual_circuit': virtual_circuits[3].pk, + 'role': VirtualCircuitTerminationRoleChoices.ROLE_HUB, + 'interface': virtual_interfaces[6].pk + }, + { + 'virtual_circuit': virtual_circuits[3].pk, + 'role': VirtualCircuitTerminationRoleChoices.ROLE_SPOKE, + 'interface': virtual_interfaces[7].pk + }, + { + 'virtual_circuit': virtual_circuits[3].pk, + 'role': VirtualCircuitTerminationRoleChoices.ROLE_SPOKE, + 'interface': virtual_interfaces[8].pk + }, + { + 'virtual_circuit': virtual_circuits[3].pk, + 'role': VirtualCircuitTerminationRoleChoices.ROLE_SPOKE, + 'interface': virtual_interfaces[9].pk + }, + ] diff --git a/netbox/circuits/tests/test_filtersets.py b/netbox/circuits/tests/test_filtersets.py index 93958298c..6b7866665 100644 --- a/netbox/circuits/tests/test_filtersets.py +++ b/netbox/circuits/tests/test_filtersets.py @@ -3,7 +3,8 @@ from django.test import TestCase from circuits.choices import * from circuits.filtersets import * from circuits.models import * -from dcim.models import Cable, Region, Site, SiteGroup +from dcim.choices import InterfaceTypeChoices +from dcim.models import Cable, Device, DeviceRole, DeviceType, Interface, Manufacturer, Region, Site, SiteGroup from ipam.models import ASN, RIR from netbox.choices import DistanceUnitChoices from tenancy.models import Tenant, TenantGroup @@ -70,10 +71,12 @@ class ProviderTestCase(TestCase, ChangeLoggedFilterSetTests): ) Circuit.objects.bulk_create(circuits) - CircuitTermination.objects.bulk_create(( - CircuitTermination(circuit=circuits[0], site=sites[0], term_side='A'), - CircuitTermination(circuit=circuits[1], site=sites[0], term_side='A'), - )) + circuit_terminations = ( + CircuitTermination(circuit=circuits[0], termination=sites[0], term_side='A'), + CircuitTermination(circuit=circuits[1], termination=sites[0], term_side='A'), + ) + for ct in circuit_terminations: + ct.save() def test_q(self): params = {'q': 'foobar1'} @@ -223,24 +226,93 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests): ProviderNetwork.objects.bulk_create(provider_networks) circuits = ( - Circuit(provider=providers[0], provider_account=provider_accounts[0], tenant=tenants[0], type=circuit_types[0], cid='Test Circuit 1', install_date='2020-01-01', termination_date='2021-01-01', commit_rate=1000, status=CircuitStatusChoices.STATUS_ACTIVE, description='foobar1', distance=10, distance_unit=DistanceUnitChoices.UNIT_FOOT), - Circuit(provider=providers[0], provider_account=provider_accounts[0], tenant=tenants[0], type=circuit_types[0], cid='Test Circuit 2', install_date='2020-01-02', termination_date='2021-01-02', commit_rate=2000, status=CircuitStatusChoices.STATUS_ACTIVE, description='foobar2', distance=20, distance_unit=DistanceUnitChoices.UNIT_METER), - Circuit(provider=providers[0], provider_account=provider_accounts[1], tenant=tenants[1], type=circuit_types[0], cid='Test Circuit 3', install_date='2020-01-03', termination_date='2021-01-03', commit_rate=3000, status=CircuitStatusChoices.STATUS_PLANNED, distance=30, distance_unit=DistanceUnitChoices.UNIT_METER), - Circuit(provider=providers[1], provider_account=provider_accounts[1], tenant=tenants[1], type=circuit_types[1], cid='Test Circuit 4', install_date='2020-01-04', termination_date='2021-01-04', commit_rate=4000, status=CircuitStatusChoices.STATUS_PLANNED), - Circuit(provider=providers[1], provider_account=provider_accounts[2], tenant=tenants[2], type=circuit_types[1], cid='Test Circuit 5', install_date='2020-01-05', termination_date='2021-01-05', commit_rate=5000, status=CircuitStatusChoices.STATUS_OFFLINE), - Circuit(provider=providers[1], provider_account=provider_accounts[2], tenant=tenants[2], type=circuit_types[1], cid='Test Circuit 6', install_date='2020-01-06', termination_date='2021-01-06', commit_rate=6000, status=CircuitStatusChoices.STATUS_OFFLINE), + Circuit( + provider=providers[0], + provider_account=provider_accounts[0], + tenant=tenants[0], + type=circuit_types[0], + cid='Test Circuit 1', + install_date='2020-01-01', + termination_date='2021-01-01', + commit_rate=1000, + status=CircuitStatusChoices.STATUS_ACTIVE, + description='foobar1', + distance=10, + distance_unit=DistanceUnitChoices.UNIT_FOOT, + ), + Circuit( + provider=providers[0], + provider_account=provider_accounts[0], + tenant=tenants[0], + type=circuit_types[0], + cid='Test Circuit 2', + install_date='2020-01-02', + termination_date='2021-01-02', + commit_rate=2000, + status=CircuitStatusChoices.STATUS_ACTIVE, + description='foobar2', + distance=20, + distance_unit=DistanceUnitChoices.UNIT_METER, + ), + Circuit( + provider=providers[0], + provider_account=provider_accounts[1], + tenant=tenants[1], + type=circuit_types[0], + cid='Test Circuit 3', + install_date='2020-01-03', + termination_date='2021-01-03', + commit_rate=3000, + status=CircuitStatusChoices.STATUS_PLANNED, + distance=30, + distance_unit=DistanceUnitChoices.UNIT_METER, + ), + Circuit( + provider=providers[1], + provider_account=provider_accounts[1], + tenant=tenants[1], + type=circuit_types[1], + cid='Test Circuit 4', + install_date='2020-01-04', + termination_date='2021-01-04', + commit_rate=4000, + status=CircuitStatusChoices.STATUS_PLANNED, + ), + Circuit( + provider=providers[1], + provider_account=provider_accounts[2], + tenant=tenants[2], + type=circuit_types[1], + cid='Test Circuit 5', + install_date='2020-01-05', + termination_date='2021-01-05', + commit_rate=5000, + status=CircuitStatusChoices.STATUS_OFFLINE, + ), + Circuit( + provider=providers[1], + provider_account=provider_accounts[2], + tenant=tenants[2], + type=circuit_types[1], + cid='Test Circuit 6', + install_date='2020-01-06', + termination_date='2021-01-06', + commit_rate=6000, + status=CircuitStatusChoices.STATUS_OFFLINE, + ), ) Circuit.objects.bulk_create(circuits) circuit_terminations = (( - CircuitTermination(circuit=circuits[0], site=sites[0], term_side='A'), - CircuitTermination(circuit=circuits[1], site=sites[1], term_side='A'), - CircuitTermination(circuit=circuits[2], site=sites[2], term_side='A'), - CircuitTermination(circuit=circuits[3], provider_network=provider_networks[0], term_side='A'), - CircuitTermination(circuit=circuits[4], provider_network=provider_networks[1], term_side='A'), - CircuitTermination(circuit=circuits[5], provider_network=provider_networks[2], term_side='A'), + CircuitTermination(circuit=circuits[0], termination=sites[0], term_side='A'), + CircuitTermination(circuit=circuits[1], termination=sites[1], term_side='A'), + CircuitTermination(circuit=circuits[2], termination=sites[2], term_side='A'), + CircuitTermination(circuit=circuits[3], termination=provider_networks[0], term_side='A'), + CircuitTermination(circuit=circuits[4], termination=provider_networks[1], term_side='A'), + CircuitTermination(circuit=circuits[5], termination=provider_networks[2], term_side='A'), )) - CircuitTermination.objects.bulk_create(circuit_terminations) + for ct in circuit_terminations: + ct.save() def test_q(self): params = {'q': 'foobar1'} @@ -383,19 +455,66 @@ class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests): ) Circuit.objects.bulk_create(circuits) - circuit_terminations = (( - CircuitTermination(circuit=circuits[0], site=sites[0], term_side='A', port_speed=1000, upstream_speed=1000, xconnect_id='ABC', description='foobar1'), - CircuitTermination(circuit=circuits[0], site=sites[1], term_side='Z', port_speed=1000, upstream_speed=1000, xconnect_id='DEF', description='foobar2'), - CircuitTermination(circuit=circuits[1], site=sites[1], term_side='A', port_speed=2000, upstream_speed=2000, xconnect_id='GHI'), - CircuitTermination(circuit=circuits[1], site=sites[2], term_side='Z', port_speed=2000, upstream_speed=2000, xconnect_id='JKL'), - CircuitTermination(circuit=circuits[2], site=sites[2], term_side='A', port_speed=3000, upstream_speed=3000, xconnect_id='MNO'), - CircuitTermination(circuit=circuits[2], site=sites[0], term_side='Z', port_speed=3000, upstream_speed=3000, xconnect_id='PQR'), - CircuitTermination(circuit=circuits[3], provider_network=provider_networks[0], term_side='A'), - CircuitTermination(circuit=circuits[4], provider_network=provider_networks[1], term_side='A'), - CircuitTermination(circuit=circuits[5], provider_network=provider_networks[2], term_side='A'), - CircuitTermination(circuit=circuits[6], provider_network=provider_networks[0], term_side='A', mark_connected=True), - )) - CircuitTermination.objects.bulk_create(circuit_terminations) + circuit_terminations = ( + CircuitTermination( + circuit=circuits[0], + termination=sites[0], + term_side='A', + port_speed=1000, + upstream_speed=1000, + xconnect_id='ABC', + description='foobar1', + ), + CircuitTermination( + circuit=circuits[0], + termination=sites[1], + term_side='Z', + port_speed=1000, + upstream_speed=1000, + xconnect_id='DEF', + description='foobar2', + ), + CircuitTermination( + circuit=circuits[1], + termination=sites[1], + term_side='A', + port_speed=2000, + upstream_speed=2000, + xconnect_id='GHI', + ), + CircuitTermination( + circuit=circuits[1], + termination=sites[2], + term_side='Z', + port_speed=2000, + upstream_speed=2000, + xconnect_id='JKL', + ), + CircuitTermination( + circuit=circuits[2], + termination=sites[2], + term_side='A', + port_speed=3000, + upstream_speed=3000, + xconnect_id='MNO', + ), + CircuitTermination( + circuit=circuits[2], + termination=sites[0], + term_side='Z', + port_speed=3000, + upstream_speed=3000, + xconnect_id='PQR', + ), + CircuitTermination(circuit=circuits[3], termination=provider_networks[0], term_side='A'), + CircuitTermination(circuit=circuits[4], termination=provider_networks[1], term_side='A'), + CircuitTermination(circuit=circuits[5], termination=provider_networks[2], term_side='A'), + CircuitTermination( + circuit=circuits[6], termination=provider_networks[0], term_side='A', mark_connected=True + ), + ) + for ct in circuit_terminations: + ct.save() Cable(a_terminations=[circuit_terminations[0]], b_terminations=[circuit_terminations[1]]).save() @@ -674,3 +793,293 @@ class ProviderAccountTestCase(TestCase, ChangeLoggedFilterSetTests): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) params = {'provider': [providers[0].slug, providers[1].slug]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + +class VirtualCircuitTestCase(TestCase, ChangeLoggedFilterSetTests): + queryset = VirtualCircuit.objects.all() + filterset = VirtualCircuitFilterSet + + @classmethod + def setUpTestData(cls): + + tenant_groups = ( + TenantGroup(name='Tenant group 1', slug='tenant-group-1'), + TenantGroup(name='Tenant group 2', slug='tenant-group-2'), + TenantGroup(name='Tenant group 3', slug='tenant-group-3'), + ) + for tenantgroup in tenant_groups: + tenantgroup.save() + + tenants = ( + Tenant(name='Tenant 1', slug='tenant-1', group=tenant_groups[0]), + Tenant(name='Tenant 2', slug='tenant-2', group=tenant_groups[1]), + Tenant(name='Tenant 3', slug='tenant-3', group=tenant_groups[2]), + ) + Tenant.objects.bulk_create(tenants) + + providers = ( + Provider(name='Provider 1', slug='provider-1'), + Provider(name='Provider 2', slug='provider-2'), + Provider(name='Provider 3', slug='provider-3'), + ) + Provider.objects.bulk_create(providers) + + provider_accounts = ( + ProviderAccount(name='Provider Account 1', provider=providers[0], account='A'), + ProviderAccount(name='Provider Account 2', provider=providers[1], account='B'), + ProviderAccount(name='Provider Account 3', provider=providers[2], account='C'), + ) + ProviderAccount.objects.bulk_create(provider_accounts) + + provider_networks = ( + ProviderNetwork(name='Provider Network 1', provider=providers[0]), + ProviderNetwork(name='Provider Network 2', provider=providers[1]), + ProviderNetwork(name='Provider Network 3', provider=providers[2]), + ) + ProviderNetwork.objects.bulk_create(provider_networks) + + virutal_circuits = ( + VirtualCircuit( + provider_network=provider_networks[0], + provider_account=provider_accounts[0], + tenant=tenants[0], + cid='Virtual Circuit 1', + status=CircuitStatusChoices.STATUS_PLANNED, + description='virtualcircuit1', + ), + VirtualCircuit( + provider_network=provider_networks[1], + provider_account=provider_accounts[1], + tenant=tenants[1], + cid='Virtual Circuit 2', + status=CircuitStatusChoices.STATUS_ACTIVE, + description='virtualcircuit2', + ), + VirtualCircuit( + provider_network=provider_networks[2], + provider_account=provider_accounts[2], + tenant=tenants[2], + cid='Virtual Circuit 3', + status=CircuitStatusChoices.STATUS_DEPROVISIONING, + description='virtualcircuit3', + ), + ) + VirtualCircuit.objects.bulk_create(virutal_circuits) + + def test_q(self): + params = {'q': 'virtualcircuit1'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + def test_cid(self): + params = {'cid': ['Virtual Circuit 1', 'Virtual Circuit 2']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_provider(self): + providers = Provider.objects.all()[:2] + params = {'provider_id': [providers[0].pk, providers[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'provider': [providers[0].slug, providers[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_provider_account(self): + provider_accounts = ProviderAccount.objects.all()[:2] + params = {'provider_account_id': [provider_accounts[0].pk, provider_accounts[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_provider_network(self): + provider_networks = ProviderNetwork.objects.all()[:2] + params = {'provider_network_id': [provider_networks[0].pk, provider_networks[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_status(self): + params = {'status': [CircuitStatusChoices.STATUS_ACTIVE, CircuitStatusChoices.STATUS_PLANNED]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_description(self): + params = {'description': ['virtualcircuit1', 'virtualcircuit2']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_tenant(self): + tenants = Tenant.objects.all()[:2] + params = {'tenant_id': [tenants[0].pk, tenants[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'tenant': [tenants[0].slug, tenants[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_tenant_group(self): + tenant_groups = TenantGroup.objects.all()[:2] + params = {'tenant_group_id': [tenant_groups[0].pk, tenant_groups[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'tenant_group': [tenant_groups[0].slug, tenant_groups[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + +class VirtualCircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests): + queryset = VirtualCircuitTermination.objects.all() + filterset = VirtualCircuitTerminationFilterSet + + @classmethod + def setUpTestData(cls): + manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1') + device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1') + device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1') + site = Site.objects.create(name='Site 1', slug='site-1') + + devices = ( + Device(site=site, name='Device 1', device_type=device_type, role=device_role), + Device(site=site, name='Device 2', device_type=device_type, role=device_role), + Device(site=site, name='Device 3', device_type=device_type, role=device_role), + ) + Device.objects.bulk_create(devices) + + virtual_interfaces = ( + # Device 1 + Interface( + device=devices[0], + name='eth0.1', + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[0], + name='eth0.2', + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + # Device 2 + Interface( + device=devices[1], + name='eth0.1', + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[1], + name='eth0.2', + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + # Device 3 + Interface( + device=devices[2], + name='eth0.1', + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[2], + name='eth0.2', + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + ) + Interface.objects.bulk_create(virtual_interfaces) + + providers = ( + Provider(name='Provider 1', slug='provider-1'), + Provider(name='Provider 2', slug='provider-2'), + Provider(name='Provider 3', slug='provider-3'), + ) + Provider.objects.bulk_create(providers) + provider_networks = ( + ProviderNetwork(provider=providers[0], name='Provider Network 1'), + ProviderNetwork(provider=providers[1], name='Provider Network 2'), + ProviderNetwork(provider=providers[2], name='Provider Network 3'), + ) + ProviderNetwork.objects.bulk_create(provider_networks) + provider_accounts = ( + ProviderAccount(provider=providers[0], account='Provider Account 1'), + ProviderAccount(provider=providers[1], account='Provider Account 2'), + ProviderAccount(provider=providers[2], account='Provider Account 3'), + ) + ProviderAccount.objects.bulk_create(provider_accounts) + + virtual_circuits = ( + VirtualCircuit( + provider_network=provider_networks[0], + provider_account=provider_accounts[0], + cid='Virtual Circuit 1' + ), + VirtualCircuit( + provider_network=provider_networks[1], + provider_account=provider_accounts[1], + cid='Virtual Circuit 2' + ), + VirtualCircuit( + provider_network=provider_networks[2], + provider_account=provider_accounts[2], + cid='Virtual Circuit 3' + ), + ) + VirtualCircuit.objects.bulk_create(virtual_circuits) + + virtual_circuit_terminations = ( + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[0], + role=VirtualCircuitTerminationRoleChoices.ROLE_HUB, + interface=virtual_interfaces[0], + description='termination1' + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[0], + role=VirtualCircuitTerminationRoleChoices.ROLE_SPOKE, + interface=virtual_interfaces[3], + description='termination2' + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[1], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[1], + description='termination3' + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[1], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[4], + description='termination4' + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[2], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[2], + description='termination5' + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[2], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[5], + description='termination6' + ), + ) + VirtualCircuitTermination.objects.bulk_create(virtual_circuit_terminations) + + def test_q(self): + params = {'q': 'termination1'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + def test_description(self): + params = {'description': ['termination1', 'termination2']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_virtual_circuit_id(self): + virtual_circuits = VirtualCircuit.objects.filter()[:2] + params = {'virtual_circuit_id': [virtual_circuits[0].pk, virtual_circuits[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + + def test_provider(self): + providers = Provider.objects.all()[:2] + params = {'provider_id': [providers[0].pk, providers[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + params = {'provider': [providers[0].slug, providers[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + + def test_provider_network(self): + provider_networks = ProviderNetwork.objects.all()[:2] + params = {'provider_network_id': [provider_networks[0].pk, provider_networks[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + + def test_provider_account(self): + provider_accounts = ProviderAccount.objects.all()[:2] + params = {'provider_account_id': [provider_accounts[0].pk, provider_accounts[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + params = {'provider_account': [provider_accounts[0].account, provider_accounts[1].account]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + + def test_interface(self): + interfaces = Interface.objects.all()[:2] + params = {'interface_id': [interfaces[0].pk, interfaces[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) diff --git a/netbox/circuits/tests/test_views.py b/netbox/circuits/tests/test_views.py index b06ade30b..036bebe23 100644 --- a/netbox/circuits/tests/test_views.py +++ b/netbox/circuits/tests/test_views.py @@ -1,12 +1,14 @@ import datetime +from django.contrib.contenttypes.models import ContentType from django.test import override_settings from django.urls import reverse from circuits.choices import * from circuits.models import * from core.models import ObjectType -from dcim.models import Cable, Interface, Site +from dcim.choices import InterfaceTypeChoices +from dcim.models import Cable, Device, DeviceRole, DeviceType, Interface, Manufacturer, Site from ipam.models import ASN, RIR from netbox.choices import ImportFormatChoices from users.models import ObjectPermission @@ -139,9 +141,15 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): CircuitType.objects.bulk_create(circuittypes) circuits = ( - Circuit(cid='Circuit 1', provider=providers[0], provider_account=provider_accounts[0], type=circuittypes[0]), - Circuit(cid='Circuit 2', provider=providers[0], provider_account=provider_accounts[0], type=circuittypes[0]), - Circuit(cid='Circuit 3', provider=providers[0], provider_account=provider_accounts[0], type=circuittypes[0]), + Circuit( + cid='Circuit 1', provider=providers[0], provider_account=provider_accounts[0], type=circuittypes[0] + ), + Circuit( + cid='Circuit 2', provider=providers[0], provider_account=provider_accounts[0], type=circuittypes[0] + ), + Circuit( + cid='Circuit 3', provider=providers[0], provider_account=provider_accounts[0], type=circuittypes[0] + ), ) Circuit.objects.bulk_create(circuits) @@ -190,27 +198,31 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[]) def test_bulk_import_objects_with_terminations(self): - json_data = """ + site = Site.objects.first() + json_data = f""" [ - { + {{ "cid": "Circuit 7", "provider": "Provider 1", "type": "Circuit Type 1", "status": "active", "description": "Testing Import", "terminations": [ - { + {{ "term_side": "A", - "site": "Site 1" - }, - { + "termination_type": "dcim.site", + "termination_id": "{site.pk}" + }}, + {{ "term_side": "Z", - "site": "Site 1" - } + "termination_type": "dcim.site", + "termination_id": "{site.pk}" + }} ] - } + }} ] """ + initial_count = self._get_queryset().count() data = { 'data': json_data, @@ -227,10 +239,10 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) # Try GET with model-level permission - self.assertHttpStatus(self.client.get(self._get_url('import')), 200) + self.assertHttpStatus(self.client.get(self._get_url('bulk_import')), 200) # Test POST with permission - self.assertHttpStatus(self.client.post(self._get_url('import'), data), 302) + self.assertHttpStatus(self.client.post(self._get_url('bulk_import'), data), 302) self.assertEqual(self._get_queryset().count(), initial_count + 1) @@ -359,24 +371,27 @@ class CircuitTerminationTestCase(ViewTestCases.PrimaryObjectViewTestCase): Circuit.objects.bulk_create(circuits) circuit_terminations = ( - CircuitTermination(circuit=circuits[0], term_side='A', site=sites[0]), - CircuitTermination(circuit=circuits[0], term_side='Z', site=sites[1]), - CircuitTermination(circuit=circuits[1], term_side='A', site=sites[0]), - CircuitTermination(circuit=circuits[1], term_side='Z', site=sites[1]), + CircuitTermination(circuit=circuits[0], term_side='A', termination=sites[0]), + CircuitTermination(circuit=circuits[0], term_side='Z', termination=sites[1]), + CircuitTermination(circuit=circuits[1], term_side='A', termination=sites[0]), + CircuitTermination(circuit=circuits[1], term_side='Z', termination=sites[1]), ) - CircuitTermination.objects.bulk_create(circuit_terminations) + for ct in circuit_terminations: + ct.save() cls.form_data = { 'circuit': circuits[2].pk, 'term_side': 'A', - 'site': sites[2].pk, + 'termination_type': ContentType.objects.get_for_model(Site).pk, + 'termination': sites[2].pk, 'description': 'New description', } + site = sites[0].pk cls.csv_data = ( - "circuit,term_side,site,description", - "Circuit 3,A,Site 1,Foo", - "Circuit 3,Z,Site 1,Bar", + "circuit,term_side,termination_type,termination_id,description", + f"Circuit 3,A,dcim.site,{site},Foo", + f"Circuit 3,Z,dcim.site,{site},Bar", ) cls.csv_update_data = ( @@ -510,3 +525,319 @@ class CircuitGroupAssignmentTestCase( cls.bulk_edit_data = { 'priority': CircuitPriorityChoices.PRIORITY_INACTIVE, } + + +class VirtualCircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase): + model = VirtualCircuit + + def setUp(self): + super().setUp() + + self.add_permissions( + 'circuits.add_virtualcircuittermination', + ) + + @classmethod + def setUpTestData(cls): + provider = Provider.objects.create(name='Provider 1', slug='provider-1') + provider_networks = ( + ProviderNetwork(provider=provider, name='Provider Network 1'), + ProviderNetwork(provider=provider, name='Provider Network 2'), + ) + ProviderNetwork.objects.bulk_create(provider_networks) + provider_accounts = ( + ProviderAccount(provider=provider, account='Provider Account 1'), + ProviderAccount(provider=provider, account='Provider Account 2'), + ) + ProviderAccount.objects.bulk_create(provider_accounts) + + virtual_circuits = ( + VirtualCircuit( + provider_network=provider_networks[0], + provider_account=provider_accounts[0], + cid='Virtual Circuit 1' + ), + VirtualCircuit( + provider_network=provider_networks[0], + provider_account=provider_accounts[0], + cid='Virtual Circuit 2' + ), + VirtualCircuit( + provider_network=provider_networks[0], + provider_account=provider_accounts[0], + cid='Virtual Circuit 3' + ), + ) + VirtualCircuit.objects.bulk_create(virtual_circuits) + + device = create_test_device('Device 1') + interfaces = ( + Interface(device=device, name='Interface 1', type=InterfaceTypeChoices.TYPE_VIRTUAL), + Interface(device=device, name='Interface 2', type=InterfaceTypeChoices.TYPE_VIRTUAL), + Interface(device=device, name='Interface 3', type=InterfaceTypeChoices.TYPE_VIRTUAL), + ) + Interface.objects.bulk_create(interfaces) + + tags = create_tags('Alpha', 'Bravo', 'Charlie') + + cls.form_data = { + 'cid': 'Virtual Circuit X', + 'provider_network': provider_networks[1].pk, + 'provider_account': provider_accounts[1].pk, + 'status': CircuitStatusChoices.STATUS_PLANNED, + 'description': 'A new virtual circuit', + 'comments': 'Some comments', + 'tags': [t.pk for t in tags], + } + + cls.csv_data = ( + "cid,provider_network,provider_account,status", + f"Virtual Circuit 4,Provider Network 1,Provider Account 1,{CircuitStatusChoices.STATUS_PLANNED}", + f"Virtual Circuit 5,Provider Network 1,Provider Account 1,{CircuitStatusChoices.STATUS_PLANNED}", + f"Virtual Circuit 6,Provider Network 1,Provider Account 1,{CircuitStatusChoices.STATUS_PLANNED}", + ) + + cls.csv_update_data = ( + "id,cid,description,status", + f"{virtual_circuits[0].pk},Virtual Circuit A,New description,{CircuitStatusChoices.STATUS_DECOMMISSIONED}", + f"{virtual_circuits[1].pk},Virtual Circuit B,New description,{CircuitStatusChoices.STATUS_DECOMMISSIONED}", + f"{virtual_circuits[2].pk},Virtual Circuit C,New description,{CircuitStatusChoices.STATUS_DECOMMISSIONED}", + ) + + cls.bulk_edit_data = { + 'provider_network': provider_networks[1].pk, + 'provider_account': provider_accounts[1].pk, + 'status': CircuitStatusChoices.STATUS_DECOMMISSIONED, + 'description': 'New description', + 'comments': 'New comments', + } + + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[]) + def test_bulk_import_objects_with_terminations(self): + interfaces = Interface.objects.filter(type=InterfaceTypeChoices.TYPE_VIRTUAL) + json_data = f""" + [ + {{ + "cid": "Virtual Circuit 7", + "provider_network": "Provider Network 1", + "status": "active", + "terminations": [ + {{ + "role": "hub", + "interface": {interfaces[0].pk} + }}, + {{ + "role": "spoke", + "interface": {interfaces[1].pk} + }}, + {{ + "role": "spoke", + "interface": {interfaces[2].pk} + }} + ] + }} + ] + """ + + initial_count = self._get_queryset().count() + data = { + 'data': json_data, + 'format': ImportFormatChoices.JSON, + } + + # Assign model-level permission + 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)) + + # Try GET with model-level permission + self.assertHttpStatus(self.client.get(self._get_url('bulk_import')), 200) + + # Test POST with permission + self.assertHttpStatus(self.client.post(self._get_url('bulk_import'), data), 302) + self.assertEqual(self._get_queryset().count(), initial_count + 1) + + +class VirtualCircuitTerminationTestCase(ViewTestCases.PrimaryObjectViewTestCase): + model = VirtualCircuitTermination + + @classmethod + def setUpTestData(cls): + manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1') + device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1') + device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1') + site = Site.objects.create(name='Site 1', slug='site-1') + + devices = ( + Device(site=site, name='hub', device_type=device_type, role=device_role), + Device(site=site, name='spoke1', device_type=device_type, role=device_role), + Device(site=site, name='spoke2', device_type=device_type, role=device_role), + Device(site=site, name='spoke3', device_type=device_type, role=device_role), + ) + Device.objects.bulk_create(devices) + + physical_interfaces = ( + Interface(device=devices[0], name='eth0', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface(device=devices[1], name='eth0', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface(device=devices[2], name='eth0', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface(device=devices[3], name='eth0', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + ) + Interface.objects.bulk_create(physical_interfaces) + + virtual_interfaces = ( + # Point-to-point VCs + Interface( + device=devices[0], + name='eth0.1', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[0], + name='eth0.2', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[0], + name='eth0.3', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[1], + name='eth0.1', + parent=physical_interfaces[1], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[2], + name='eth0.1', + parent=physical_interfaces[2], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[3], + name='eth0.1', + parent=physical_interfaces[3], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + + # Hub and spoke VCs + Interface( + device=devices[0], + name='eth0.9', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[1], + name='eth0.9', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[2], + name='eth0.9', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + Interface( + device=devices[3], + name='eth0.9', + parent=physical_interfaces[0], + type=InterfaceTypeChoices.TYPE_VIRTUAL + ), + ) + Interface.objects.bulk_create(virtual_interfaces) + + provider = Provider.objects.create(name='Provider 1', slug='provider-1') + provider_network = ProviderNetwork.objects.create(provider=provider, name='Provider Network 1') + provider_account = ProviderAccount.objects.create(provider=provider, account='Provider Account 1') + + virtual_circuits = ( + VirtualCircuit( + provider_network=provider_network, + provider_account=provider_account, + cid='Virtual Circuit 1' + ), + VirtualCircuit( + provider_network=provider_network, + provider_account=provider_account, + cid='Virtual Circuit 2' + ), + VirtualCircuit( + provider_network=provider_network, + provider_account=provider_account, + cid='Virtual Circuit 3' + ), + VirtualCircuit( + provider_network=provider_network, + provider_account=provider_account, + cid='Virtual Circuit 4' + ), + ) + VirtualCircuit.objects.bulk_create(virtual_circuits) + + virtual_circuit_terminations = ( + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[0], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[0] + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[0], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[3] + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[1], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[1] + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[1], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[4] + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[2], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[2] + ), + VirtualCircuitTermination( + virtual_circuit=virtual_circuits[2], + role=VirtualCircuitTerminationRoleChoices.ROLE_PEER, + interface=virtual_interfaces[5] + ), + ) + VirtualCircuitTermination.objects.bulk_create(virtual_circuit_terminations) + + cls.form_data = { + 'virtual_circuit': virtual_circuits[3].pk, + 'role': VirtualCircuitTerminationRoleChoices.ROLE_HUB, + 'interface': virtual_interfaces[6].pk + } + + cls.csv_data = ( + "virtual_circuit,role,interface,description", + f"Virtual Circuit 4,{VirtualCircuitTerminationRoleChoices.ROLE_HUB},{virtual_interfaces[6].pk},Hub", + f"Virtual Circuit 4,{VirtualCircuitTerminationRoleChoices.ROLE_SPOKE},{virtual_interfaces[7].pk},Spoke 1", + f"Virtual Circuit 4,{VirtualCircuitTerminationRoleChoices.ROLE_SPOKE},{virtual_interfaces[8].pk},Spoke 2", + f"Virtual Circuit 4,{VirtualCircuitTerminationRoleChoices.ROLE_SPOKE},{virtual_interfaces[9].pk},Spoke 3", + ) + + cls.csv_update_data = ( + "id,role,description", + f"{virtual_circuit_terminations[0].pk},{VirtualCircuitTerminationRoleChoices.ROLE_SPOKE},New description", + f"{virtual_circuit_terminations[1].pk},{VirtualCircuitTerminationRoleChoices.ROLE_SPOKE},New description", + f"{virtual_circuit_terminations[2].pk},{VirtualCircuitTerminationRoleChoices.ROLE_SPOKE},New description", + ) + + cls.bulk_edit_data = { + 'description': 'New description', + } diff --git a/netbox/circuits/urls.py b/netbox/circuits/urls.py index 2171d49be..49eaa3910 100644 --- a/netbox/circuits/urls.py +++ b/netbox/circuits/urls.py @@ -5,69 +5,68 @@ from . import views app_name = 'circuits' urlpatterns = [ - - # Providers - path('providers/', views.ProviderListView.as_view(), name='provider_list'), - path('providers/add/', views.ProviderEditView.as_view(), name='provider_add'), - path('providers/import/', views.ProviderBulkImportView.as_view(), name='provider_import'), - path('providers/edit/', views.ProviderBulkEditView.as_view(), name='provider_bulk_edit'), - path('providers/delete/', views.ProviderBulkDeleteView.as_view(), name='provider_bulk_delete'), + path('providers/', include(get_model_urls('circuits', 'provider', detail=False))), path('providers//', include(get_model_urls('circuits', 'provider'))), - # Provider accounts - path('provider-accounts/', views.ProviderAccountListView.as_view(), name='provideraccount_list'), - path('provider-accounts/add/', views.ProviderAccountEditView.as_view(), name='provideraccount_add'), - path('provider-accounts/import/', views.ProviderAccountBulkImportView.as_view(), name='provideraccount_import'), - path('provider-accounts/edit/', views.ProviderAccountBulkEditView.as_view(), name='provideraccount_bulk_edit'), - path('provider-accounts/delete/', views.ProviderAccountBulkDeleteView.as_view(), name='provideraccount_bulk_delete'), + path('provider-accounts/', include(get_model_urls('circuits', 'provideraccount', detail=False))), path('provider-accounts//', include(get_model_urls('circuits', 'provideraccount'))), - # Provider networks - path('provider-networks/', views.ProviderNetworkListView.as_view(), name='providernetwork_list'), - path('provider-networks/add/', views.ProviderNetworkEditView.as_view(), name='providernetwork_add'), - path('provider-networks/import/', views.ProviderNetworkBulkImportView.as_view(), name='providernetwork_import'), - path('provider-networks/edit/', views.ProviderNetworkBulkEditView.as_view(), name='providernetwork_bulk_edit'), - path('provider-networks/delete/', views.ProviderNetworkBulkDeleteView.as_view(), name='providernetwork_bulk_delete'), + path('provider-networks/', include(get_model_urls('circuits', 'providernetwork', detail=False))), path('provider-networks//', include(get_model_urls('circuits', 'providernetwork'))), - # Circuit types - path('circuit-types/', views.CircuitTypeListView.as_view(), name='circuittype_list'), - path('circuit-types/add/', views.CircuitTypeEditView.as_view(), name='circuittype_add'), - path('circuit-types/import/', views.CircuitTypeBulkImportView.as_view(), name='circuittype_import'), - path('circuit-types/edit/', views.CircuitTypeBulkEditView.as_view(), name='circuittype_bulk_edit'), - path('circuit-types/delete/', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'), + path('circuit-types/', include(get_model_urls('circuits', 'circuittype', detail=False))), path('circuit-types//', include(get_model_urls('circuits', 'circuittype'))), - # Circuits - path('circuits/', views.CircuitListView.as_view(), name='circuit_list'), - path('circuits/add/', views.CircuitEditView.as_view(), name='circuit_add'), - path('circuits/import/', views.CircuitBulkImportView.as_view(), name='circuit_import'), - path('circuits/edit/', views.CircuitBulkEditView.as_view(), name='circuit_bulk_edit'), - path('circuits/delete/', views.CircuitBulkDeleteView.as_view(), name='circuit_bulk_delete'), - path('circuits//terminations/swap/', views.CircuitSwapTerminations.as_view(), name='circuit_terminations_swap'), + path('circuits/', include(get_model_urls('circuits', 'circuit', detail=False))), + path( + 'circuits//terminations/swap/', + views.CircuitSwapTerminations.as_view(), + name='circuit_terminations_swap' + ), path('circuits//', include(get_model_urls('circuits', 'circuit'))), - # Circuit terminations - path('circuit-terminations/', views.CircuitTerminationListView.as_view(), name='circuittermination_list'), - path('circuit-terminations/add/', views.CircuitTerminationEditView.as_view(), name='circuittermination_add'), - path('circuit-terminations/import/', views.CircuitTerminationBulkImportView.as_view(), name='circuittermination_import'), - path('circuit-terminations/edit/', views.CircuitTerminationBulkEditView.as_view(), name='circuittermination_bulk_edit'), - path('circuit-terminations/delete/', views.CircuitTerminationBulkDeleteView.as_view(), name='circuittermination_bulk_delete'), + path('circuit-terminations/', include(get_model_urls('circuits', 'circuittermination', detail=False))), path('circuit-terminations//', include(get_model_urls('circuits', 'circuittermination'))), - # Circuit Groups - path('circuit-groups/', views.CircuitGroupListView.as_view(), name='circuitgroup_list'), - path('circuit-groups/add/', views.CircuitGroupEditView.as_view(), name='circuitgroup_add'), - path('circuit-groups/import/', views.CircuitGroupBulkImportView.as_view(), name='circuitgroup_import'), - path('circuit-groups/edit/', views.CircuitGroupBulkEditView.as_view(), name='circuitgroup_bulk_edit'), - path('circuit-groups/delete/', views.CircuitGroupBulkDeleteView.as_view(), name='circuitgroup_bulk_delete'), + path('circuit-groups/', include(get_model_urls('circuits', 'circuitgroup', detail=False))), path('circuit-groups//', include(get_model_urls('circuits', 'circuitgroup'))), - # Circuit Group Assignments - path('circuit-group-assignments/', views.CircuitGroupAssignmentListView.as_view(), name='circuitgroupassignment_list'), - path('circuit-group-assignments/add/', views.CircuitGroupAssignmentEditView.as_view(), name='circuitgroupassignment_add'), - path('circuit-group-assignments/import/', views.CircuitGroupAssignmentBulkImportView.as_view(), name='circuitgroupassignment_import'), - path('circuit-group-assignments/edit/', views.CircuitGroupAssignmentBulkEditView.as_view(), name='circuitgroupassignment_bulk_edit'), - path('circuit-group-assignments/delete/', views.CircuitGroupAssignmentBulkDeleteView.as_view(), name='circuitgroupassignment_bulk_delete'), + path('circuit-group-assignments/', include(get_model_urls('circuits', 'circuitgroupassignment', detail=False))), path('circuit-group-assignments//', include(get_model_urls('circuits', 'circuitgroupassignment'))), + + # Virtual circuits + path('virtual-circuits/', views.VirtualCircuitListView.as_view(), name='virtualcircuit_list'), + path('virtual-circuits/add/', views.VirtualCircuitEditView.as_view(), name='virtualcircuit_add'), + path('virtual-circuits/import/', views.VirtualCircuitBulkImportView.as_view(), name='virtualcircuit_bulk_import'), + path('virtual-circuits/edit/', views.VirtualCircuitBulkEditView.as_view(), name='virtualcircuit_bulk_edit'), + path('virtual-circuits/delete/', views.VirtualCircuitBulkDeleteView.as_view(), name='virtualcircuit_bulk_delete'), + path('virtual-circuits//', include(get_model_urls('circuits', 'virtualcircuit'))), + + # Virtual circuit terminations + path( + 'virtual-circuit-terminations/', + views.VirtualCircuitTerminationListView.as_view(), + name='virtualcircuittermination_list', + ), + path( + 'virtual-circuit-terminations/add/', + views.VirtualCircuitTerminationEditView.as_view(), + name='virtualcircuittermination_add', + ), + path( + 'virtual-circuit-terminations/import/', + views.VirtualCircuitTerminationBulkImportView.as_view(), + name='virtualcircuittermination_bulk_import', + ), + path( + 'virtual-circuit-terminations/edit/', + views.VirtualCircuitTerminationBulkEditView.as_view(), + name='virtualcircuittermination_bulk_edit', + ), + path( + 'virtual-circuit-terminations/delete/', + views.VirtualCircuitTerminationBulkDeleteView.as_view(), + name='virtualcircuittermination_bulk_delete', + ), + path('virtual-circuit-terminations//', include(get_model_urls('circuits', 'virtualcircuittermination'))), ] diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index 8218960c9..09f79789e 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -17,6 +17,7 @@ from .models import * # Providers # +@register_model_view(Provider, 'list', path='', detail=False) class ProviderListView(generic.ObjectListView): queryset = Provider.objects.annotate( count_circuits=count_related(Circuit, 'provider') @@ -36,6 +37,7 @@ class ProviderView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(Provider, 'add', detail=False) @register_model_view(Provider, 'edit') class ProviderEditView(generic.ObjectEditView): queryset = Provider.objects.all() @@ -47,11 +49,13 @@ class ProviderDeleteView(generic.ObjectDeleteView): queryset = Provider.objects.all() +@register_model_view(Provider, 'bulk_import', detail=False) class ProviderBulkImportView(generic.BulkImportView): queryset = Provider.objects.all() model_form = forms.ProviderImportForm +@register_model_view(Provider, 'bulk_edit', path='edit', detail=False) class ProviderBulkEditView(generic.BulkEditView): queryset = Provider.objects.annotate( count_circuits=count_related(Circuit, 'provider') @@ -61,6 +65,7 @@ class ProviderBulkEditView(generic.BulkEditView): form = forms.ProviderBulkEditForm +@register_model_view(Provider, 'bulk_delete', path='delete', detail=False) class ProviderBulkDeleteView(generic.BulkDeleteView): queryset = Provider.objects.annotate( count_circuits=count_related(Circuit, 'provider') @@ -78,6 +83,7 @@ class ProviderContactsView(ObjectContactsView): # ProviderAccounts # +@register_model_view(ProviderAccount, 'list', path='', detail=False) class ProviderAccountListView(generic.ObjectListView): queryset = ProviderAccount.objects.annotate( count_circuits=count_related(Circuit, 'provider_account') @@ -97,6 +103,7 @@ class ProviderAccountView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(ProviderAccount, 'add', detail=False) @register_model_view(ProviderAccount, 'edit') class ProviderAccountEditView(generic.ObjectEditView): queryset = ProviderAccount.objects.all() @@ -108,12 +115,14 @@ class ProviderAccountDeleteView(generic.ObjectDeleteView): queryset = ProviderAccount.objects.all() +@register_model_view(ProviderAccount, 'bulk_import', detail=False) class ProviderAccountBulkImportView(generic.BulkImportView): queryset = ProviderAccount.objects.all() model_form = forms.ProviderAccountImportForm table = tables.ProviderAccountTable +@register_model_view(ProviderAccount, 'bulk_edit', path='edit', detail=False) class ProviderAccountBulkEditView(generic.BulkEditView): queryset = ProviderAccount.objects.annotate( count_circuits=count_related(Circuit, 'provider_account') @@ -123,6 +132,7 @@ class ProviderAccountBulkEditView(generic.BulkEditView): form = forms.ProviderAccountBulkEditForm +@register_model_view(ProviderAccount, 'bulk_delete', path='delete', detail=False) class ProviderAccountBulkDeleteView(generic.BulkDeleteView): queryset = ProviderAccount.objects.annotate( count_circuits=count_related(Circuit, 'provider_account') @@ -140,6 +150,7 @@ class ProviderAccountContactsView(ObjectContactsView): # Provider networks # +@register_model_view(ProviderNetwork, 'list', path='', detail=False) class ProviderNetworkListView(generic.ObjectListView): queryset = ProviderNetwork.objects.all() filterset = filtersets.ProviderNetworkFilterSet @@ -158,7 +169,7 @@ class ProviderNetworkView(GetRelatedModelsMixin, generic.ObjectView): instance, extra=( ( - Circuit.objects.restrict(request.user, 'view').filter(terminations__provider_network=instance), + Circuit.objects.restrict(request.user, 'view').filter(terminations___provider_network=instance), 'provider_network_id', ), ), @@ -166,6 +177,7 @@ class ProviderNetworkView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(ProviderNetwork, 'add', detail=False) @register_model_view(ProviderNetwork, 'edit') class ProviderNetworkEditView(generic.ObjectEditView): queryset = ProviderNetwork.objects.all() @@ -177,11 +189,13 @@ class ProviderNetworkDeleteView(generic.ObjectDeleteView): queryset = ProviderNetwork.objects.all() +@register_model_view(ProviderNetwork, 'bulk_import', detail=False) class ProviderNetworkBulkImportView(generic.BulkImportView): queryset = ProviderNetwork.objects.all() model_form = forms.ProviderNetworkImportForm +@register_model_view(ProviderNetwork, 'bulk_edit', path='edit', detail=False) class ProviderNetworkBulkEditView(generic.BulkEditView): queryset = ProviderNetwork.objects.all() filterset = filtersets.ProviderNetworkFilterSet @@ -189,6 +203,7 @@ class ProviderNetworkBulkEditView(generic.BulkEditView): form = forms.ProviderNetworkBulkEditForm +@register_model_view(ProviderNetwork, 'bulk_delete', path='delete', detail=False) class ProviderNetworkBulkDeleteView(generic.BulkDeleteView): queryset = ProviderNetwork.objects.all() filterset = filtersets.ProviderNetworkFilterSet @@ -199,6 +214,7 @@ class ProviderNetworkBulkDeleteView(generic.BulkDeleteView): # Circuit Types # +@register_model_view(CircuitType, 'list', path='', detail=False) class CircuitTypeListView(generic.ObjectListView): queryset = CircuitType.objects.annotate( circuit_count=count_related(Circuit, 'type') @@ -218,6 +234,7 @@ class CircuitTypeView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(CircuitType, 'add', detail=False) @register_model_view(CircuitType, 'edit') class CircuitTypeEditView(generic.ObjectEditView): queryset = CircuitType.objects.all() @@ -229,11 +246,13 @@ class CircuitTypeDeleteView(generic.ObjectDeleteView): queryset = CircuitType.objects.all() +@register_model_view(CircuitType, 'bulk_import', detail=False) class CircuitTypeBulkImportView(generic.BulkImportView): queryset = CircuitType.objects.all() model_form = forms.CircuitTypeImportForm +@register_model_view(CircuitType, 'bulk_edit', path='edit', detail=False) class CircuitTypeBulkEditView(generic.BulkEditView): queryset = CircuitType.objects.annotate( circuit_count=count_related(Circuit, 'type') @@ -243,6 +262,7 @@ class CircuitTypeBulkEditView(generic.BulkEditView): form = forms.CircuitTypeBulkEditForm +@register_model_view(CircuitType, 'bulk_delete', path='delete', detail=False) class CircuitTypeBulkDeleteView(generic.BulkDeleteView): queryset = CircuitType.objects.annotate( circuit_count=count_related(Circuit, 'type') @@ -255,10 +275,10 @@ class CircuitTypeBulkDeleteView(generic.BulkDeleteView): # Circuits # +@register_model_view(Circuit, 'list', path='', detail=False) class CircuitListView(generic.ObjectListView): queryset = Circuit.objects.prefetch_related( - 'tenant__group', 'termination_a__site', 'termination_z__site', - 'termination_a__provider_network', 'termination_z__provider_network', + 'tenant__group', 'termination_a__termination', 'termination_z__termination', ) filterset = filtersets.CircuitFilterSet filterset_form = forms.CircuitFilterForm @@ -270,6 +290,7 @@ class CircuitView(generic.ObjectView): queryset = Circuit.objects.all() +@register_model_view(Circuit, 'add', detail=False) @register_model_view(Circuit, 'edit') class CircuitEditView(generic.ObjectEditView): queryset = Circuit.objects.all() @@ -281,6 +302,7 @@ class CircuitDeleteView(generic.ObjectDeleteView): queryset = Circuit.objects.all() +@register_model_view(Circuit, 'bulk_import', detail=False) class CircuitBulkImportView(generic.BulkImportView): queryset = Circuit.objects.all() model_form = forms.CircuitImportForm @@ -296,20 +318,20 @@ class CircuitBulkImportView(generic.BulkImportView): return data +@register_model_view(Circuit, 'bulk_edit', path='edit', detail=False) class CircuitBulkEditView(generic.BulkEditView): queryset = Circuit.objects.prefetch_related( - 'termination_a__site', 'termination_z__site', - 'termination_a__provider_network', 'termination_z__provider_network', + 'tenant__group', 'termination_a__termination', 'termination_z__termination', ) filterset = filtersets.CircuitFilterSet table = tables.CircuitTable form = forms.CircuitBulkEditForm +@register_model_view(Circuit, 'bulk_delete', path='delete', detail=False) class CircuitBulkDeleteView(generic.BulkDeleteView): queryset = Circuit.objects.prefetch_related( - 'termination_a__site', 'termination_z__site', - 'termination_a__provider_network', 'termination_z__provider_network', + 'tenant__group', 'termination_a__termination', 'termination_z__termination', ) filterset = filtersets.CircuitFilterSet table = tables.CircuitTable @@ -400,6 +422,7 @@ class CircuitContactsView(ObjectContactsView): # Circuit terminations # +@register_model_view(CircuitTermination, 'list', path='', detail=False) class CircuitTerminationListView(generic.ObjectListView): queryset = CircuitTermination.objects.all() filterset = filtersets.CircuitTerminationFilterSet @@ -412,6 +435,7 @@ class CircuitTerminationView(generic.ObjectView): queryset = CircuitTermination.objects.all() +@register_model_view(CircuitTermination, 'add', detail=False) @register_model_view(CircuitTermination, 'edit') class CircuitTerminationEditView(generic.ObjectEditView): queryset = CircuitTermination.objects.all() @@ -423,11 +447,13 @@ class CircuitTerminationDeleteView(generic.ObjectDeleteView): queryset = CircuitTermination.objects.all() +@register_model_view(CircuitTermination, 'bulk_import', detail=False) class CircuitTerminationBulkImportView(generic.BulkImportView): queryset = CircuitTermination.objects.all() model_form = forms.CircuitTerminationImportForm +@register_model_view(CircuitTermination, 'bulk_edit', path='edit', detail=False) class CircuitTerminationBulkEditView(generic.BulkEditView): queryset = CircuitTermination.objects.all() filterset = filtersets.CircuitTerminationFilterSet @@ -435,6 +461,7 @@ class CircuitTerminationBulkEditView(generic.BulkEditView): form = forms.CircuitTerminationBulkEditForm +@register_model_view(CircuitTermination, 'bulk_delete', path='delete', detail=False) class CircuitTerminationBulkDeleteView(generic.BulkDeleteView): queryset = CircuitTermination.objects.all() filterset = filtersets.CircuitTerminationFilterSet @@ -449,6 +476,7 @@ register_model_view(CircuitTermination, 'trace', kwargs={'model': CircuitTermina # Circuit Groups # +@register_model_view(CircuitGroup, 'list', path='', detail=False) class CircuitGroupListView(generic.ObjectListView): queryset = CircuitGroup.objects.annotate( circuit_group_assignment_count=count_related(CircuitGroupAssignment, 'group') @@ -468,6 +496,7 @@ class CircuitGroupView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(CircuitGroup, 'add', detail=False) @register_model_view(CircuitGroup, 'edit') class CircuitGroupEditView(generic.ObjectEditView): queryset = CircuitGroup.objects.all() @@ -479,11 +508,13 @@ class CircuitGroupDeleteView(generic.ObjectDeleteView): queryset = CircuitGroup.objects.all() +@register_model_view(CircuitGroup, 'bulk_import', detail=False) class CircuitGroupBulkImportView(generic.BulkImportView): queryset = CircuitGroup.objects.all() model_form = forms.CircuitGroupImportForm +@register_model_view(CircuitGroup, 'bulk_edit', path='edit', detail=False) class CircuitGroupBulkEditView(generic.BulkEditView): queryset = CircuitGroup.objects.all() filterset = filtersets.CircuitGroupFilterSet @@ -491,6 +522,7 @@ class CircuitGroupBulkEditView(generic.BulkEditView): form = forms.CircuitGroupBulkEditForm +@register_model_view(CircuitGroup, 'bulk_delete', path='delete', detail=False) class CircuitGroupBulkDeleteView(generic.BulkDeleteView): queryset = CircuitGroup.objects.all() filterset = filtersets.CircuitGroupFilterSet @@ -501,6 +533,7 @@ class CircuitGroupBulkDeleteView(generic.BulkDeleteView): # Circuit Groups # +@register_model_view(CircuitGroupAssignment, 'list', path='', detail=False) class CircuitGroupAssignmentListView(generic.ObjectListView): queryset = CircuitGroupAssignment.objects.all() filterset = filtersets.CircuitGroupAssignmentFilterSet @@ -513,6 +546,7 @@ class CircuitGroupAssignmentView(generic.ObjectView): queryset = CircuitGroupAssignment.objects.all() +@register_model_view(CircuitGroupAssignment, 'add', detail=False) @register_model_view(CircuitGroupAssignment, 'edit') class CircuitGroupAssignmentEditView(generic.ObjectEditView): queryset = CircuitGroupAssignment.objects.all() @@ -524,11 +558,13 @@ class CircuitGroupAssignmentDeleteView(generic.ObjectDeleteView): queryset = CircuitGroupAssignment.objects.all() +@register_model_view(CircuitGroupAssignment, 'bulk_import', detail=False) class CircuitGroupAssignmentBulkImportView(generic.BulkImportView): queryset = CircuitGroupAssignment.objects.all() model_form = forms.CircuitGroupAssignmentImportForm +@register_model_view(CircuitGroupAssignment, 'bulk_edit', path='edit', detail=False) class CircuitGroupAssignmentBulkEditView(generic.BulkEditView): queryset = CircuitGroupAssignment.objects.all() filterset = filtersets.CircuitGroupAssignmentFilterSet @@ -536,7 +572,114 @@ class CircuitGroupAssignmentBulkEditView(generic.BulkEditView): form = forms.CircuitGroupAssignmentBulkEditForm +@register_model_view(CircuitGroupAssignment, 'bulk_delete', path='delete', detail=False) class CircuitGroupAssignmentBulkDeleteView(generic.BulkDeleteView): queryset = CircuitGroupAssignment.objects.all() filterset = filtersets.CircuitGroupAssignmentFilterSet table = tables.CircuitGroupAssignmentTable + + +# +# Virtual circuits +# + +class VirtualCircuitListView(generic.ObjectListView): + queryset = VirtualCircuit.objects.annotate( + termination_count=count_related(VirtualCircuitTermination, 'virtual_circuit') + ) + filterset = filtersets.VirtualCircuitFilterSet + filterset_form = forms.VirtualCircuitFilterForm + table = tables.VirtualCircuitTable + + +@register_model_view(VirtualCircuit) +class VirtualCircuitView(generic.ObjectView): + queryset = VirtualCircuit.objects.all() + + +@register_model_view(VirtualCircuit, 'edit') +class VirtualCircuitEditView(generic.ObjectEditView): + queryset = VirtualCircuit.objects.all() + form = forms.VirtualCircuitForm + + +@register_model_view(VirtualCircuit, 'delete') +class VirtualCircuitDeleteView(generic.ObjectDeleteView): + queryset = VirtualCircuit.objects.all() + + +class VirtualCircuitBulkImportView(generic.BulkImportView): + queryset = VirtualCircuit.objects.all() + model_form = forms.VirtualCircuitImportForm + additional_permissions = [ + 'circuits.add_virtualcircuittermination', + ] + related_object_forms = { + 'terminations': forms.VirtualCircuitTerminationImportRelatedForm, + } + + def prep_related_object_data(self, parent, data): + data.update({'virtual_circuit': parent}) + return data + + +class VirtualCircuitBulkEditView(generic.BulkEditView): + queryset = VirtualCircuit.objects.annotate( + termination_count=count_related(VirtualCircuitTermination, 'virtual_circuit') + ) + filterset = filtersets.VirtualCircuitFilterSet + table = tables.VirtualCircuitTable + form = forms.VirtualCircuitBulkEditForm + + +class VirtualCircuitBulkDeleteView(generic.BulkDeleteView): + queryset = VirtualCircuit.objects.annotate( + termination_count=count_related(VirtualCircuitTermination, 'virtual_circuit') + ) + filterset = filtersets.VirtualCircuitFilterSet + table = tables.VirtualCircuitTable + + +# +# Virtual circuit terminations +# + +class VirtualCircuitTerminationListView(generic.ObjectListView): + queryset = VirtualCircuitTermination.objects.all() + filterset = filtersets.VirtualCircuitTerminationFilterSet + filterset_form = forms.VirtualCircuitTerminationFilterForm + table = tables.VirtualCircuitTerminationTable + + +@register_model_view(VirtualCircuitTermination) +class VirtualCircuitTerminationView(generic.ObjectView): + queryset = VirtualCircuitTermination.objects.all() + + +@register_model_view(VirtualCircuitTermination, 'edit') +class VirtualCircuitTerminationEditView(generic.ObjectEditView): + queryset = VirtualCircuitTermination.objects.all() + form = forms.VirtualCircuitTerminationForm + + +@register_model_view(VirtualCircuitTermination, 'delete') +class VirtualCircuitTerminationDeleteView(generic.ObjectDeleteView): + queryset = VirtualCircuitTermination.objects.all() + + +class VirtualCircuitTerminationBulkImportView(generic.BulkImportView): + queryset = VirtualCircuitTermination.objects.all() + model_form = forms.VirtualCircuitTerminationImportForm + + +class VirtualCircuitTerminationBulkEditView(generic.BulkEditView): + queryset = VirtualCircuitTermination.objects.all() + filterset = filtersets.VirtualCircuitTerminationFilterSet + table = tables.VirtualCircuitTerminationTable + form = forms.VirtualCircuitTerminationBulkEditForm + + +class VirtualCircuitTerminationBulkDeleteView(generic.BulkDeleteView): + queryset = VirtualCircuitTermination.objects.all() + filterset = filtersets.VirtualCircuitTerminationFilterSet + table = tables.VirtualCircuitTerminationTable diff --git a/netbox/core/api/nested_serializers.py b/netbox/core/api/nested_serializers.py deleted file mode 100644 index df7b41ca7..000000000 --- a/netbox/core/api/nested_serializers.py +++ /dev/null @@ -1,47 +0,0 @@ -import warnings - -from rest_framework import serializers - -from core.choices import JobStatusChoices -from core.models import * -from netbox.api.fields import ChoiceField -from netbox.api.serializers import WritableNestedSerializer -from users.api.serializers import UserSerializer - -__all__ = ( - 'NestedDataFileSerializer', - 'NestedDataSourceSerializer', - 'NestedJobSerializer', -) - -# TODO: Remove in v4.2 -warnings.warn( - "Dedicated nested serializers will be removed in NetBox v4.2. Use Serializer(nested=True) instead.", - DeprecationWarning -) - - -class NestedDataSourceSerializer(WritableNestedSerializer): - - class Meta: - model = DataSource - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedDataFileSerializer(WritableNestedSerializer): - - class Meta: - model = DataFile - fields = ['id', 'url', 'display_url', 'display', 'path'] - - -class NestedJobSerializer(serializers.ModelSerializer): - status = ChoiceField(choices=JobStatusChoices) - user = UserSerializer( - nested=True, - read_only=True - ) - - class Meta: - model = Job - fields = ['url', 'display_url', 'created', 'completed', 'user', 'status'] diff --git a/netbox/core/api/schema.py b/netbox/core/api/schema.py index 1ac822b8c..663ee2899 100644 --- a/netbox/core/api/schema.py +++ b/netbox/core/api/schema.py @@ -35,7 +35,10 @@ class ChoiceFieldFix(OpenApiSerializerFieldExtension): elif direction == "response": value = build_cf - label = {**build_basic_type(OpenApiTypes.STR), "enum": list(OrderedDict.fromkeys(self.target.choices.values()))} + label = { + **build_basic_type(OpenApiTypes.STR), + "enum": list(OrderedDict.fromkeys(self.target.choices.values())) + } return build_object_type( properties={ @@ -155,6 +158,9 @@ class NetBoxAutoSchema(AutoSchema): fields = {} if hasattr(serializer, 'child') else serializer.fields remove_fields = [] + # If you get a failure here for "AttributeError: 'cached_property' object has no attribute 'items'" + # it is probably because you are using a viewsets.ViewSet for the API View and are defining a + # serializer_class. You will also need to define a get_serializer() method like for GenericAPIView. for child_name, child in fields.items(): # read_only fields don't need to be in writable (write only) serializers if 'read_only' in dir(child) and child.read_only: diff --git a/netbox/core/api/serializers.py b/netbox/core/api/serializers.py index 2dde6be9f..9a6d4d726 100644 --- a/netbox/core/api/serializers.py +++ b/netbox/core/api/serializers.py @@ -1,3 +1,4 @@ from .serializers_.change_logging import * from .serializers_.data import * from .serializers_.jobs import * +from .serializers_.tasks import * diff --git a/netbox/core/api/serializers_/jobs.py b/netbox/core/api/serializers_/jobs.py index 544dddb56..306287e88 100644 --- a/netbox/core/api/serializers_/jobs.py +++ b/netbox/core/api/serializers_/jobs.py @@ -22,7 +22,7 @@ class JobSerializer(BaseModelSerializer): class Meta: model = Job fields = [ - 'id', 'url', 'display_url', 'display', 'object_type', 'object_id', 'name', 'status', 'created', 'scheduled', 'interval', - 'started', 'completed', 'user', 'data', 'error', 'job_id', + 'id', 'url', 'display_url', 'display', 'object_type', 'object_id', 'name', 'status', 'created', 'scheduled', + 'interval', 'started', 'completed', 'user', 'data', 'error', 'job_id', ] brief_fields = ('url', 'created', 'completed', 'user', 'status') diff --git a/netbox/core/api/serializers_/tasks.py b/netbox/core/api/serializers_/tasks.py new file mode 100644 index 000000000..53f2b5126 --- /dev/null +++ b/netbox/core/api/serializers_/tasks.py @@ -0,0 +1,87 @@ +from rest_framework import serializers +from rest_framework.reverse import reverse + +__all__ = ( + 'BackgroundTaskSerializer', + 'BackgroundQueueSerializer', + 'BackgroundWorkerSerializer', +) + + +class BackgroundTaskSerializer(serializers.Serializer): + id = serializers.CharField() + url = serializers.HyperlinkedIdentityField( + view_name='core-api:rqtask-detail', + lookup_field='id', + lookup_url_kwarg='pk' + ) + description = serializers.CharField() + origin = serializers.CharField() + func_name = serializers.CharField() + args = serializers.ListField(child=serializers.CharField()) + kwargs = serializers.DictField() + result = serializers.CharField() + timeout = serializers.IntegerField() + result_ttl = serializers.IntegerField() + created_at = serializers.DateTimeField() + enqueued_at = serializers.DateTimeField() + started_at = serializers.DateTimeField() + ended_at = serializers.DateTimeField() + worker_name = serializers.CharField() + position = serializers.SerializerMethodField() + status = serializers.SerializerMethodField() + meta = serializers.DictField() + last_heartbeat = serializers.CharField() + + is_finished = serializers.BooleanField() + is_queued = serializers.BooleanField() + is_failed = serializers.BooleanField() + is_started = serializers.BooleanField() + is_deferred = serializers.BooleanField() + is_canceled = serializers.BooleanField() + is_scheduled = serializers.BooleanField() + is_stopped = serializers.BooleanField() + + def get_position(self, obj) -> int: + return obj.get_position() + + def get_status(self, obj) -> str: + return obj.get_status() + + +class BackgroundQueueSerializer(serializers.Serializer): + name = serializers.CharField() + url = serializers.SerializerMethodField() + jobs = serializers.IntegerField() + oldest_job_timestamp = serializers.CharField() + index = serializers.IntegerField() + scheduler_pid = serializers.CharField() + workers = serializers.IntegerField() + finished_jobs = serializers.IntegerField() + started_jobs = serializers.IntegerField() + deferred_jobs = serializers.IntegerField() + failed_jobs = serializers.IntegerField() + scheduled_jobs = serializers.IntegerField() + + def get_url(self, obj): + return reverse('core-api:rqqueue-detail', args=[obj['name']], request=self.context.get("request")) + + +class BackgroundWorkerSerializer(serializers.Serializer): + name = serializers.CharField() + url = serializers.HyperlinkedIdentityField( + view_name='core-api:rqworker-detail', + lookup_field='name' + ) + state = serializers.SerializerMethodField() + birth_date = serializers.DateTimeField() + queue_names = serializers.ListField( + child=serializers.CharField() + ) + pid = serializers.CharField() + successful_job_count = serializers.IntegerField() + failed_job_count = serializers.IntegerField() + total_working_time = serializers.IntegerField() + + def get_state(self, obj): + return obj.get_state() diff --git a/netbox/core/api/urls.py b/netbox/core/api/urls.py index 95ee1896e..3c22f1cf4 100644 --- a/netbox/core/api/urls.py +++ b/netbox/core/api/urls.py @@ -1,6 +1,7 @@ from netbox.api.routers import NetBoxRouter from . import views +app_name = 'core-api' router = NetBoxRouter() router.APIRootView = views.CoreRootView @@ -9,6 +10,8 @@ router.register('data-sources', views.DataSourceViewSet) router.register('data-files', views.DataFileViewSet) router.register('jobs', views.JobViewSet) router.register('object-changes', views.ObjectChangeViewSet) +router.register('background-queues', views.BackgroundQueueViewSet, basename='rqqueue') +router.register('background-workers', views.BackgroundWorkerViewSet, basename='rqworker') +router.register('background-tasks', views.BackgroundTaskViewSet, basename='rqtask') -app_name = 'core-api' urlpatterns = router.urls diff --git a/netbox/core/api/views.py b/netbox/core/api/views.py index b3a024c02..4e5b148fc 100644 --- a/netbox/core/api/views.py +++ b/netbox/core/api/views.py @@ -1,5 +1,8 @@ +from django.http import Http404, HttpResponse from django.shortcuts import get_object_or_404 from django.utils.translation import gettext_lazy as _ +from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import extend_schema from rest_framework.decorators import action from rest_framework.exceptions import PermissionDenied from rest_framework.response import Response @@ -10,8 +13,17 @@ from core import filtersets from core.choices import DataSourceStatusChoices from core.jobs import SyncDataSourceJob from core.models import * +from core.utils import delete_rq_job, enqueue_rq_job, get_rq_jobs, requeue_rq_job, stop_rq_job +from django_rq.queues import get_redis_connection +from django_rq.utils import get_statistics +from django_rq.settings import QUEUES_LIST from netbox.api.metadata import ContentTypeMetadata +from netbox.api.pagination import LimitOffsetListPagination from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet +from rest_framework import viewsets +from rest_framework.permissions import IsAdminUser +from rq.job import Job as RQ_Job +from rq.worker import Worker from . import serializers @@ -71,3 +83,152 @@ class ObjectChangeViewSet(ReadOnlyModelViewSet): queryset = ObjectChange.objects.valid_models() serializer_class = serializers.ObjectChangeSerializer filterset_class = filtersets.ObjectChangeFilterSet + + +class BaseRQViewSet(viewsets.ViewSet): + """ + Base class for RQ view sets. Provides a list() method. Subclasses must implement get_data(). + """ + permission_classes = [IsAdminUser] + serializer_class = None + + def get_data(self): + raise NotImplementedError() + + @extend_schema(responses={200: OpenApiTypes.OBJECT}) + def list(self, request): + data = self.get_data() + paginator = LimitOffsetListPagination() + data = paginator.paginate_list(data, request) + + serializer = self.serializer_class(data, many=True, context={'request': request}) + return paginator.get_paginated_response(serializer.data) + + def get_serializer(self, *args, **kwargs): + """ + Return the serializer instance that should be used for validating and + deserializing input, and for serializing output. + """ + serializer_class = self.get_serializer_class() + kwargs['context'] = self.get_serializer_context() + return serializer_class(*args, **kwargs) + + +class BackgroundQueueViewSet(BaseRQViewSet): + """ + Retrieve a list of RQ Queues. + Note: Queue names are not URL safe so not returning a detail view. + """ + serializer_class = serializers.BackgroundQueueSerializer + lookup_field = 'name' + lookup_value_regex = r'[\w.@+-]+' + + def get_view_name(self): + return "Background Queues" + + def get_data(self): + return get_statistics(run_maintenance_tasks=True)["queues"] + + @extend_schema(responses={200: OpenApiTypes.OBJECT}) + def retrieve(self, request, name): + data = self.get_data() + if not data: + raise Http404 + + for queue in data: + if queue['name'] == name: + serializer = self.serializer_class(queue, context={'request': request}) + return Response(serializer.data) + + raise Http404 + + +class BackgroundWorkerViewSet(BaseRQViewSet): + """ + Retrieve a list of RQ Workers. + """ + serializer_class = serializers.BackgroundWorkerSerializer + lookup_field = 'name' + + def get_view_name(self): + return "Background Workers" + + def get_data(self): + config = QUEUES_LIST[0] + return Worker.all(get_redis_connection(config['connection_config'])) + + def retrieve(self, request, name): + # all the RQ queues should use the same connection + config = QUEUES_LIST[0] + workers = Worker.all(get_redis_connection(config['connection_config'])) + worker = next((item for item in workers if item.name == name), None) + if not worker: + raise Http404 + + serializer = serializers.BackgroundWorkerSerializer(worker, context={'request': request}) + return Response(serializer.data) + + +class BackgroundTaskViewSet(BaseRQViewSet): + """ + Retrieve a list of RQ Tasks. + """ + serializer_class = serializers.BackgroundTaskSerializer + + def get_view_name(self): + return "Background Tasks" + + def get_data(self): + return get_rq_jobs() + + def get_task_from_id(self, task_id): + config = QUEUES_LIST[0] + task = RQ_Job.fetch(task_id, connection=get_redis_connection(config['connection_config'])) + if not task: + raise Http404 + + return task + + @extend_schema(responses={200: OpenApiTypes.OBJECT}) + def retrieve(self, request, pk): + """ + Retrieve the details of the specified RQ Task. + """ + task = self.get_task_from_id(pk) + serializer = self.serializer_class(task, context={'request': request}) + return Response(serializer.data) + + @action(methods=["POST"], detail=True) + def delete(self, request, pk): + """ + Delete the specified RQ Task. + """ + delete_rq_job(pk) + return HttpResponse(status=200) + + @action(methods=["POST"], detail=True) + def requeue(self, request, pk): + """ + Requeues the specified RQ Task. + """ + requeue_rq_job(pk) + return HttpResponse(status=200) + + @action(methods=["POST"], detail=True) + def enqueue(self, request, pk): + """ + Enqueues the specified RQ Task. + """ + enqueue_rq_job(pk) + return HttpResponse(status=200) + + @action(methods=["POST"], detail=True) + def stop(self, request, pk): + """ + Stops the specified RQ Task. + """ + stopped_jobs = stop_rq_job(pk) + if len(stopped_jobs) == 1: + return HttpResponse(status=200) + else: + return HttpResponse(status=204) diff --git a/netbox/core/choices.py b/netbox/core/choices.py index 01a072ce1..442acc26b 100644 --- a/netbox/core/choices.py +++ b/netbox/core/choices.py @@ -72,6 +72,20 @@ class JobStatusChoices(ChoiceSet): ) +class JobIntervalChoices(ChoiceSet): + INTERVAL_MINUTELY = 1 + INTERVAL_HOURLY = 60 + INTERVAL_DAILY = 60 * 24 + INTERVAL_WEEKLY = 60 * 24 * 7 + + CHOICES = ( + (INTERVAL_MINUTELY, _('Minutely')), + (INTERVAL_HOURLY, _('Hourly')), + (INTERVAL_DAILY, _('Daily')), + (INTERVAL_WEEKLY, _('Weekly')), + ) + + # # ObjectChanges # diff --git a/netbox/core/management/commands/rqworker.py b/netbox/core/management/commands/rqworker.py index e1fb6fd11..b2879c3d8 100644 --- a/netbox/core/management/commands/rqworker.py +++ b/netbox/core/management/commands/rqworker.py @@ -2,6 +2,8 @@ import logging from django_rq.management.commands.rqworker import Command as _Command +from netbox.registry import registry + DEFAULT_QUEUES = ('high', 'default', 'low') @@ -14,6 +16,15 @@ class Command(_Command): of only the 'default' queue). """ def handle(self, *args, **options): + # Setup system jobs. + for job, kwargs in registry['system_jobs'].items(): + try: + interval = kwargs['interval'] + except KeyError: + raise TypeError("System job must specify an interval (in minutes).") + logger.debug(f"Scheduling system job {job.name} (interval={interval})") + job.enqueue_once(**kwargs) + # Run the worker with scheduler functionality options['with_scheduler'] = True diff --git a/netbox/core/migrations/0001_squashed_0005.py b/netbox/core/migrations/0001_squashed_0005.py index 971370bf2..b89fa3b25 100644 --- a/netbox/core/migrations/0001_squashed_0005.py +++ b/netbox/core/migrations/0001_squashed_0005.py @@ -8,13 +8,12 @@ import utilities.json class Migration(migrations.Migration): - replaces = [ ('core', '0001_initial'), ('core', '0002_managedfile'), ('core', '0003_job'), ('core', '0004_replicate_jobresults'), - ('core', '0005_job_created_auto_now') + ('core', '0005_job_created_auto_now'), ] dependencies = [ @@ -30,7 +29,10 @@ class Migration(migrations.Migration): ('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)), + ( + '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)), @@ -55,9 +57,28 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(editable=False)), ('path', models.CharField(editable=False, max_length=1000)), ('size', models.PositiveIntegerField(editable=False)), - ('hash', models.CharField(editable=False, max_length=64, validators=[django.core.validators.RegexValidator(message='Length must be 64 hexadecimal characters.', regex='^[0-9a-f]{64}$')])), + ( + 'hash', + models.CharField( + editable=False, + max_length=64, + validators=[ + django.core.validators.RegexValidator( + message='Length must be 64 hexadecimal characters.', regex='^[0-9a-f]{64}$' + ) + ], + ), + ), ('data', models.BinaryField()), - ('source', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='datafiles', to='core.datasource')), + ( + 'source', + models.ForeignKey( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name='datafiles', + to='core.datasource', + ), + ), ], options={ 'ordering': ('source', 'path'), @@ -76,8 +97,18 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('object_id', models.PositiveBigIntegerField()), - ('datafile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='core.datafile')), - ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype')), + ( + 'datafile', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='+', to='core.datafile' + ), + ), + ( + 'object_type', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype' + ), + ), ], options={ 'indexes': [models.Index(fields=['object_type', 'object_id'], name='core_autosy_object__c17bac_idx')], @@ -97,8 +128,26 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(blank=True, editable=False, null=True)), ('file_root', models.CharField(max_length=1000)), ('file_path', models.FilePathField(editable=False)), - ('data_file', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='core.datafile')), - ('data_source', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='core.datasource')), + ( + 'data_file', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='core.datafile', + ), + ), + ( + 'data_source', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='core.datasource', + ), + ), ('auto_sync_enabled', models.BooleanField(default=False)), ], options={ @@ -108,7 +157,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='managedfile', - constraint=models.UniqueConstraint(fields=('file_root', 'file_path'), name='core_managedfile_unique_root_path'), + constraint=models.UniqueConstraint( + fields=('file_root', 'file_path'), name='core_managedfile_unique_root_path' + ), ), migrations.CreateModel( name='Job', @@ -118,14 +169,33 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=200)), ('created', models.DateTimeField()), ('scheduled', models.DateTimeField(blank=True, null=True)), - ('interval', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])), + ( + 'interval', + models.PositiveIntegerField( + blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)] + ), + ), ('started', models.DateTimeField(blank=True, null=True)), ('completed', models.DateTimeField(blank=True, null=True)), ('status', models.CharField(default='pending', max_length=30)), ('data', models.JSONField(blank=True, null=True)), ('job_id', models.UUIDField(unique=True)), - ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='jobs', to='contenttypes.contenttype')), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ( + 'object_type', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='jobs', to='contenttypes.contenttype' + ), + ), + ( + 'user', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ 'ordering': ['-created'], diff --git a/netbox/core/migrations/0006_datasource_type_remove_choices.py b/netbox/core/migrations/0006_datasource_type_remove_choices.py index 0ad8d8854..7c9914298 100644 --- a/netbox/core/migrations/0006_datasource_type_remove_choices.py +++ b/netbox/core/migrations/0006_datasource_type_remove_choices.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('core', '0005_job_created_auto_now'), ] diff --git a/netbox/core/migrations/0007_job_add_error_field.py b/netbox/core/migrations/0007_job_add_error_field.py index e2e173bfd..3b0e02b56 100644 --- a/netbox/core/migrations/0007_job_add_error_field.py +++ b/netbox/core/migrations/0007_job_add_error_field.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('core', '0006_datasource_type_remove_choices'), ] diff --git a/netbox/core/migrations/0008_contenttype_proxy.py b/netbox/core/migrations/0008_contenttype_proxy.py index dee82a969..9acaf3ad7 100644 --- a/netbox/core/migrations/0008_contenttype_proxy.py +++ b/netbox/core/migrations/0008_contenttype_proxy.py @@ -3,7 +3,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ ('contenttypes', '0002_remove_content_type_name'), ('core', '0007_job_add_error_field'), @@ -12,8 +11,7 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( name='ObjectType', - fields=[ - ], + fields=[], options={ 'proxy': True, 'indexes': [], diff --git a/netbox/core/migrations/0009_configrevision.py b/netbox/core/migrations/0009_configrevision.py index e7f817a16..6acd4531d 100644 --- a/netbox/core/migrations/0009_configrevision.py +++ b/netbox/core/migrations/0009_configrevision.py @@ -2,7 +2,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('core', '0008_contenttype_proxy'), ] diff --git a/netbox/core/migrations/0010_gfk_indexes.py b/netbox/core/migrations/0010_gfk_indexes.py index d51bc67ad..1e593a0c7 100644 --- a/netbox/core/migrations/0010_gfk_indexes.py +++ b/netbox/core/migrations/0010_gfk_indexes.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('core', '0009_configrevision'), ] diff --git a/netbox/core/migrations/0011_move_objectchange.py b/netbox/core/migrations/0011_move_objectchange.py index 2b41133ec..673763ce4 100644 --- a/netbox/core/migrations/0011_move_objectchange.py +++ b/netbox/core/migrations/0011_move_objectchange.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('contenttypes', '0002_remove_content_type_name'), ('core', '0010_gfk_indexes'), @@ -27,15 +26,49 @@ class Migration(migrations.Migration): ('object_repr', models.CharField(editable=False, max_length=200)), ('prechange_data', models.JSONField(blank=True, editable=False, null=True)), ('postchange_data', models.JSONField(blank=True, editable=False, null=True)), - ('changed_object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')), - ('related_object_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='changes', to=settings.AUTH_USER_MODEL)), + ( + 'changed_object_type', + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), + ), + ( + 'related_object_type', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), + ), + ( + 'user', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='changes', + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ 'verbose_name': 'object change', 'verbose_name_plural': 'object changes', 'ordering': ['-time'], - 'indexes': [models.Index(fields=['changed_object_type', 'changed_object_id'], name='core_object_changed_c227ce_idx'), models.Index(fields=['related_object_type', 'related_object_id'], name='core_object_related_3375d6_idx')], + 'indexes': [ + models.Index( + fields=['changed_object_type', 'changed_object_id'], + name='core_object_changed_c227ce_idx', + ), + models.Index( + fields=['related_object_type', 'related_object_id'], + name='core_object_related_3375d6_idx', + ), + ], }, ), ], diff --git a/netbox/core/migrations/0012_job_object_type_optional.py b/netbox/core/migrations/0012_job_object_type_optional.py index 3c6664afc..3798b1285 100644 --- a/netbox/core/migrations/0012_job_object_type_optional.py +++ b/netbox/core/migrations/0012_job_object_type_optional.py @@ -3,7 +3,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('contenttypes', '0002_remove_content_type_name'), ('core', '0011_move_objectchange'), @@ -18,7 +17,7 @@ class Migration(migrations.Migration): null=True, on_delete=django.db.models.deletion.CASCADE, related_name='jobs', - to='contenttypes.contenttype' + to='contenttypes.contenttype', ), ), ] diff --git a/netbox/core/models/files.py b/netbox/core/models/files.py index 7b626a441..cc446bac7 100644 --- a/netbox/core/models/files.py +++ b/netbox/core/models/files.py @@ -93,9 +93,14 @@ class ManagedFile(SyncedDataMixin, models.Model): self.file_path = os.path.basename(self.data_path) # Ensure that the file root and path make a unique pair - if self._meta.model.objects.filter(file_root=self.file_root, file_path=self.file_path).exclude(pk=self.pk).exists(): + if self._meta.model.objects.filter( + file_root=self.file_root, file_path=self.file_path + ).exclude(pk=self.pk).exists(): raise ValidationError( - f"A {self._meta.verbose_name.lower()} with this file path already exists ({self.file_root}/{self.file_path}).") + _("A {model} with this file path already exists ({path}).").format( + model=self._meta.verbose_name.lower(), + path=f"{self.file_root}/{self.file_path}" + )) def delete(self, *args, **kwargs): # Delete file from disk diff --git a/netbox/core/models/jobs.py b/netbox/core/models/jobs.py index e1b5715dd..5caa9cc2d 100644 --- a/netbox/core/models/jobs.py +++ b/netbox/core/models/jobs.py @@ -9,6 +9,7 @@ from django.db import models from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext as _ +from rq.exceptions import InvalidJobOperation from core.choices import JobStatusChoices from core.models import ObjectType @@ -130,7 +131,7 @@ class Job(models.Model): super().clean() # Validate the assigned object type - if self.object_type not in ObjectType.objects.with_feature('jobs'): + if self.object_type and self.object_type not in ObjectType.objects.with_feature('jobs'): raise ValidationError( _("Jobs cannot be assigned to this object type ({type}).").format(type=self.object_type) ) @@ -158,7 +159,11 @@ class Job(models.Model): job = queue.fetch_job(str(self.job_id)) if job: - job.cancel() + try: + job.cancel() + except InvalidJobOperation: + # Job may raise this exception from get_status() if missing from Redis + pass def start(self): """ @@ -198,7 +203,17 @@ class Job(models.Model): job_end.send(self) @classmethod - def enqueue(cls, func, instance=None, name='', user=None, schedule_at=None, interval=None, immediate=False, **kwargs): + def enqueue( + cls, + func, + instance=None, + name='', + user=None, + schedule_at=None, + interval=None, + immediate=False, + **kwargs + ): """ Create a Job instance and enqueue a job using the given callable @@ -223,7 +238,7 @@ class Job(models.Model): rq_queue_name = get_queue_for_model(object_type.model if object_type else None) queue = django_rq.get_queue(rq_queue_name) status = JobStatusChoices.STATUS_SCHEDULED if schedule_at else JobStatusChoices.STATUS_PENDING - job = Job.objects.create( + job = Job( object_type=object_type, object_id=object_id, name=name, @@ -233,6 +248,8 @@ class Job(models.Model): user=user, job_id=uuid.uuid4() ) + job.full_clean() + job.save() # Run the job immediately, rather than enqueuing it as a background task. Note that this is a synchronous # (blocking) operation, and execution will pause until the job completes. diff --git a/netbox/core/tests/test_api.py b/netbox/core/tests/test_api.py index eeb3bd9c4..d8fb8fd83 100644 --- a/netbox/core/tests/test_api.py +++ b/netbox/core/tests/test_api.py @@ -1,7 +1,14 @@ +import uuid + +from django_rq import get_queue +from django_rq.workers import get_worker from django.urls import reverse from django.utils import timezone +from rq.job import Job as RQ_Job, JobStatus +from rq.registry import FailedJobRegistry, StartedJobRegistry -from utilities.testing import APITestCase, APIViewTestCases +from users.models import Token, User +from utilities.testing import APITestCase, APIViewTestCases, TestCase from ..models import * @@ -91,3 +98,164 @@ class DataFileTest( ), ) DataFile.objects.bulk_create(data_files) + + +class BackgroundTaskTestCase(TestCase): + user_permissions = () + + @staticmethod + def dummy_job_default(): + return "Job finished" + + @staticmethod + def dummy_job_failing(): + raise Exception("Job failed") + + def setUp(self): + """ + Create a user and token for API calls. + """ + # Create the test user and assign permissions + self.user = User.objects.create_user(username='testuser') + self.user.is_staff = True + self.user.is_active = True + self.user.save() + self.token = Token.objects.create(user=self.user) + self.header = {'HTTP_AUTHORIZATION': f'Token {self.token.key}'} + + # Clear all queues prior to running each test + get_queue('default').connection.flushall() + get_queue('high').connection.flushall() + get_queue('low').connection.flushall() + + def test_background_queue_list(self): + url = reverse('core-api:rqqueue-list') + + # Attempt to load view without permission + self.user.is_staff = False + self.user.save() + response = self.client.get(url, **self.header) + self.assertEqual(response.status_code, 403) + + # Load view with permission + self.user.is_staff = True + self.user.save() + response = self.client.get(url, **self.header) + self.assertEqual(response.status_code, 200) + self.assertIn('default', str(response.content)) + self.assertIn('high', str(response.content)) + self.assertIn('low', str(response.content)) + + def test_background_queue(self): + response = self.client.get(reverse('core-api:rqqueue-detail', args=['default']), **self.header) + self.assertEqual(response.status_code, 200) + self.assertIn('default', str(response.content)) + self.assertIn('oldest_job_timestamp', str(response.content)) + self.assertIn('scheduled_jobs', str(response.content)) + + def test_background_task_list(self): + queue = get_queue('default') + queue.enqueue(self.dummy_job_default) + + response = self.client.get(reverse('core-api:rqtask-list'), **self.header) + self.assertEqual(response.status_code, 200) + self.assertIn('origin', str(response.content)) + self.assertIn('core.tests.test_api.BackgroundTaskTestCase.dummy_job_default()', str(response.content)) + + def test_background_task(self): + queue = get_queue('default') + job = queue.enqueue(self.dummy_job_default) + + response = self.client.get(reverse('core-api:rqtask-detail', args=[job.id]), **self.header) + self.assertEqual(response.status_code, 200) + self.assertIn(str(job.id), str(response.content)) + self.assertIn('origin', str(response.content)) + self.assertIn('meta', str(response.content)) + self.assertIn('kwargs', str(response.content)) + + def test_background_task_delete(self): + queue = get_queue('default') + job = queue.enqueue(self.dummy_job_default) + + response = self.client.post(reverse('core-api:rqtask-delete', args=[job.id]), **self.header) + self.assertEqual(response.status_code, 200) + self.assertFalse(RQ_Job.exists(job.id, connection=queue.connection)) + queue = get_queue('default') + self.assertNotIn(job.id, queue.job_ids) + + def test_background_task_requeue(self): + queue = get_queue('default') + + # Enqueue & run a job that will fail + job = queue.enqueue(self.dummy_job_failing) + worker = get_worker('default') + worker.work(burst=True) + self.assertTrue(job.is_failed) + + # Re-enqueue the failed job and check that its status has been reset + response = self.client.post(reverse('core-api:rqtask-requeue', args=[job.id]), **self.header) + self.assertEqual(response.status_code, 200) + job = RQ_Job.fetch(job.id, queue.connection) + self.assertFalse(job.is_failed) + + def test_background_task_enqueue(self): + queue = get_queue('default') + + # Enqueue some jobs that each depends on its predecessor + job = previous_job = None + for _ in range(0, 3): + job = queue.enqueue(self.dummy_job_default, depends_on=previous_job) + previous_job = job + + # Check that the last job to be enqueued has a status of deferred + self.assertIsNotNone(job) + self.assertEqual(job.get_status(), JobStatus.DEFERRED) + self.assertIsNone(job.enqueued_at) + + # Force-enqueue the deferred job + response = self.client.post(reverse('core-api:rqtask-enqueue', args=[job.id]), **self.header) + self.assertEqual(response.status_code, 200) + + # Check that job's status is updated correctly + job = queue.fetch_job(job.id) + self.assertEqual(job.get_status(), JobStatus.QUEUED) + self.assertIsNotNone(job.enqueued_at) + + def test_background_task_stop(self): + queue = get_queue('default') + + worker = get_worker('default') + job = queue.enqueue(self.dummy_job_default) + worker.prepare_job_execution(job) + + self.assertEqual(job.get_status(), JobStatus.STARTED) + response = self.client.post(reverse('core-api:rqtask-stop', args=[job.id]), **self.header) + self.assertEqual(response.status_code, 200) + worker.monitor_work_horse(job, queue) # Sets the job as Failed and removes from Started + started_job_registry = StartedJobRegistry(queue.name, connection=queue.connection) + self.assertEqual(len(started_job_registry), 0) + + canceled_job_registry = FailedJobRegistry(queue.name, connection=queue.connection) + self.assertEqual(len(canceled_job_registry), 1) + self.assertIn(job.id, canceled_job_registry) + + def test_worker_list(self): + worker1 = get_worker('default', name=uuid.uuid4().hex) + worker1.register_birth() + + worker2 = get_worker('high') + worker2.register_birth() + + response = self.client.get(reverse('core-api:rqworker-list'), **self.header) + self.assertEqual(response.status_code, 200) + self.assertIn(str(worker1.name), str(response.content)) + + def test_worker(self): + worker1 = get_worker('default', name=uuid.uuid4().hex) + worker1.register_birth() + + response = self.client.get(reverse('core-api:rqworker-detail', args=[worker1.name]), **self.header) + self.assertEqual(response.status_code, 200) + self.assertIn(str(worker1.name), str(response.content)) + self.assertIn('birth_date', str(response.content)) + self.assertIn('total_working_time', str(response.content)) diff --git a/netbox/core/tests/test_changelog.py b/netbox/core/tests/test_changelog.py index c58968ee8..4914dbaf3 100644 --- a/netbox/core/tests/test_changelog.py +++ b/netbox/core/tests/test_changelog.py @@ -76,10 +76,6 @@ class ChangeLogViewTest(ModelViewTestCase): self.assertEqual(oc.postchange_data['custom_fields']['cf2'], form_data['cf_cf2']) self.assertEqual(oc.postchange_data['tags'], ['Tag 1', 'Tag 2']) - # Check that private attributes were included in raw data but not display data - self.assertIn('_name', oc.postchange_data) - self.assertNotIn('_name', oc.postchange_data_clean) - def test_update_object(self): site = Site(name='Site 1', slug='site-1') site.save() @@ -117,12 +113,6 @@ class ChangeLogViewTest(ModelViewTestCase): self.assertEqual(oc.postchange_data['custom_fields']['cf2'], form_data['cf_cf2']) self.assertEqual(oc.postchange_data['tags'], ['Tag 3']) - # Check that private attributes were included in raw data but not display data - self.assertIn('_name', oc.prechange_data) - self.assertNotIn('_name', oc.prechange_data_clean) - self.assertIn('_name', oc.postchange_data) - self.assertNotIn('_name', oc.postchange_data_clean) - def test_delete_object(self): site = Site( name='Site 1', @@ -153,10 +143,6 @@ class ChangeLogViewTest(ModelViewTestCase): self.assertEqual(oc.prechange_data['tags'], ['Tag 1', 'Tag 2']) self.assertEqual(oc.postchange_data, None) - # Check that private attributes were included in raw data but not display data - self.assertIn('_name', oc.prechange_data) - self.assertNotIn('_name', oc.prechange_data_clean) - def test_bulk_update_objects(self): sites = ( Site(name='Site 1', slug='site-1', status=SiteStatusChoices.STATUS_ACTIVE), @@ -353,10 +339,6 @@ class ChangeLogAPITest(APITestCase): self.assertEqual(oc.postchange_data['custom_fields'], data['custom_fields']) self.assertEqual(oc.postchange_data['tags'], ['Tag 1', 'Tag 2']) - # Check that private attributes were included in raw data but not display data - self.assertIn('_name', oc.postchange_data) - self.assertNotIn('_name', oc.postchange_data_clean) - def test_update_object(self): site = Site(name='Site 1', slug='site-1') site.save() @@ -389,12 +371,6 @@ class ChangeLogAPITest(APITestCase): self.assertEqual(oc.postchange_data['custom_fields'], data['custom_fields']) self.assertEqual(oc.postchange_data['tags'], ['Tag 3']) - # Check that private attributes were included in raw data but not display data - self.assertIn('_name', oc.prechange_data) - self.assertNotIn('_name', oc.prechange_data_clean) - self.assertIn('_name', oc.postchange_data) - self.assertNotIn('_name', oc.postchange_data_clean) - def test_delete_object(self): site = Site( name='Site 1', @@ -423,10 +399,6 @@ class ChangeLogAPITest(APITestCase): self.assertEqual(oc.prechange_data['tags'], ['Tag 1', 'Tag 2']) self.assertEqual(oc.postchange_data, None) - # Check that private attributes were included in raw data but not display data - self.assertIn('_name', oc.prechange_data) - self.assertNotIn('_name', oc.prechange_data_clean) - def test_bulk_create_objects(self): data = ( { diff --git a/netbox/core/tests/test_views.py b/netbox/core/tests/test_views.py index 3c847e4ef..047b51ef6 100644 --- a/netbox/core/tests/test_views.py +++ b/netbox/core/tests/test_views.py @@ -308,6 +308,7 @@ class BackgroundTaskTestCase(TestCase): worker = get_worker('default') job = queue.enqueue(self.dummy_job_default) worker.prepare_job_execution(job) + worker.prepare_execution(job) self.assertEqual(job.get_status(), JobStatus.STARTED) @@ -345,3 +346,32 @@ class BackgroundTaskTestCase(TestCase): self.assertIn(str(worker1.name), str(response.content)) self.assertIn('Birth', str(response.content)) self.assertIn('Total working time', str(response.content)) + + +class SystemTestCase(TestCase): + + def setUp(self): + super().setUp() + + self.user.is_staff = True + self.user.save() + + def test_system_view_default(self): + # Test UI render + response = self.client.get(reverse('core:system')) + self.assertEqual(response.status_code, 200) + + # Test export + response = self.client.get(f"{reverse('core:system')}?export=true") + self.assertEqual(response.status_code, 200) + + def test_system_view_with_config_revision(self): + ConfigRevision.objects.create() + + # Test UI render + response = self.client.get(reverse('core:system')) + self.assertEqual(response.status_code, 200) + + # Test export + response = self.client.get(f"{reverse('core:system')}?export=true") + self.assertEqual(response.status_code, 200) diff --git a/netbox/core/urls.py b/netbox/core/urls.py index fd6ec8996..b922c8bed 100644 --- a/netbox/core/urls.py +++ b/netbox/core/urls.py @@ -6,51 +6,50 @@ from . import views app_name = 'core' urlpatterns = ( - # Data sources - path('data-sources/', views.DataSourceListView.as_view(), name='datasource_list'), - path('data-sources/add/', views.DataSourceEditView.as_view(), name='datasource_add'), - path('data-sources/import/', views.DataSourceBulkImportView.as_view(), name='datasource_import'), - path('data-sources/edit/', views.DataSourceBulkEditView.as_view(), name='datasource_bulk_edit'), - path('data-sources/delete/', views.DataSourceBulkDeleteView.as_view(), name='datasource_bulk_delete'), + path('data-sources/', include(get_model_urls('core', 'datasource', detail=False))), path('data-sources//', include(get_model_urls('core', 'datasource'))), - # Data files - path('data-files/', views.DataFileListView.as_view(), name='datafile_list'), - path('data-files/delete/', views.DataFileBulkDeleteView.as_view(), name='datafile_bulk_delete'), + path('data-files/', include(get_model_urls('core', 'datafile', detail=False))), path('data-files//', include(get_model_urls('core', 'datafile'))), - # Job results - path('jobs/', views.JobListView.as_view(), name='job_list'), - path('jobs/delete/', views.JobBulkDeleteView.as_view(), name='job_bulk_delete'), - path('jobs//', views.JobView.as_view(), name='job'), - path('jobs//delete/', views.JobDeleteView.as_view(), name='job_delete'), + path('jobs/', include(get_model_urls('core', 'job', detail=False))), + path('jobs//', include(get_model_urls('core', 'job'))), - # Change logging - path('changelog/', views.ObjectChangeListView.as_view(), name='objectchange_list'), + path('changelog/', include(get_model_urls('core', 'objectchange', detail=False))), path('changelog//', include(get_model_urls('core', 'objectchange'))), # Background Tasks path('background-queues/', views.BackgroundQueueListView.as_view(), name='background_queue_list'), - path('background-queues///', views.BackgroundTaskListView.as_view(), name='background_task_list'), + path( + 'background-queues///', + views.BackgroundTaskListView.as_view(), + name='background_task_list' + ), path('background-tasks//', views.BackgroundTaskView.as_view(), name='background_task'), - path('background-tasks//delete/', views.BackgroundTaskDeleteView.as_view(), name='background_task_delete'), - path('background-tasks//requeue/', views.BackgroundTaskRequeueView.as_view(), name='background_task_requeue'), - path('background-tasks//enqueue/', views.BackgroundTaskEnqueueView.as_view(), name='background_task_enqueue'), + path( + 'background-tasks//delete/', + views.BackgroundTaskDeleteView.as_view(), + name='background_task_delete' + ), + path( + 'background-tasks//requeue/', + views.BackgroundTaskRequeueView.as_view(), + name='background_task_requeue' + ), + path( + 'background-tasks//enqueue/', + views.BackgroundTaskEnqueueView.as_view(), + name='background_task_enqueue' + ), path('background-tasks//stop/', views.BackgroundTaskStopView.as_view(), name='background_task_stop'), path('background-workers//', views.WorkerListView.as_view(), name='worker_list'), path('background-workers//', views.WorkerView.as_view(), name='worker'), - # Config revisions - path('config-revisions/', views.ConfigRevisionListView.as_view(), name='configrevision_list'), - path('config-revisions/add/', views.ConfigRevisionEditView.as_view(), name='configrevision_add'), - path('config-revisions/delete/', views.ConfigRevisionBulkDeleteView.as_view(), name='configrevision_bulk_delete'), - path('config-revisions//restore/', views.ConfigRevisionRestoreView.as_view(), name='configrevision_restore'), + path('config-revisions/', include(get_model_urls('core', 'configrevision', detail=False))), path('config-revisions//', include(get_model_urls('core', 'configrevision'))), - # System path('system/', views.SystemView.as_view(), name='system'), - # Plugins path('plugins/', views.PluginListView.as_view(), name='plugin_list'), path('plugins//', views.PluginView.as_view(), name='plugin'), ) diff --git a/netbox/core/utils.py b/netbox/core/utils.py new file mode 100644 index 000000000..26adfdfa2 --- /dev/null +++ b/netbox/core/utils.py @@ -0,0 +1,155 @@ +from django.http import Http404 +from django.utils.translation import gettext_lazy as _ +from django_rq.queues import get_queue, get_queue_by_index, get_redis_connection +from django_rq.settings import QUEUES_MAP, QUEUES_LIST +from django_rq.utils import get_jobs, stop_jobs +from rq import requeue_job +from rq.exceptions import NoSuchJobError +from rq.job import Job as RQ_Job, JobStatus as RQJobStatus +from rq.registry import ( + DeferredJobRegistry, + FailedJobRegistry, + FinishedJobRegistry, + ScheduledJobRegistry, + StartedJobRegistry, +) + +__all__ = ( + 'delete_rq_job', + 'enqueue_rq_job', + 'get_rq_jobs', + 'get_rq_jobs_from_status', + 'requeue_rq_job', + 'stop_rq_job', +) + + +def get_rq_jobs(): + """ + Return a list of all RQ jobs. + """ + jobs = set() + + for queue in QUEUES_LIST: + queue = get_queue(queue['name']) + jobs.update(queue.get_jobs()) + + return list(jobs) + + +def get_rq_jobs_from_status(queue, status): + """ + Return the RQ jobs with the given status. + """ + jobs = [] + + try: + registry_cls = { + RQJobStatus.STARTED: StartedJobRegistry, + RQJobStatus.DEFERRED: DeferredJobRegistry, + RQJobStatus.FINISHED: FinishedJobRegistry, + RQJobStatus.FAILED: FailedJobRegistry, + RQJobStatus.SCHEDULED: ScheduledJobRegistry, + }[status] + except KeyError: + raise Http404 + registry = registry_cls(queue.name, queue.connection) + + job_ids = registry.get_job_ids() + if status != RQJobStatus.DEFERRED: + jobs = get_jobs(queue, job_ids, registry) + else: + # Deferred jobs require special handling + for job_id in job_ids: + try: + jobs.append(RQ_Job.fetch(job_id, connection=queue.connection, serializer=queue.serializer)) + except NoSuchJobError: + pass + + if jobs and status == RQJobStatus.SCHEDULED: + for job in jobs: + job.scheduled_at = registry.get_scheduled_time(job) + + return jobs + + +def delete_rq_job(job_id): + """ + Delete the specified RQ job. + """ + config = QUEUES_LIST[0] + try: + job = RQ_Job.fetch(job_id, connection=get_redis_connection(config['connection_config']),) + except NoSuchJobError: + raise Http404(_("Job {job_id} not found").format(job_id=job_id)) + + queue_index = QUEUES_MAP[job.origin] + queue = get_queue_by_index(queue_index) + + # Remove job id from queue and delete the actual job + queue.connection.lrem(queue.key, 0, job.id) + job.delete() + + +def requeue_rq_job(job_id): + """ + Requeue the specified RQ job. + """ + config = QUEUES_LIST[0] + try: + job = RQ_Job.fetch(job_id, connection=get_redis_connection(config['connection_config']),) + except NoSuchJobError: + raise Http404(_("Job {id} not found.").format(id=job_id)) + + queue_index = QUEUES_MAP[job.origin] + queue = get_queue_by_index(queue_index) + + requeue_job(job_id, connection=queue.connection, serializer=queue.serializer) + + +def enqueue_rq_job(job_id): + """ + Enqueue the specified RQ job. + """ + config = QUEUES_LIST[0] + try: + job = RQ_Job.fetch(job_id, connection=get_redis_connection(config['connection_config']),) + except NoSuchJobError: + raise Http404(_("Job {id} not found.").format(id=job_id)) + + queue_index = QUEUES_MAP[job.origin] + queue = get_queue_by_index(queue_index) + + try: + # _enqueue_job is new in RQ 1.14, this is used to enqueue + # job regardless of its dependencies + queue._enqueue_job(job) + except AttributeError: + queue.enqueue_job(job) + + # Remove job from correct registry if needed + if job.get_status() == RQJobStatus.DEFERRED: + registry = DeferredJobRegistry(queue.name, queue.connection) + registry.remove(job) + elif job.get_status() == RQJobStatus.FINISHED: + registry = FinishedJobRegistry(queue.name, queue.connection) + registry.remove(job) + elif job.get_status() == RQJobStatus.SCHEDULED: + registry = ScheduledJobRegistry(queue.name, queue.connection) + registry.remove(job) + + +def stop_rq_job(job_id): + """ + Stop the specified RQ job. + """ + config = QUEUES_LIST[0] + try: + job = RQ_Job.fetch(job_id, connection=get_redis_connection(config['connection_config']),) + except NoSuchJobError: + raise Http404(_("Job {job_id} not found").format(job_id=job_id)) + + queue_index = QUEUES_MAP[job.origin] + queue = get_queue_by_index(queue_index) + + return stop_jobs(queue, job_id)[0] diff --git a/netbox/core/views.py b/netbox/core/views.py index 3c5319626..713807a82 100644 --- a/netbox/core/views.py +++ b/netbox/core/views.py @@ -14,16 +14,13 @@ from django.utils.translation import gettext_lazy as _ from django.views.generic import View from django_rq.queues import get_connection, get_queue_by_index, get_redis_connection from django_rq.settings import QUEUES_MAP, QUEUES_LIST -from django_rq.utils import get_jobs, get_statistics, stop_jobs -from rq import requeue_job +from django_rq.utils import get_statistics from rq.exceptions import NoSuchJobError from rq.job import Job as RQ_Job, JobStatus as RQJobStatus -from rq.registry import ( - DeferredJobRegistry, FailedJobRegistry, FinishedJobRegistry, ScheduledJobRegistry, StartedJobRegistry, -) from rq.worker import Worker from rq.worker_registration import clean_worker_registry +from core.utils import delete_rq_job, enqueue_rq_job, get_rq_jobs_from_status, requeue_rq_job, stop_rq_job from netbox.config import get_config, PARAMS from netbox.views import generic from netbox.views.generic.base import BaseObjectView @@ -46,6 +43,7 @@ from .tables import CatalogPluginTable, PluginVersionTable # Data sources # +@register_model_view(DataSource, 'list', path='', detail=False) class DataSourceListView(generic.ObjectListView): queryset = DataSource.objects.annotate( file_count=count_related(DataFile, 'source') @@ -92,6 +90,7 @@ class DataSourceSyncView(BaseObjectView): return redirect(datasource.get_absolute_url()) +@register_model_view(DataSource, 'add', detail=False) @register_model_view(DataSource, 'edit') class DataSourceEditView(generic.ObjectEditView): queryset = DataSource.objects.all() @@ -103,11 +102,13 @@ class DataSourceDeleteView(generic.ObjectDeleteView): queryset = DataSource.objects.all() +@register_model_view(DataSource, 'bulk_import', detail=False) class DataSourceBulkImportView(generic.BulkImportView): queryset = DataSource.objects.all() model_form = forms.DataSourceImportForm +@register_model_view(DataSource, 'bulk_edit', path='edit', detail=False) class DataSourceBulkEditView(generic.BulkEditView): queryset = DataSource.objects.annotate( count_files=count_related(DataFile, 'source') @@ -117,6 +118,7 @@ class DataSourceBulkEditView(generic.BulkEditView): form = forms.DataSourceBulkEditForm +@register_model_view(DataSource, 'bulk_delete', path='delete', detail=False) class DataSourceBulkDeleteView(generic.BulkDeleteView): queryset = DataSource.objects.annotate( count_files=count_related(DataFile, 'source') @@ -129,6 +131,7 @@ class DataSourceBulkDeleteView(generic.BulkDeleteView): # Data files # +@register_model_view(DataFile, 'list', path='', detail=False) class DataFileListView(generic.ObjectListView): queryset = DataFile.objects.defer('data') filterset = filtersets.DataFileFilterSet @@ -149,6 +152,7 @@ class DataFileDeleteView(generic.ObjectDeleteView): queryset = DataFile.objects.all() +@register_model_view(DataFile, 'bulk_delete', path='delete', detail=False) class DataFileBulkDeleteView(generic.BulkDeleteView): queryset = DataFile.objects.defer('data') filterset = filtersets.DataFileFilterSet @@ -159,6 +163,7 @@ class DataFileBulkDeleteView(generic.BulkDeleteView): # Jobs # +@register_model_view(Job, 'list', path='', detail=False) class JobListView(generic.ObjectListView): queryset = Job.objects.all() filterset = filtersets.JobFilterSet @@ -170,14 +175,17 @@ class JobListView(generic.ObjectListView): } +@register_model_view(Job) class JobView(generic.ObjectView): queryset = Job.objects.all() +@register_model_view(Job, 'delete') class JobDeleteView(generic.ObjectDeleteView): queryset = Job.objects.all() +@register_model_view(Job, 'bulk_delete', path='delete', detail=False) class JobBulkDeleteView(generic.BulkDeleteView): queryset = Job.objects.all() filterset = filtersets.JobFilterSet @@ -188,6 +196,7 @@ class JobBulkDeleteView(generic.BulkDeleteView): # Change logging # +@register_model_view(ObjectChange, 'list', path='', detail=False) class ObjectChangeListView(generic.ObjectListView): queryset = ObjectChange.objects.valid_models() filterset = filtersets.ObjectChangeFilterSet @@ -257,6 +266,7 @@ class ObjectChangeView(generic.ObjectView): # Config Revisions # +@register_model_view(ConfigRevision, 'list', path='', detail=False) class ConfigRevisionListView(generic.ObjectListView): queryset = ConfigRevision.objects.all() filterset = filtersets.ConfigRevisionFilterSet @@ -269,6 +279,7 @@ class ConfigRevisionView(generic.ObjectView): queryset = ConfigRevision.objects.all() +@register_model_view(ConfigRevision, 'add', detail=False) class ConfigRevisionEditView(generic.ObjectEditView): queryset = ConfigRevision.objects.all() form = forms.ConfigRevisionForm @@ -279,12 +290,14 @@ class ConfigRevisionDeleteView(generic.ObjectDeleteView): queryset = ConfigRevision.objects.all() +@register_model_view(ConfigRevision, 'bulk_delete', path='delete', detail=False) class ConfigRevisionBulkDeleteView(generic.BulkDeleteView): queryset = ConfigRevision.objects.all() filterset = filtersets.ConfigRevisionFilterSet table = tables.ConfigRevisionTable +@register_model_view(ConfigRevision, 'restore') class ConfigRevisionRestoreView(ContentTypePermissionRequiredMixin, View): def get_required_permission(self): @@ -347,41 +360,12 @@ class BackgroundTaskListView(TableMixin, BaseRQView): table = tables.BackgroundTaskTable def get_table_data(self, request, queue, status): - jobs = [] # Call get_jobs() to returned queued tasks if status == RQJobStatus.QUEUED: return queue.get_jobs() - # For other statuses, determine the registry to list (or raise a 404 for invalid statuses) - try: - registry_cls = { - RQJobStatus.STARTED: StartedJobRegistry, - RQJobStatus.DEFERRED: DeferredJobRegistry, - RQJobStatus.FINISHED: FinishedJobRegistry, - RQJobStatus.FAILED: FailedJobRegistry, - RQJobStatus.SCHEDULED: ScheduledJobRegistry, - }[status] - except KeyError: - raise Http404 - registry = registry_cls(queue.name, queue.connection) - - job_ids = registry.get_job_ids() - if status != RQJobStatus.DEFERRED: - jobs = get_jobs(queue, job_ids, registry) - else: - # Deferred jobs require special handling - for job_id in job_ids: - try: - jobs.append(RQ_Job.fetch(job_id, connection=queue.connection, serializer=queue.serializer)) - except NoSuchJobError: - pass - - if jobs and status == RQJobStatus.SCHEDULED: - for job in jobs: - job.scheduled_at = registry.get_scheduled_time(job) - - return jobs + return get_rq_jobs_from_status(queue, status) def get(self, request, queue_index, status): queue = get_queue_by_index(queue_index) @@ -447,19 +431,7 @@ class BackgroundTaskDeleteView(BaseRQView): form = ConfirmationForm(request.POST) if form.is_valid(): - # all the RQ queues should use the same connection - config = QUEUES_LIST[0] - try: - job = RQ_Job.fetch(job_id, connection=get_redis_connection(config['connection_config']),) - except NoSuchJobError: - raise Http404(_("Job {job_id} not found").format(job_id=job_id)) - - queue_index = QUEUES_MAP[job.origin] - queue = get_queue_by_index(queue_index) - - # Remove job id from queue and delete the actual job - queue.connection.lrem(queue.key, 0, job.id) - job.delete() + delete_rq_job(job_id) messages.success(request, _('Job {id} has been deleted.').format(id=job_id)) else: messages.error(request, _('Error deleting job {id}: {error}').format(id=job_id, error=form.errors[0])) @@ -470,17 +442,7 @@ class BackgroundTaskDeleteView(BaseRQView): class BackgroundTaskRequeueView(BaseRQView): def get(self, request, job_id): - # all the RQ queues should use the same connection - config = QUEUES_LIST[0] - try: - job = RQ_Job.fetch(job_id, connection=get_redis_connection(config['connection_config']),) - except NoSuchJobError: - raise Http404(_("Job {id} not found.").format(id=job_id)) - - queue_index = QUEUES_MAP[job.origin] - queue = get_queue_by_index(queue_index) - - requeue_job(job_id, connection=queue.connection, serializer=queue.serializer) + requeue_rq_job(job_id) messages.success(request, _('Job {id} has been re-enqueued.').format(id=job_id)) return redirect(reverse('core:background_task', args=[job_id])) @@ -489,33 +451,7 @@ class BackgroundTaskEnqueueView(BaseRQView): def get(self, request, job_id): # all the RQ queues should use the same connection - config = QUEUES_LIST[0] - try: - job = RQ_Job.fetch(job_id, connection=get_redis_connection(config['connection_config']),) - except NoSuchJobError: - raise Http404(_("Job {id} not found.").format(id=job_id)) - - queue_index = QUEUES_MAP[job.origin] - queue = get_queue_by_index(queue_index) - - try: - # _enqueue_job is new in RQ 1.14, this is used to enqueue - # job regardless of its dependencies - queue._enqueue_job(job) - except AttributeError: - queue.enqueue_job(job) - - # Remove job from correct registry if needed - if job.get_status() == RQJobStatus.DEFERRED: - registry = DeferredJobRegistry(queue.name, queue.connection) - registry.remove(job) - elif job.get_status() == RQJobStatus.FINISHED: - registry = FinishedJobRegistry(queue.name, queue.connection) - registry.remove(job) - elif job.get_status() == RQJobStatus.SCHEDULED: - registry = ScheduledJobRegistry(queue.name, queue.connection) - registry.remove(job) - + enqueue_rq_job(job_id) messages.success(request, _('Job {id} has been enqueued.').format(id=job_id)) return redirect(reverse('core:background_task', args=[job_id])) @@ -523,17 +459,7 @@ class BackgroundTaskEnqueueView(BaseRQView): class BackgroundTaskStopView(BaseRQView): def get(self, request, job_id): - # all the RQ queues should use the same connection - config = QUEUES_LIST[0] - try: - job = RQ_Job.fetch(job_id, connection=get_redis_connection(config['connection_config']),) - except NoSuchJobError: - raise Http404(_("Job {job_id} not found").format(job_id=job_id)) - - queue_index = QUEUES_MAP[job.origin] - queue = get_queue_by_index(queue_index) - - stopped_jobs = stop_jobs(queue, job_id)[0] + stopped_jobs = stop_rq_job(job_id) if len(stopped_jobs) == 1: messages.success(request, _('Job {id} has been stopped.').format(id=job_id)) else: @@ -626,11 +552,7 @@ class SystemView(UserPassesTestMixin, View): } # Configuration - try: - config = ConfigRevision.objects.get(pk=cache.get('config_version')) - except ConfigRevision.DoesNotExist: - # Fall back to using the active config data if no record is found - config = get_config() + config = get_config() # Raw data export if 'export' in request.GET: diff --git a/netbox/dcim/api/nested_serializers.py b/netbox/dcim/api/nested_serializers.py deleted file mode 100644 index 4b8f0db4a..000000000 --- a/netbox/dcim/api/nested_serializers.py +++ /dev/null @@ -1,389 +0,0 @@ -import warnings - -from drf_spectacular.utils import extend_schema_serializer -from rest_framework import serializers - -from dcim import models -from netbox.api.fields import RelatedObjectCountField -from netbox.api.serializers import WritableNestedSerializer -from .serializers_.nested import ( - NestedDeviceBaySerializer, NestedDeviceSerializer, NestedInterfaceSerializer, NestedInterfaceTemplateSerializer, - NestedLocationSerializer, NestedModuleBaySerializer, NestedRegionSerializer, NestedSiteGroupSerializer, -) - -__all__ = [ - 'NestedCableSerializer', - 'NestedConsolePortSerializer', - 'NestedConsolePortTemplateSerializer', - 'NestedConsoleServerPortSerializer', - 'NestedConsoleServerPortTemplateSerializer', - 'NestedDeviceBaySerializer', - 'NestedDeviceBayTemplateSerializer', - 'NestedDeviceRoleSerializer', - 'NestedDeviceSerializer', - 'NestedDeviceTypeSerializer', - 'NestedFrontPortSerializer', - 'NestedFrontPortTemplateSerializer', - 'NestedInterfaceSerializer', - 'NestedInterfaceTemplateSerializer', - 'NestedInventoryItemSerializer', - 'NestedInventoryItemRoleSerializer', - 'NestedInventoryItemTemplateSerializer', - 'NestedManufacturerSerializer', - 'NestedModuleBaySerializer', - 'NestedModuleBayTemplateSerializer', - 'NestedModuleSerializer', - 'NestedModuleTypeSerializer', - 'NestedPlatformSerializer', - 'NestedPowerFeedSerializer', - 'NestedPowerOutletSerializer', - 'NestedPowerOutletTemplateSerializer', - 'NestedPowerPanelSerializer', - 'NestedPowerPortSerializer', - 'NestedPowerPortTemplateSerializer', - 'NestedLocationSerializer', - 'NestedRackReservationSerializer', - 'NestedRackRoleSerializer', - 'NestedRackSerializer', - 'NestedRearPortSerializer', - 'NestedRearPortTemplateSerializer', - 'NestedRegionSerializer', - 'NestedSiteSerializer', - 'NestedSiteGroupSerializer', - 'NestedVirtualChassisSerializer', - 'NestedVirtualDeviceContextSerializer', -] - -# TODO: Remove in v4.2 -warnings.warn( - "Dedicated nested serializers will be removed in NetBox v4.2. Use Serializer(nested=True) instead.", - DeprecationWarning -) - - -# -# Regions/sites -# - -class NestedSiteSerializer(WritableNestedSerializer): - - class Meta: - model = models.Site - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug'] - - -# -# Racks -# - -@extend_schema_serializer( - exclude_fields=('rack_count',), -) -class NestedRackRoleSerializer(WritableNestedSerializer): - rack_count = RelatedObjectCountField('racks') - - class Meta: - model = models.RackRole - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'rack_count'] - - -@extend_schema_serializer( - exclude_fields=('device_count',), -) -class NestedRackSerializer(WritableNestedSerializer): - device_count = RelatedObjectCountField('devices') - - class Meta: - model = models.Rack - fields = ['id', 'url', 'display_url', 'display', 'name', 'device_count'] - - -class NestedRackReservationSerializer(WritableNestedSerializer): - user = serializers.SerializerMethodField(read_only=True) - - class Meta: - model = models.RackReservation - fields = ['id', 'url', 'display_url', 'display', 'user', 'units'] - - def get_user(self, obj): - return obj.user.username - - -# -# Device/module types -# - -@extend_schema_serializer( - exclude_fields=('devicetype_count',), -) -class NestedManufacturerSerializer(WritableNestedSerializer): - devicetype_count = RelatedObjectCountField('device_types') - - class Meta: - model = models.Manufacturer - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'devicetype_count'] - - -@extend_schema_serializer( - exclude_fields=('device_count',), -) -class NestedDeviceTypeSerializer(WritableNestedSerializer): - manufacturer = NestedManufacturerSerializer(read_only=True) - device_count = RelatedObjectCountField('instances') - - class Meta: - model = models.DeviceType - fields = ['id', 'url', 'display_url', 'display', 'manufacturer', 'model', 'slug', 'device_count'] - - -class NestedModuleTypeSerializer(WritableNestedSerializer): - manufacturer = NestedManufacturerSerializer(read_only=True) - - class Meta: - model = models.ModuleType - fields = ['id', 'url', 'display_url', 'display', 'manufacturer', 'model'] - - -# -# Component templates -# - -class NestedConsolePortTemplateSerializer(WritableNestedSerializer): - - class Meta: - model = models.ConsolePortTemplate - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedConsoleServerPortTemplateSerializer(WritableNestedSerializer): - - class Meta: - model = models.ConsoleServerPortTemplate - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedPowerPortTemplateSerializer(WritableNestedSerializer): - - class Meta: - model = models.PowerPortTemplate - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedPowerOutletTemplateSerializer(WritableNestedSerializer): - - class Meta: - model = models.PowerOutletTemplate - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedRearPortTemplateSerializer(WritableNestedSerializer): - - class Meta: - model = models.RearPortTemplate - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedFrontPortTemplateSerializer(WritableNestedSerializer): - - class Meta: - model = models.FrontPortTemplate - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedModuleBayTemplateSerializer(WritableNestedSerializer): - - class Meta: - model = models.ModuleBayTemplate - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedDeviceBayTemplateSerializer(WritableNestedSerializer): - - class Meta: - model = models.DeviceBayTemplate - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedInventoryItemTemplateSerializer(WritableNestedSerializer): - _depth = serializers.IntegerField(source='level', read_only=True) - - class Meta: - model = models.InventoryItemTemplate - fields = ['id', 'url', 'display_url', 'display', 'name', '_depth'] - - -# -# Devices -# - -@extend_schema_serializer( - exclude_fields=('device_count', 'virtualmachine_count'), -) -class NestedDeviceRoleSerializer(WritableNestedSerializer): - device_count = RelatedObjectCountField('devices') - virtualmachine_count = RelatedObjectCountField('virtual_machines') - - class Meta: - model = models.DeviceRole - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'device_count', 'virtualmachine_count'] - - -@extend_schema_serializer( - exclude_fields=('device_count', 'virtualmachine_count'), -) -class NestedPlatformSerializer(WritableNestedSerializer): - device_count = RelatedObjectCountField('devices') - virtualmachine_count = RelatedObjectCountField('virtual_machines') - - class Meta: - model = models.Platform - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'device_count', 'virtualmachine_count'] - - -class ModuleNestedModuleBaySerializer(WritableNestedSerializer): - - class Meta: - model = models.ModuleBay - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedModuleSerializer(WritableNestedSerializer): - device = NestedDeviceSerializer(read_only=True) - module_bay = ModuleNestedModuleBaySerializer(read_only=True) - module_type = NestedModuleTypeSerializer(read_only=True) - - class Meta: - model = models.Module - fields = ['id', 'url', 'display_url', 'display', 'device', 'module_bay', 'module_type'] - - -class NestedConsoleServerPortSerializer(WritableNestedSerializer): - device = NestedDeviceSerializer(read_only=True) - _occupied = serializers.BooleanField(required=False, read_only=True) - - class Meta: - model = models.ConsoleServerPort - fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied'] - - -class NestedConsolePortSerializer(WritableNestedSerializer): - device = NestedDeviceSerializer(read_only=True) - _occupied = serializers.BooleanField(required=False, read_only=True) - - class Meta: - model = models.ConsolePort - fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied'] - - -class NestedPowerOutletSerializer(WritableNestedSerializer): - device = NestedDeviceSerializer(read_only=True) - _occupied = serializers.BooleanField(required=False, read_only=True) - - class Meta: - model = models.PowerOutlet - fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied'] - - -class NestedPowerPortSerializer(WritableNestedSerializer): - device = NestedDeviceSerializer(read_only=True) - _occupied = serializers.BooleanField(required=False, read_only=True) - - class Meta: - model = models.PowerPort - fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied'] - - -class NestedRearPortSerializer(WritableNestedSerializer): - device = NestedDeviceSerializer(read_only=True) - _occupied = serializers.BooleanField(required=False, read_only=True) - - class Meta: - model = models.RearPort - fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied'] - - -class NestedFrontPortSerializer(WritableNestedSerializer): - device = NestedDeviceSerializer(read_only=True) - _occupied = serializers.BooleanField(required=False, read_only=True) - - class Meta: - model = models.FrontPort - fields = ['id', 'url', 'display_url', 'display', 'device', 'name', 'cable', '_occupied'] - - -class NestedInventoryItemSerializer(WritableNestedSerializer): - device = NestedDeviceSerializer(read_only=True) - _depth = serializers.IntegerField(source='level', read_only=True) - - class Meta: - model = models.InventoryItem - fields = ['id', 'url', 'display_url', 'display', 'device', 'name', '_depth'] - - -@extend_schema_serializer( - exclude_fields=('inventoryitem_count',), -) -class NestedInventoryItemRoleSerializer(WritableNestedSerializer): - inventoryitem_count = RelatedObjectCountField('inventory_items') - - class Meta: - model = models.InventoryItemRole - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'inventoryitem_count'] - - -# -# Cables -# - -class NestedCableSerializer(WritableNestedSerializer): - - class Meta: - model = models.Cable - fields = ['id', 'url', 'display_url', 'display', 'label'] - - -# -# Virtual chassis -# - -@extend_schema_serializer( - exclude_fields=('member_count',), -) -class NestedVirtualChassisSerializer(WritableNestedSerializer): - master = NestedDeviceSerializer() - member_count = serializers.IntegerField(read_only=True) - - class Meta: - model = models.VirtualChassis - fields = ['id', 'url', 'display_url', 'display', 'name', 'master', 'member_count'] - - -# -# Power panels/feeds -# - -@extend_schema_serializer( - exclude_fields=('powerfeed_count',), -) -class NestedPowerPanelSerializer(WritableNestedSerializer): - powerfeed_count = RelatedObjectCountField('powerfeeds') - - class Meta: - model = models.PowerPanel - fields = ['id', 'url', 'display_url', 'display', 'name', 'powerfeed_count'] - - -class NestedPowerFeedSerializer(WritableNestedSerializer): - _occupied = serializers.BooleanField(required=False, read_only=True) - - class Meta: - model = models.PowerFeed - fields = ['id', 'url', 'display_url', 'display', 'name', 'cable', '_occupied'] - - -class NestedVirtualDeviceContextSerializer(WritableNestedSerializer): - device = NestedDeviceSerializer() - - class Meta: - model = models.VirtualDeviceContext - fields = ['id', 'url', 'display_url', 'display', 'name', 'identifier', 'device'] diff --git a/netbox/dcim/api/serializers_/device_components.py b/netbox/dcim/api/serializers_/device_components.py index e285ce349..a6767bb6f 100644 --- a/netbox/dcim/api/serializers_/device_components.py +++ b/netbox/dcim/api/serializers_/device_components.py @@ -8,7 +8,7 @@ from dcim.models import ( ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, InventoryItem, ModuleBay, PowerOutlet, PowerPort, RearPort, VirtualDeviceContext, ) -from ipam.api.serializers_.vlans import VLANSerializer +from ipam.api.serializers_.vlans import VLANSerializer, VLANTranslationPolicySerializer from ipam.api.serializers_.vrfs import VRFSerializer from ipam.models import VLAN from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField @@ -21,7 +21,7 @@ from wireless.choices import * from wireless.models import WirelessLAN from .base import ConnectedEndpointsSerializer from .cables import CabledObjectSerializer -from .devices import DeviceSerializer, ModuleSerializer, VirtualDeviceContextSerializer +from .devices import DeviceSerializer, MACAddressSerializer, ModuleSerializer, VirtualDeviceContextSerializer from .manufacturers import ManufacturerSerializer from .nested import NestedInterfaceSerializer from .roles import InventoryItemRoleSerializer @@ -196,6 +196,8 @@ class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect required=False, many=True ) + qinq_svlan = VLANSerializer(nested=True, required=False, allow_null=True) + vlan_translation_policy = VLANTranslationPolicySerializer(nested=True, required=False, allow_null=True) vrf = VRFSerializer(nested=True, required=False, allow_null=True) l2vpn_termination = L2VPNTerminationSerializer(nested=True, read_only=True, allow_null=True) wireless_link = NestedWirelessLinkSerializer(read_only=True, allow_null=True) @@ -208,22 +210,21 @@ class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect ) count_ipaddresses = serializers.IntegerField(read_only=True) count_fhrp_groups = serializers.IntegerField(read_only=True) - mac_address = serializers.CharField( - required=False, - default=None, - allow_blank=True, - allow_null=True - ) + # Maintains backward compatibility with NetBox [ge,xe]-0/0/[0-9]). The token {module}, if present, will be " + "automatically replaced with the position value when creating a new module." + ) + class ConsolePortTemplateForm(ModularComponentTemplateForm): fieldsets = ( @@ -991,7 +1010,8 @@ class InterfaceTemplateForm(ModularComponentTemplateForm): class Meta: model = InterfaceTemplate fields = [ - 'device_type', 'module_type', 'name', 'label', 'type', 'mgmt_only', 'enabled', 'description', 'poe_mode', 'poe_type', 'bridge', 'rf_role', + 'device_type', 'module_type', 'name', 'label', 'type', 'mgmt_only', 'enabled', 'description', 'poe_mode', + 'poe_type', 'bridge', 'rf_role', ] @@ -1173,7 +1193,10 @@ class InventoryItemTemplateForm(ComponentTemplateForm): break elif component_type and component_id: # When adding the InventoryItem from a component page - if content_type := ContentType.objects.filter(MODULAR_COMPONENT_TEMPLATE_MODELS).filter(pk=component_type).first(): + content_type = ContentType.objects.filter( + MODULAR_COMPONENT_TEMPLATE_MODELS + ).filter(pk=component_type).first() + if content_type: if component := content_type.model_class().objects.filter(pk=component_id).first(): initial[content_type.model] = component @@ -1285,16 +1308,16 @@ class PowerOutletForm(ModularDeviceComponentForm): fieldsets = ( FieldSet( - 'device', 'module', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'mark_connected', 'description', - 'tags', + 'device', 'module', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'mark_connected', + 'description', 'tags', ), ) class Meta: model = PowerOutlet fields = [ - 'device', 'module', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'mark_connected', 'description', - 'tags', + 'device', 'module', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'mark_connected', + 'description', 'tags', ] @@ -1372,6 +1395,16 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm): 'available_on_device': '$device', } ) + qinq_svlan = DynamicModelChoiceField( + queryset=VLAN.objects.all(), + required=False, + label=_('Q-in-Q Service VLAN'), + query_params={ + 'group_id': '$vlan_group', + 'available_on_device': '$device', + 'qinq_role': VLANQinQRoleChoices.ROLE_SERVICE, + } + ) vrf = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, @@ -1382,16 +1415,24 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm): required=False, label=_('WWN') ) + vlan_translation_policy = DynamicModelChoiceField( + queryset=VLANTranslationPolicy.objects.all(), + required=False, + label=_('VLAN Translation Policy') + ) fieldsets = ( FieldSet( 'device', 'module', 'name', 'label', 'type', 'speed', 'duplex', 'description', 'tags', name=_('Interface') ), - FieldSet('vrf', 'mac_address', 'wwn', name=_('Addressing')), + FieldSet('vrf', 'primary_mac_address', 'wwn', name=_('Addressing')), FieldSet('vdcs', 'mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected', name=_('Operation')), FieldSet('parent', 'bridge', 'lag', name=_('Related Interfaces')), FieldSet('poe_mode', 'poe_type', name=_('PoE')), - FieldSet('mode', 'vlan_group', 'untagged_vlan', 'tagged_vlans', name=_('802.1Q Switching')), + FieldSet( + 'mode', 'vlan_group', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', + name=_('802.1Q Switching') + ), FieldSet( 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'wireless_lan_group', 'wireless_lans', name=_('Wireless') @@ -1401,10 +1442,11 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm): class Meta: model = Interface fields = [ - 'device', 'module', 'vdcs', 'name', 'label', 'type', 'speed', 'duplex', 'enabled', 'parent', 'bridge', 'lag', - 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'poe_mode', 'poe_type', 'mode', + 'device', 'module', 'vdcs', 'name', 'label', 'type', 'speed', 'duplex', 'enabled', 'parent', 'bridge', + 'lag', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'poe_mode', 'poe_type', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'wireless_lans', - 'untagged_vlan', 'tagged_vlans', 'vrf', 'tags', + 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'primary_mac_address', + 'tags', ] widgets = { 'speed': NumberWithOptions( @@ -1576,7 +1618,10 @@ class InventoryItemForm(DeviceComponentForm): ) fieldsets = ( - FieldSet('device', 'parent', 'name', 'label', 'status', 'role', 'description', 'tags', name=_('Inventory Item')), + FieldSet( + 'device', 'parent', 'name', 'label', 'status', 'role', 'description', 'tags', + name=_('Inventory Item') + ), FieldSet('manufacturer', 'part_id', 'serial', 'asset_tag', name=_('Hardware')), FieldSet( TabbedGroups( @@ -1698,3 +1743,78 @@ class VirtualDeviceContextForm(TenancyForm, NetBoxModelForm): 'device', 'name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'comments', 'tags' ] + + +# +# Addressing +# + +class MACAddressForm(NetBoxModelForm): + mac_address = forms.CharField( + required=True, + label=_('MAC address') + ) + interface = DynamicModelChoiceField( + label=_('Interface'), + queryset=Interface.objects.all(), + required=False, + context={ + 'parent': 'device', + }, + ) + vminterface = DynamicModelChoiceField( + label=_('VM Interface'), + queryset=VMInterface.objects.all(), + required=False, + context={ + 'parent': 'virtual_machine', + }, + ) + + fieldsets = ( + FieldSet( + 'mac_address', 'description', 'tags', + ), + FieldSet( + TabbedGroups( + FieldSet('interface', name=_('Device')), + FieldSet('vminterface', name=_('Virtual Machine')), + ), + ), + ) + + class Meta: + model = MACAddress + fields = [ + 'mac_address', 'interface', 'vminterface', 'description', 'tags', + ] + + def __init__(self, *args, **kwargs): + + # Initialize helper selectors + instance = kwargs.get('instance') + initial = kwargs.get('initial', {}).copy() + if instance: + if type(instance.assigned_object) is Interface: + initial['interface'] = instance.assigned_object + elif type(instance.assigned_object) is VMInterface: + initial['vminterface'] = instance.assigned_object + kwargs['initial'] = initial + + super().__init__(*args, **kwargs) + + def clean(self): + super().clean() + + # Handle object assignment + selected_objects = [ + field for field in ('interface', 'vminterface') if self.cleaned_data[field] + ] + if len(selected_objects) > 1: + raise forms.ValidationError({ + selected_objects[1]: _("A MAC address can only be assigned to a single object.") + }) + elif selected_objects: + self.instance.assigned_object = self.cleaned_data[selected_objects[0]] + else: + self.instance.assigned_object = None diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index d18c7ed14..6f6cd8f7c 100644 --- a/netbox/dcim/forms/object_create.py +++ b/netbox/dcim/forms/object_create.py @@ -243,14 +243,6 @@ class InterfaceCreateForm(ComponentCreateForm, model_forms.InterfaceForm): class Meta(model_forms.InterfaceForm.Meta): exclude = ('name', 'label') - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - if 'module' in self.fields: - self.fields['name'].help_text += _( - "The string {module} will be replaced with the position of the assigned module, if any." - ) - class FrontPortCreateForm(ComponentCreateForm, model_forms.FrontPortForm): device = DynamicModelChoiceField( @@ -424,7 +416,8 @@ class VirtualChassisCreateForm(NetBoxModelForm): class Meta: model = VirtualChassis fields = [ - 'name', 'domain', 'description', 'region', 'site_group', 'site', 'rack', 'members', 'initial_position', 'tags', + 'name', 'domain', 'description', 'region', 'site_group', 'site', 'rack', 'members', 'initial_position', + 'tags', ] def clean(self): diff --git a/netbox/dcim/forms/object_import.py b/netbox/dcim/forms/object_import.py index d46ef83ad..821f91402 100644 --- a/netbox/dcim/forms/object_import.py +++ b/netbox/dcim/forms/object_import.py @@ -136,7 +136,8 @@ class FrontPortTemplateImportForm(forms.ModelForm): class Meta: model = FrontPortTemplate fields = [ - 'device_type', 'module_type', 'name', 'type', 'color', 'rear_port', 'rear_port_position', 'label', 'description', + 'device_type', 'module_type', 'name', 'type', 'color', 'rear_port', 'rear_port_position', 'label', + 'description', ] diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py index 8c256aecb..94f2c6d38 100644 --- a/netbox/dcim/graphql/filters.py +++ b/netbox/dcim/graphql/filters.py @@ -23,6 +23,7 @@ __all__ = ( 'InventoryItemFilter', 'InventoryItemRoleFilter', 'LocationFilter', + 'MACAddressFilter', 'ManufacturerFilter', 'ModuleFilter', 'ModuleBayFilter', @@ -133,6 +134,12 @@ class FrontPortTemplateFilter(BaseFilterMixin): pass +@strawberry_django.filter(models.MACAddress, lookups=True) +@autotype_decorator(filtersets.MACAddressFilterSet) +class MACAddressFilter(BaseFilterMixin): + pass + + @strawberry_django.filter(models.Interface, lookups=True) @autotype_decorator(filtersets.InterfaceFilterSet) class InterfaceFilter(BaseFilterMixin): diff --git a/netbox/dcim/graphql/schema.py b/netbox/dcim/graphql/schema.py index 65818fb20..011a2b58b 100644 --- a/netbox/dcim/graphql/schema.py +++ b/netbox/dcim/graphql/schema.py @@ -44,6 +44,9 @@ class DCIMQuery: front_port_template: FrontPortTemplateType = strawberry_django.field() front_port_template_list: List[FrontPortTemplateType] = strawberry_django.field() + mac_address: MACAddressType = strawberry_django.field() + mac_address_list: List[MACAddressType] = strawberry_django.field() + interface: InterfaceType = strawberry_django.field() interface_list: List[InterfaceType] = strawberry_django.field() diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index b951aead0..1020861f4 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -34,6 +34,7 @@ __all__ = ( 'InventoryItemRoleType', 'InventoryItemTemplateType', 'LocationType', + 'MACAddressType', 'ManufacturerType', 'ModularComponentType', 'ModuleType', @@ -76,7 +77,6 @@ class ComponentType( """ Base type for device/VM components """ - _name: str device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] @@ -93,7 +93,6 @@ class ComponentTemplateType( """ Base type for device/VM components """ - _name: str device_type: Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')] @@ -112,7 +111,7 @@ class ModularComponentTemplateType(ComponentTemplateType): @strawberry_django.type( models.CableTermination, - exclude=('termination_type', 'termination_id'), + exclude=('termination_type', 'termination_id', '_device', '_rack', '_location', '_site'), filters=CableTerminationFilter ) class CableTerminationType(NetBoxObjectType): @@ -181,7 +180,7 @@ class ConsolePortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin filters=ConsolePortTemplateFilter ) class ConsolePortTemplateType(ModularComponentTemplateType): - _name: str + pass @strawberry_django.type( @@ -199,7 +198,7 @@ class ConsoleServerPortType(ModularComponentType, CabledObjectMixin, PathEndpoin filters=ConsoleServerPortTemplateFilter ) class ConsoleServerPortTemplateType(ModularComponentTemplateType): - _name: str + pass @strawberry_django.type( @@ -208,7 +207,6 @@ class ConsoleServerPortTemplateType(ModularComponentTemplateType): filters=DeviceFilter ) class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): - _name: str console_port_count: BigInt console_server_port_count: BigInt power_port_count: BigInt @@ -243,6 +241,7 @@ class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, NetBo consoleserverports: List[Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')]] poweroutlets: List[Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')]] frontports: List[Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')]] + devicebays: List[Annotated["DeviceBayType", strawberry.lazy('dcim.graphql.types')]] modulebays: List[Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')]] services: List[Annotated["ServiceType", strawberry.lazy('ipam.graphql.types')]] inventoryitems: List[Annotated["InventoryItemType", strawberry.lazy('dcim.graphql.types')]] @@ -272,7 +271,7 @@ class DeviceBayType(ComponentType): filters=DeviceBayTemplateFilter ) class DeviceBayTemplateType(ComponentTemplateType): - _name: str + pass @strawberry_django.type( @@ -281,7 +280,6 @@ class DeviceBayTemplateType(ComponentTemplateType): filters=InventoryItemTemplateFilter ) class InventoryItemTemplateType(ComponentTemplateType): - _name: str role: Annotated["InventoryItemRoleType", strawberry.lazy('dcim.graphql.types')] | None manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] @@ -365,18 +363,33 @@ class FrontPortType(ModularComponentType, CabledObjectMixin): filters=FrontPortTemplateFilter ) class FrontPortTemplateType(ModularComponentTemplateType): - _name: str color: str rear_port: Annotated["RearPortTemplateType", strawberry.lazy('dcim.graphql.types')] +@strawberry_django.type( + models.MACAddress, + exclude=('assigned_object_type', 'assigned_object_id'), + filters=MACAddressFilter +) +class MACAddressType(NetBoxObjectType): + mac_address: str + + @strawberry_django.field + def assigned_object(self) -> Annotated[Union[ + Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')], + Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')], + ], strawberry.union("MACAddressAssignmentType")] | None: + return self.assigned_object + + @strawberry_django.type( models.Interface, exclude=('_path',), filters=InterfaceFilter ) class InterfaceType(IPAddressesMixin, ModularComponentType, CabledObjectMixin, PathEndpointMixin): - mac_address: str | None + _name: str wwn: str | None parent: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')] | None bridge: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')] | None @@ -384,6 +397,9 @@ class InterfaceType(IPAddressesMixin, ModularComponentType, CabledObjectMixin, P wireless_link: Annotated["WirelessLinkType", strawberry.lazy('wireless.graphql.types')] | None untagged_vlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None + primary_mac_address: Annotated["MACAddressType", strawberry.lazy('dcim.graphql.types')] | None + qinq_svlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None + vlan_translation_policy: Annotated["VLANTranslationPolicyType", strawberry.lazy('ipam.graphql.types')] | None vdcs: List[Annotated["VirtualDeviceContextType", strawberry.lazy('dcim.graphql.types')]] tagged_vlans: List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]] @@ -391,6 +407,7 @@ class InterfaceType(IPAddressesMixin, ModularComponentType, CabledObjectMixin, P wireless_lans: List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]] member_interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]] child_interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]] + mac_addresses: List[Annotated["MACAddressType", strawberry.lazy('dcim.graphql.types')]] @strawberry_django.type( @@ -460,6 +477,16 @@ class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, Organi devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]] children: List[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]] + @strawberry_django.field + def clusters(self) -> List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]: + return self.cluster_set.all() + + @strawberry_django.field + def circuit_terminations(self) -> List[ + Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')] + ]: + return self.circuit_terminations.all() + @strawberry_django.type( models.Manufacturer, @@ -516,7 +543,7 @@ class ModuleBayType(ModularComponentType): filters=ModuleBayTemplateFilter ) class ModuleBayTemplateType(ModularComponentTemplateType): - _name: str + pass @strawberry_django.type( @@ -577,7 +604,6 @@ class PowerOutletType(ModularComponentType, CabledObjectMixin, PathEndpointMixin filters=PowerOutletTemplateFilter ) class PowerOutletTemplateType(ModularComponentTemplateType): - _name: str power_port: Annotated["PowerPortTemplateType", strawberry.lazy('dcim.graphql.types')] | None @@ -609,8 +635,6 @@ class PowerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): filters=PowerPortTemplateFilter ) class PowerPortTemplateType(ModularComponentTemplateType): - _name: str - poweroutlet_templates: List[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]] @@ -629,7 +653,6 @@ class RackTypeType(NetBoxObjectType): filters=RackFilter ) class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): - _name: str site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] location: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -682,7 +705,6 @@ class RearPortType(ModularComponentType, CabledObjectMixin): filters=RearPortTemplateFilter ) class RearPortTemplateType(ModularComponentTemplateType): - _name: str color: str frontport_templates: List[Annotated["FrontPortTemplateType", strawberry.lazy('dcim.graphql.types')]] @@ -703,6 +725,16 @@ class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): def parent(self) -> Annotated["RegionType", strawberry.lazy('dcim.graphql.types')] | None: return self.parent + @strawberry_django.field + def clusters(self) -> List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]: + return self.cluster_set.all() + + @strawberry_django.field + def circuit_terminations(self) -> List[ + Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')] + ]: + return self.circuit_terminations.all() + @strawberry_django.type( models.Site, @@ -710,7 +742,6 @@ class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): filters=SiteFilter ) class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): - _name: str time_zone: str | None region: Annotated["RegionType", strawberry.lazy('dcim.graphql.types')] | None group: Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')] | None @@ -728,6 +759,16 @@ class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObje clusters: List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]] vlans: List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]] + @strawberry_django.field + def clusters(self) -> List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]: + return self.cluster_set.all() + + @strawberry_django.field + def circuit_terminations(self) -> List[ + Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')] + ]: + return self.circuit_terminations.all() + @strawberry_django.type( models.SiteGroup, @@ -744,6 +785,16 @@ class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): def parent(self) -> Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')] | None: return self.parent + @strawberry_django.field + def clusters(self) -> List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]: + return self.cluster_set.all() + + @strawberry_django.field + def circuit_terminations(self) -> List[ + Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')] + ]: + return self.circuit_terminations.all() + @strawberry_django.type( models.VirtualChassis, diff --git a/netbox/dcim/management/commands/buildschema.py b/netbox/dcim/management/commands/buildschema.py index 44a0e95f2..529a2462c 100644 --- a/netbox/dcim/management/commands/buildschema.py +++ b/netbox/dcim/management/commands/buildschema.py @@ -6,6 +6,7 @@ from django.core.management.base import BaseCommand from jinja2 import FileSystemLoader, Environment from dcim.choices import * +from netbox.choices import WeightUnitChoices TEMPLATE_FILENAME = 'devicetype_schema.jinja2' OUTPUT_FILENAME = 'contrib/generated_schema.json' diff --git a/netbox/dcim/migrations/0001_squashed.py b/netbox/dcim/migrations/0001_squashed.py index cf0ef4816..f08fe1d70 100644 --- a/netbox/dcim/migrations/0001_squashed.py +++ b/netbox/dcim/migrations/0001_squashed.py @@ -13,11 +13,9 @@ import utilities.validators class Migration(migrations.Migration): - initial = True - dependencies = [ - ] + dependencies = [] replaces = [ ('dcim', '0001_initial'), @@ -64,7 +62,12 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)), @@ -83,7 +86,12 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('type', models.CharField(blank=True, max_length=50)), @@ -100,7 +108,12 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)), @@ -119,7 +132,12 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('type', models.CharField(blank=True, max_length=50)), @@ -137,14 +155,34 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(primary_key=True, serialize=False)), ('local_context_data', models.JSONField(blank=True, null=True)), ('name', models.CharField(blank=True, max_length=64, null=True)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize, null=True)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize, null=True + ), + ), ('serial', models.CharField(blank=True, max_length=50)), ('asset_tag', models.CharField(blank=True, max_length=50, null=True, unique=True)), - ('position', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])), + ( + 'position', + models.PositiveSmallIntegerField( + blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)] + ), + ), ('face', models.CharField(blank=True, max_length=50)), ('status', models.CharField(default='active', max_length=50)), - ('vc_position', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)])), - ('vc_priority', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)])), + ( + 'vc_position', + models.PositiveSmallIntegerField( + blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)] + ), + ), + ( + 'vc_priority', + models.PositiveSmallIntegerField( + blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)] + ), + ), ('comments', models.TextField(blank=True)), ], options={ @@ -159,7 +197,12 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ], @@ -174,7 +217,12 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ], @@ -228,13 +276,27 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)), ('mark_connected', models.BooleanField(default=False)), ('type', models.CharField(max_length=50)), - ('rear_port_position', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])), + ( + 'rear_port_position', + models.PositiveSmallIntegerField( + default=1, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(1024), + ], + ), + ), ], options={ 'ordering': ('device', '_name'), @@ -247,11 +309,25 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('type', models.CharField(max_length=50)), - ('rear_port_position', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])), + ( + 'rear_port_position', + models.PositiveSmallIntegerField( + default=1, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(1024), + ], + ), + ), ], options={ 'ordering': ('device_type', '_name'), @@ -271,9 +347,24 @@ class Migration(migrations.Migration): ('mark_connected', models.BooleanField(default=False)), ('enabled', models.BooleanField(default=True)), ('mac_address', dcim.fields.MACAddressField(blank=True, null=True)), - ('mtu', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(65536)])), + ( + 'mtu', + models.PositiveIntegerField( + blank=True, + null=True, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(65536), + ], + ), + ), ('mode', models.CharField(blank=True, max_length=50)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface + ), + ), ('type', models.CharField(max_length=50)), ('mgmt_only', models.BooleanField(default=False)), ], @@ -290,7 +381,12 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=64)), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface + ), + ), ('type', models.CharField(max_length=50)), ('mgmt_only', models.BooleanField(default=False)), ], @@ -306,7 +402,12 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('part_id', models.CharField(blank=True, max_length=50)), @@ -388,8 +489,19 @@ class Migration(migrations.Migration): ('supply', models.CharField(default='ac', max_length=50)), ('phase', models.CharField(default='single-phase', max_length=50)), ('voltage', models.SmallIntegerField(validators=[utilities.validators.ExclusionValidator([0])])), - ('amperage', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1)])), - ('max_utilization', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])), + ( + 'amperage', + models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1)]), + ), + ( + 'max_utilization', + models.PositiveSmallIntegerField( + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(100), + ] + ), + ), ('available_power', models.PositiveIntegerField(default=0, editable=False)), ('comments', models.TextField(blank=True)), ], @@ -405,7 +517,12 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)), @@ -424,7 +541,12 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('type', models.CharField(blank=True, max_length=50)), @@ -455,14 +577,29 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)), ('mark_connected', models.BooleanField(default=False)), ('type', models.CharField(blank=True, max_length=50)), - ('maximum_draw', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])), - ('allocated_draw', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])), + ( + 'maximum_draw', + models.PositiveSmallIntegerField( + blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)] + ), + ), + ( + 'allocated_draw', + models.PositiveSmallIntegerField( + blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)] + ), + ), ], options={ 'ordering': ('device', '_name'), @@ -475,12 +612,27 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('type', models.CharField(blank=True, max_length=50)), - ('maximum_draw', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])), - ('allocated_draw', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])), + ( + 'maximum_draw', + models.PositiveSmallIntegerField( + blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)] + ), + ), + ( + 'allocated_draw', + models.PositiveSmallIntegerField( + blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)] + ), + ), ], options={ 'ordering': ('device_type', '_name'), @@ -494,14 +646,28 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=100)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('facility_id', models.CharField(blank=True, max_length=50, null=True)), ('status', models.CharField(default='active', max_length=50)), ('serial', models.CharField(blank=True, max_length=50)), ('asset_tag', models.CharField(blank=True, max_length=50, null=True, unique=True)), ('type', models.CharField(blank=True, max_length=50)), ('width', models.PositiveSmallIntegerField(default=19)), - ('u_height', models.PositiveSmallIntegerField(default=42, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])), + ( + 'u_height', + models.PositiveSmallIntegerField( + default=42, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(100), + ], + ), + ), ('desc_units', models.BooleanField(default=False)), ('outer_width', models.PositiveSmallIntegerField(blank=True, null=True)), ('outer_depth', models.PositiveSmallIntegerField(blank=True, null=True)), @@ -519,7 +685,10 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), - ('units', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(), size=None)), + ( + 'units', + django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(), size=None), + ), ('description', models.CharField(max_length=200)), ], options={ @@ -550,13 +719,27 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)), ('mark_connected', models.BooleanField(default=False)), ('type', models.CharField(max_length=50)), - ('positions', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])), + ( + 'positions', + models.PositiveSmallIntegerField( + default=1, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(1024), + ], + ), + ), ], options={ 'ordering': ('device', '_name'), @@ -569,11 +752,25 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('type', models.CharField(max_length=50)), - ('positions', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])), + ( + 'positions', + models.PositiveSmallIntegerField( + default=1, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(1024), + ], + ), + ), ], options={ 'ordering': ('device_type', '_name'), @@ -606,7 +803,12 @@ class Migration(migrations.Migration): ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=100, unique=True)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('slug', models.SlugField(max_length=100, unique=True)), ('status', models.CharField(default='active', max_length=50)), ('facility', models.CharField(blank=True, max_length=50)), @@ -654,7 +856,16 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), ('domain', models.CharField(blank=True, max_length=30)), - ('master', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vc_master_for', to='dcim.device')), + ( + 'master', + models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='vc_master_for', + to='dcim.device', + ), + ), ], options={ 'verbose_name_plural': 'virtual chassis', diff --git a/netbox/dcim/migrations/0002_squashed.py b/netbox/dcim/migrations/0002_squashed.py index 786167680..2e830560f 100644 --- a/netbox/dcim/migrations/0002_squashed.py +++ b/netbox/dcim/migrations/0002_squashed.py @@ -6,7 +6,6 @@ import taggit.managers class Migration(migrations.Migration): - dependencies = [ ('dcim', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), @@ -28,17 +27,35 @@ class Migration(migrations.Migration): migrations.AddField( model_name='sitegroup', name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.sitegroup'), + field=mptt.fields.TreeForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='children', + to='dcim.sitegroup', + ), ), migrations.AddField( model_name='site', name='group', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sites', to='dcim.sitegroup'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='sites', + to='dcim.sitegroup', + ), ), migrations.AddField( model_name='site', name='region', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sites', to='dcim.region'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='sites', + to='dcim.region', + ), ), migrations.AddField( model_name='site', @@ -48,32 +65,56 @@ class Migration(migrations.Migration): migrations.AddField( model_name='site', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sites', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='sites', + to='tenancy.tenant', + ), ), migrations.AddField( model_name='region', name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.region'), + field=mptt.fields.TreeForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='children', + to='dcim.region', + ), ), migrations.AddField( model_name='rearporttemplate', name='device_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype' + ), ), migrations.AddField( model_name='rearport', name='_cable_peer_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='rearport', name='cable', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable' + ), ), migrations.AddField( model_name='rearport', name='device', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device' + ), ), migrations.AddField( model_name='rearport', @@ -83,7 +124,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name='rackreservation', name='rack', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to='dcim.rack'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to='dcim.rack' + ), ), migrations.AddField( model_name='rackreservation', @@ -93,7 +136,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='rackreservation', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='rackreservations', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='rackreservations', + to='tenancy.tenant', + ), ), migrations.AddField( model_name='rackreservation', @@ -103,12 +152,24 @@ class Migration(migrations.Migration): migrations.AddField( model_name='rack', name='location', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='racks', to='dcim.location'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='racks', + to='dcim.location', + ), ), migrations.AddField( model_name='rack', name='role', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='dcim.rackrole'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='racks', + to='dcim.rackrole', + ), ), migrations.AddField( model_name='rack', @@ -123,32 +184,52 @@ class Migration(migrations.Migration): migrations.AddField( model_name='rack', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='racks', + to='tenancy.tenant', + ), ), migrations.AddField( model_name='powerporttemplate', name='device_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype' + ), ), migrations.AddField( model_name='powerport', name='_cable_peer_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='powerport', name='_path', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath' + ), ), migrations.AddField( model_name='powerport', name='cable', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable' + ), ), migrations.AddField( model_name='powerport', name='device', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device' + ), ), migrations.AddField( model_name='powerport', @@ -158,7 +239,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name='powerpanel', name='location', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.location'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.location' + ), ), migrations.AddField( model_name='powerpanel', @@ -173,37 +256,63 @@ class Migration(migrations.Migration): migrations.AddField( model_name='poweroutlettemplate', name='device_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype' + ), ), migrations.AddField( model_name='poweroutlettemplate', name='power_port', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlet_templates', to='dcim.powerporttemplate'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='poweroutlet_templates', + to='dcim.powerporttemplate', + ), ), migrations.AddField( model_name='poweroutlet', name='_cable_peer_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='poweroutlet', name='_path', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath' + ), ), migrations.AddField( model_name='poweroutlet', name='cable', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable' + ), ), migrations.AddField( model_name='poweroutlet', name='device', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device' + ), ), migrations.AddField( model_name='poweroutlet', name='power_port', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlets', to='dcim.powerport'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='poweroutlets', + to='dcim.powerport', + ), ), migrations.AddField( model_name='poweroutlet', @@ -213,27 +322,45 @@ class Migration(migrations.Migration): migrations.AddField( model_name='powerfeed', name='_cable_peer_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='powerfeed', name='_path', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath' + ), ), migrations.AddField( model_name='powerfeed', name='cable', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable' + ), ), migrations.AddField( model_name='powerfeed', name='power_panel', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='powerfeeds', to='dcim.powerpanel'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='powerfeeds', to='dcim.powerpanel' + ), ), migrations.AddField( model_name='powerfeed', name='rack', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='powerfeeds', to='dcim.rack'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='powerfeeds', + to='dcim.rack', + ), ), migrations.AddField( model_name='powerfeed', @@ -243,32 +370,60 @@ class Migration(migrations.Migration): migrations.AddField( model_name='platform', name='manufacturer', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='platforms', to='dcim.manufacturer'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='platforms', + to='dcim.manufacturer', + ), ), migrations.AddField( model_name='location', name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.location'), + field=mptt.fields.TreeForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='children', + to='dcim.location', + ), ), migrations.AddField( model_name='location', name='site', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations', to='dcim.site'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='locations', to='dcim.site' + ), ), migrations.AddField( model_name='inventoryitem', name='device', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device' + ), ), migrations.AddField( model_name='inventoryitem', name='manufacturer', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_items', to='dcim.manufacturer'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='inventory_items', + to='dcim.manufacturer', + ), ), migrations.AddField( model_name='inventoryitem', name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='child_items', to='dcim.inventoryitem'), + field=mptt.fields.TreeForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='child_items', + to='dcim.inventoryitem', + ), ), migrations.AddField( model_name='inventoryitem', @@ -278,36 +433,62 @@ class Migration(migrations.Migration): migrations.AddField( model_name='interfacetemplate', name='device_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype' + ), ), migrations.AddField( model_name='interface', name='_cable_peer_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='interface', name='_path', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath' + ), ), migrations.AddField( model_name='interface', name='cable', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable' + ), ), migrations.AddField( model_name='interface', name='device', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device' + ), ), migrations.AddField( model_name='interface', name='lag', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_interfaces', to='dcim.interface'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='member_interfaces', + to='dcim.interface', + ), ), migrations.AddField( model_name='interface', name='parent', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='child_interfaces', to='dcim.interface'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='child_interfaces', + to='dcim.interface', + ), ), ] diff --git a/netbox/dcim/migrations/0003_squashed_0130.py b/netbox/dcim/migrations/0003_squashed_0130.py index 592aaf9a8..0248d9ba1 100644 --- a/netbox/dcim/migrations/0003_squashed_0130.py +++ b/netbox/dcim/migrations/0003_squashed_0130.py @@ -4,7 +4,6 @@ import taggit.managers class Migration(migrations.Migration): - dependencies = [ ('dcim', '0002_auto_20160622_1821'), ('virtualization', '0001_virtualization'), @@ -160,37 +159,61 @@ class Migration(migrations.Migration): migrations.AddField( model_name='interface', name='untagged_vlan', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interfaces_as_untagged', to='ipam.vlan'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='interfaces_as_untagged', + to='ipam.vlan', + ), ), migrations.AddField( model_name='frontporttemplate', name='device_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype' + ), ), migrations.AddField( model_name='frontporttemplate', name='rear_port', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontport_templates', to='dcim.rearporttemplate'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='frontport_templates', + to='dcim.rearporttemplate', + ), ), migrations.AddField( model_name='frontport', name='_cable_peer_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='frontport', name='cable', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable' + ), ), migrations.AddField( model_name='frontport', name='device', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device' + ), ), migrations.AddField( model_name='frontport', name='rear_port', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontports', to='dcim.rearport'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='frontports', to='dcim.rearport' + ), ), migrations.AddField( model_name='frontport', @@ -200,7 +223,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name='devicetype', name='manufacturer', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='device_types', to='dcim.manufacturer'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='device_types', to='dcim.manufacturer' + ), ), migrations.AddField( model_name='devicetype', @@ -210,17 +235,27 @@ class Migration(migrations.Migration): migrations.AddField( model_name='devicebaytemplate', name='device_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype' + ), ), migrations.AddField( model_name='devicebay', name='device', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device' + ), ), migrations.AddField( model_name='devicebay', name='installed_device', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parent_bay', to='dcim.device'), + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='parent_bay', + to='dcim.device', + ), ), migrations.AddField( model_name='devicebay', @@ -230,47 +265,89 @@ class Migration(migrations.Migration): migrations.AddField( model_name='device', name='cluster', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='devices', to='virtualization.cluster'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='devices', + to='virtualization.cluster', + ), ), migrations.AddField( model_name='device', name='device_role', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.devicerole'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.devicerole' + ), ), migrations.AddField( model_name='device', name='device_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='instances', to='dcim.devicetype'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='instances', to='dcim.devicetype' + ), ), migrations.AddField( model_name='device', name='location', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.location'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='devices', + to='dcim.location', + ), ), migrations.AddField( model_name='device', name='platform', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='devices', to='dcim.platform'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='devices', + to='dcim.platform', + ), ), migrations.AddField( model_name='device', name='primary_ip4', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip4_for', to='ipam.ipaddress'), + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='primary_ip4_for', + to='ipam.ipaddress', + ), ), migrations.AddField( model_name='device', name='primary_ip6', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip6_for', to='ipam.ipaddress'), + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='primary_ip6_for', + to='ipam.ipaddress', + ), ), migrations.AddField( model_name='device', name='rack', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.rack'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='devices', + to='dcim.rack', + ), ), migrations.AddField( model_name='device', name='site', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.site'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.site' + ), ), migrations.AddField( model_name='device', @@ -280,37 +357,63 @@ class Migration(migrations.Migration): migrations.AddField( model_name='device', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='devices', + to='tenancy.tenant', + ), ), migrations.AddField( model_name='device', name='virtual_chassis', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='dcim.virtualchassis'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='members', + to='dcim.virtualchassis', + ), ), migrations.AddField( model_name='consoleserverporttemplate', name='device_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype' + ), ), migrations.AddField( model_name='consoleserverport', name='_cable_peer_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='consoleserverport', name='_path', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath' + ), ), migrations.AddField( model_name='consoleserverport', name='cable', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable' + ), ), migrations.AddField( model_name='consoleserverport', name='device', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device' + ), ), migrations.AddField( model_name='consoleserverport', @@ -320,27 +423,41 @@ class Migration(migrations.Migration): migrations.AddField( model_name='consoleporttemplate', name='device_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype' + ), ), migrations.AddField( model_name='consoleport', name='_cable_peer_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='consoleport', name='_path', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath' + ), ), migrations.AddField( model_name='consoleport', name='cable', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable' + ), ), migrations.AddField( model_name='consoleport', name='device', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device' + ), ), migrations.AddField( model_name='consoleport', @@ -350,22 +467,34 @@ class Migration(migrations.Migration): migrations.AddField( model_name='cablepath', name='destination_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='cablepath', name='origin_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype' + ), ), migrations.AddField( model_name='cable', name='_termination_a_device', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.device'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.device' + ), ), migrations.AddField( model_name='cable', name='_termination_b_device', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.device'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.device' + ), ), migrations.AddField( model_name='cable', @@ -375,12 +504,64 @@ class Migration(migrations.Migration): migrations.AddField( model_name='cable', name='termination_a_type', - field=models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + limit_choices_to=models.Q( + models.Q( + models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), + models.Q( + ('app_label', 'dcim'), + ( + 'model__in', + ( + 'consoleport', + 'consoleserverport', + 'frontport', + 'interface', + 'powerfeed', + 'poweroutlet', + 'powerport', + 'rearport', + ), + ), + ), + _connector='OR', + ) + ), + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='cable', name='termination_b_type', - field=models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + limit_choices_to=models.Q( + models.Q( + models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), + models.Q( + ('app_label', 'dcim'), + ( + 'model__in', + ( + 'consoleport', + 'consoleserverport', + 'frontport', + 'interface', + 'powerfeed', + 'poweroutlet', + 'powerport', + 'rearport', + ), + ), + ), + _connector='OR', + ) + ), + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AlterUniqueTogether( name='rearporttemplate', @@ -456,7 +637,11 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='device', - unique_together={('rack', 'position', 'face'), ('virtual_chassis', 'vc_position'), ('site', 'tenant', 'name')}, + unique_together={ + ('rack', 'position', 'face'), + ('virtual_chassis', 'vc_position'), + ('site', 'tenant', 'name'), + }, ), migrations.AlterUniqueTogether( name='consoleserverporttemplate', diff --git a/netbox/dcim/migrations/0131_squashed_0159.py b/netbox/dcim/migrations/0131_squashed_0159.py index f7e7cfdb2..3866e8cc8 100644 --- a/netbox/dcim/migrations/0131_squashed_0159.py +++ b/netbox/dcim/migrations/0131_squashed_0159.py @@ -10,7 +10,6 @@ import utilities.ordering class Migration(migrations.Migration): - replaces = [ ('dcim', '0131_consoleport_speed'), ('dcim', '0132_cable_length'), @@ -40,7 +39,7 @@ class Migration(migrations.Migration): ('dcim', '0156_location_status'), ('dcim', '0157_new_cabling_models'), ('dcim', '0158_populate_cable_terminations'), - ('dcim', '0159_populate_cable_paths') + ('dcim', '0159_populate_cable_paths'), ] dependencies = [ @@ -96,17 +95,35 @@ class Migration(migrations.Migration): migrations.AddField( model_name='interface', name='bridge', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bridge_interfaces', to='dcim.interface'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='bridge_interfaces', + to='dcim.interface', + ), ), migrations.AddField( model_name='location', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='locations', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='locations', + to='tenancy.tenant', + ), ), migrations.AddField( model_name='cable', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='cables', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='cables', + to='tenancy.tenant', + ), ), migrations.AddField( model_name='devicetype', @@ -148,7 +165,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='location', - constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('site', 'name'), name='dcim_location_name'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent', None)), fields=('site', 'name'), name='dcim_location_name' + ), ), migrations.AddConstraint( model_name='location', @@ -156,7 +175,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='location', - constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('site', 'slug'), name='dcim_location_slug'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent', None)), fields=('site', 'slug'), name='dcim_location_slug' + ), ), migrations.AddConstraint( model_name='region', @@ -164,7 +185,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='region', - constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('name',), name='dcim_region_name'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent', None)), fields=('name',), name='dcim_region_name' + ), ), migrations.AddConstraint( model_name='region', @@ -172,7 +195,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='region', - constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('slug',), name='dcim_region_slug'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent', None)), fields=('slug',), name='dcim_region_slug' + ), ), migrations.AddConstraint( model_name='sitegroup', @@ -180,7 +205,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='sitegroup', - constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('name',), name='dcim_sitegroup_name'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent', None)), fields=('name',), name='dcim_sitegroup_name' + ), ), migrations.AddConstraint( model_name='sitegroup', @@ -188,7 +215,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='sitegroup', - constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('slug',), name='dcim_sitegroup_slug'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent', None)), fields=('slug',), name='dcim_sitegroup_slug' + ), ), migrations.AddField( model_name='devicerole', @@ -328,7 +357,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name='interface', name='tx_power', - field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(127)]), + field=models.PositiveSmallIntegerField( + blank=True, null=True, validators=[django.core.validators.MaxValueValidator(127)] + ), ), migrations.AddField( model_name='interface', @@ -338,7 +369,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='interface', name='wireless_link', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wireless.wirelesslink'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='wireless.wirelesslink', + ), ), migrations.AddField( model_name='site', @@ -348,12 +385,24 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='device', name='primary_ip4', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress'), + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='ipam.ipaddress', + ), ), migrations.AlterField( model_name='device', name='primary_ip6', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress'), + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='ipam.ipaddress', + ), ), migrations.RemoveField( model_name='site', @@ -372,7 +421,23 @@ class Migration(migrations.Migration): name='contact_phone', ), migrations.RunSQL( - sql="\n DO $$\n DECLARE\n idx record;\n BEGIN\n FOR idx IN\n SELECT indexname AS old_name,\n replace(indexname, 'module', 'inventoryitem') AS new_name\n FROM pg_indexes\n WHERE schemaname = 'public' AND\n tablename = 'dcim_inventoryitem' AND\n indexname LIKE 'dcim_module_%'\n LOOP\n EXECUTE format(\n 'ALTER INDEX %I RENAME TO %I;',\n idx.old_name,\n idx.new_name\n );\n END LOOP;\n END$$;\n ", + sql="""DO $$ + DECLARE idx record; + BEGIN + FOR idx IN + SELECT indexname AS old_name, replace(indexname, 'module', 'inventoryitem') AS new_name + FROM pg_indexes + WHERE schemaname = 'public' AND + tablename = 'dcim_inventoryitem' AND + indexname LIKE 'dcim_module_%' + LOOP + EXECUTE format( + 'ALTER INDEX %I RENAME TO %I;', + idx.old_name, + idx.new_name + ); + END LOOP; + END$$;""", ), migrations.AlterModelOptions( name='consoleporttemplate', @@ -405,49 +470,99 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='consoleporttemplate', name='device_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.devicetype', + ), ), migrations.AlterField( model_name='consoleserverporttemplate', name='device_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.devicetype', + ), ), migrations.AlterField( model_name='frontporttemplate', name='device_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.devicetype', + ), ), migrations.AlterField( model_name='interfacetemplate', name='device_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.devicetype', + ), ), migrations.AlterField( model_name='poweroutlettemplate', name='device_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.devicetype', + ), ), migrations.AlterField( model_name='powerporttemplate', name='device_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.devicetype', + ), ), migrations.AlterField( model_name='rearporttemplate', name='device_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.devicetype', + ), ), migrations.CreateModel( name='ModuleType', fields=[ ('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)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('model', models.CharField(max_length=100)), ('part_number', models.CharField(blank=True, max_length=50)), ('comments', models.TextField(blank=True)), - ('manufacturer', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='module_types', to='dcim.manufacturer')), + ( + 'manufacturer', + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='module_types', to='dcim.manufacturer' + ), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), ], options={ @@ -460,14 +575,27 @@ class Migration(migrations.Migration): fields=[ ('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)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('position', models.CharField(blank=True, max_length=30)), ('description', models.CharField(blank=True, max_length=200)), - ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device')), + ( + 'device', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device' + ), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), ], options={ @@ -480,15 +608,35 @@ class Migration(migrations.Migration): fields=[ ('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)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('local_context_data', models.JSONField(blank=True, null=True)), ('serial', models.CharField(blank=True, max_length=50)), ('asset_tag', models.CharField(blank=True, max_length=50, null=True, unique=True)), ('comments', models.TextField(blank=True)), - ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='modules', to='dcim.device')), - ('module_bay', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='installed_module', to='dcim.modulebay')), - ('module_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='instances', to='dcim.moduletype')), + ( + 'device', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='modules', to='dcim.device' + ), + ), + ( + 'module_bay', + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name='installed_module', + to='dcim.modulebay', + ), + ), + ( + 'module_type', + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='instances', to='dcim.moduletype' + ), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), ], options={ @@ -498,72 +646,156 @@ class Migration(migrations.Migration): migrations.AddField( model_name='consoleport', name='module', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.module', + ), ), migrations.AddField( model_name='consoleporttemplate', name='module_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.moduletype', + ), ), migrations.AddField( model_name='consoleserverport', name='module', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.module', + ), ), migrations.AddField( model_name='consoleserverporttemplate', name='module_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.moduletype', + ), ), migrations.AddField( model_name='frontport', name='module', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.module', + ), ), migrations.AddField( model_name='frontporttemplate', name='module_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.moduletype', + ), ), migrations.AddField( model_name='interface', name='module', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.module', + ), ), migrations.AddField( model_name='interfacetemplate', name='module_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.moduletype', + ), ), migrations.AddField( model_name='poweroutlet', name='module', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.module', + ), ), migrations.AddField( model_name='poweroutlettemplate', name='module_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.moduletype', + ), ), migrations.AddField( model_name='powerport', name='module', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.module', + ), ), migrations.AddField( model_name='powerporttemplate', name='module_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.moduletype', + ), ), migrations.AddField( model_name='rearport', name='module', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.module', + ), ), migrations.AddField( model_name='rearporttemplate', name='module_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.moduletype', + ), ), migrations.AlterUniqueTogether( name='consoleporttemplate', @@ -598,7 +830,10 @@ class Migration(migrations.Migration): fields=[ ('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)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('name', models.CharField(max_length=100, unique=True)), ('slug', models.SlugField(max_length=100, unique=True)), @@ -613,7 +848,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='inventoryitem', name='role', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_items', to='dcim.inventoryitemrole'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='inventory_items', + to='dcim.inventoryitemrole', + ), ), migrations.AddField( model_name='inventoryitem', @@ -623,12 +864,39 @@ class Migration(migrations.Migration): migrations.AddField( model_name='inventoryitem', name='component_type', - field=models.ForeignKey(blank=True, limit_choices_to=models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'poweroutlet', 'powerport', 'rearport'))), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + limit_choices_to=models.Q( + ('app_label', 'dcim'), + ( + 'model__in', + ( + 'consoleport', + 'consoleserverport', + 'frontport', + 'interface', + 'poweroutlet', + 'powerport', + 'rearport', + ), + ), + ), + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='interface', name='vrf', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interfaces', to='ipam.vrf'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='interfaces', + to='ipam.vrf', + ), ), migrations.AddField( model_name='interface', @@ -952,7 +1220,12 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('description', models.CharField(blank=True, max_length=200)), ('component_id', models.PositiveBigIntegerField(blank=True, null=True)), @@ -961,11 +1234,67 @@ class Migration(migrations.Migration): ('rght', models.PositiveIntegerField(editable=False)), ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), ('level', models.PositiveIntegerField(editable=False)), - ('component_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(('app_label', 'dcim'), ('model__in', ('consoleporttemplate', 'consoleserverporttemplate', 'frontporttemplate', 'interfacetemplate', 'poweroutlettemplate', 'powerporttemplate', 'rearporttemplate'))), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')), - ('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype')), - ('manufacturer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_item_templates', to='dcim.manufacturer')), - ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='child_items', to='dcim.inventoryitemtemplate')), - ('role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_item_templates', to='dcim.inventoryitemrole')), + ( + 'component_type', + models.ForeignKey( + blank=True, + limit_choices_to=models.Q( + ('app_label', 'dcim'), + ( + 'model__in', + ( + 'consoleporttemplate', + 'consoleserverporttemplate', + 'frontporttemplate', + 'interfacetemplate', + 'poweroutlettemplate', + 'powerporttemplate', + 'rearporttemplate', + ), + ), + ), + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), + ), + ( + 'device_type', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype' + ), + ), + ( + 'manufacturer', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='inventory_item_templates', + to='dcim.manufacturer', + ), + ), + ( + 'parent', + mptt.fields.TreeForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='child_items', + to='dcim.inventoryitemtemplate', + ), + ), + ( + 'role', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='inventory_item_templates', + to='dcim.inventoryitemrole', + ), + ), ], options={ 'ordering': ('device_type__id', 'parent__id', '_name'), @@ -989,11 +1318,21 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('name', models.CharField(max_length=64)), - ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)), + ( + '_name', + utilities.fields.NaturalOrderingField( + 'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize + ), + ), ('label', models.CharField(blank=True, max_length=64)), ('position', models.CharField(blank=True, max_length=30)), ('description', models.CharField(blank=True, max_length=200)), - ('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype')), + ( + 'device_type', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype' + ), + ), ], options={ 'ordering': ('device_type', '_name'), @@ -1088,7 +1427,16 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='device', name='position', - field=models.DecimalField(blank=True, decimal_places=1, max_digits=4, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100.5)]), + field=models.DecimalField( + blank=True, + decimal_places=1, + max_digits=4, + null=True, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(100.5), + ], + ), ), migrations.AddField( model_name='interface', @@ -1121,12 +1469,66 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('cable_end', models.CharField(max_length=1)), ('termination_id', models.PositiveBigIntegerField()), - ('cable', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='dcim.cable')), - ('termination_type', models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')), - ('_device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.device')), - ('_rack', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.rack')), - ('_location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.location')), - ('_site', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.site')), + ( + 'cable', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='dcim.cable' + ), + ), + ( + 'termination_type', + models.ForeignKey( + limit_choices_to=models.Q( + models.Q( + models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), + models.Q( + ('app_label', 'dcim'), + ( + 'model__in', + ( + 'consoleport', + 'consoleserverport', + 'frontport', + 'interface', + 'powerfeed', + 'poweroutlet', + 'powerport', + 'rearport', + ), + ), + ), + _connector='OR', + ) + ), + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), + ), + ( + '_device', + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.device' + ), + ), + ( + '_rack', + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.rack' + ), + ), + ( + '_location', + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.location' + ), + ), + ( + '_site', + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.site' + ), + ), ], options={ 'ordering': ('cable', 'cable_end', 'pk'), @@ -1134,7 +1536,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='cabletermination', - constraint=models.UniqueConstraint(fields=('termination_type', 'termination_id'), name='dcim_cable_termination_unique_termination'), + constraint=models.UniqueConstraint( + fields=('termination_type', 'termination_id'), name='dcim_cable_termination_unique_termination' + ), ), migrations.RenameField( model_name='cablepath', diff --git a/netbox/dcim/migrations/0160_squashed_0166.py b/netbox/dcim/migrations/0160_squashed_0166.py index 440a8115e..0deb58bab 100644 --- a/netbox/dcim/migrations/0160_squashed_0166.py +++ b/netbox/dcim/migrations/0160_squashed_0166.py @@ -6,7 +6,6 @@ import utilities.json class Migration(migrations.Migration): - replaces = [ ('dcim', '0160_populate_cable_ends'), ('dcim', '0161_cabling_cleanup'), @@ -14,7 +13,7 @@ class Migration(migrations.Migration): ('dcim', '0163_weight_fields'), ('dcim', '0164_rack_mounting_depth'), ('dcim', '0165_standardize_description_comments'), - ('dcim', '0166_virtualdevicecontext') + ('dcim', '0166_virtualdevicecontext'), ] dependencies = [ @@ -275,7 +274,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='cabletermination', - constraint=models.UniqueConstraint(fields=('termination_type', 'termination_id'), name='dcim_cabletermination_unique_termination'), + constraint=models.UniqueConstraint( + fields=('termination_type', 'termination_id'), name='dcim_cabletermination_unique_termination' + ), ), migrations.AddConstraint( model_name='consoleport', @@ -283,39 +284,64 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='consoleporttemplate', - constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_consoleporttemplate_unique_device_type_name'), + constraint=models.UniqueConstraint( + fields=('device_type', 'name'), name='dcim_consoleporttemplate_unique_device_type_name' + ), ), migrations.AddConstraint( model_name='consoleporttemplate', - constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_consoleporttemplate_unique_module_type_name'), + constraint=models.UniqueConstraint( + fields=('module_type', 'name'), name='dcim_consoleporttemplate_unique_module_type_name' + ), ), migrations.AddConstraint( model_name='consoleserverport', - constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_consoleserverport_unique_device_name'), + constraint=models.UniqueConstraint( + fields=('device', 'name'), name='dcim_consoleserverport_unique_device_name' + ), ), migrations.AddConstraint( model_name='consoleserverporttemplate', - constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_consoleserverporttemplate_unique_device_type_name'), + constraint=models.UniqueConstraint( + fields=('device_type', 'name'), name='dcim_consoleserverporttemplate_unique_device_type_name' + ), ), migrations.AddConstraint( model_name='consoleserverporttemplate', - constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_consoleserverporttemplate_unique_module_type_name'), + constraint=models.UniqueConstraint( + fields=('module_type', 'name'), name='dcim_consoleserverporttemplate_unique_module_type_name' + ), ), migrations.AddConstraint( model_name='device', - constraint=models.UniqueConstraint(django.db.models.functions.text.Lower('name'), models.F('site'), models.F('tenant'), name='dcim_device_unique_name_site_tenant'), + constraint=models.UniqueConstraint( + django.db.models.functions.text.Lower('name'), + models.F('site'), + models.F('tenant'), + name='dcim_device_unique_name_site_tenant', + ), ), migrations.AddConstraint( model_name='device', - constraint=models.UniqueConstraint(django.db.models.functions.text.Lower('name'), models.F('site'), condition=models.Q(('tenant__isnull', True)), name='dcim_device_unique_name_site', violation_error_message='Device name must be unique per site.'), + constraint=models.UniqueConstraint( + django.db.models.functions.text.Lower('name'), + models.F('site'), + condition=models.Q(('tenant__isnull', True)), + name='dcim_device_unique_name_site', + violation_error_message='Device name must be unique per site.', + ), ), migrations.AddConstraint( model_name='device', - constraint=models.UniqueConstraint(fields=('rack', 'position', 'face'), name='dcim_device_unique_rack_position_face'), + constraint=models.UniqueConstraint( + fields=('rack', 'position', 'face'), name='dcim_device_unique_rack_position_face' + ), ), migrations.AddConstraint( model_name='device', - constraint=models.UniqueConstraint(fields=('virtual_chassis', 'vc_position'), name='dcim_device_unique_virtual_chassis_vc_position'), + constraint=models.UniqueConstraint( + fields=('virtual_chassis', 'vc_position'), name='dcim_device_unique_virtual_chassis_vc_position' + ), ), migrations.AddConstraint( model_name='devicebay', @@ -323,15 +349,21 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='devicebaytemplate', - constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_devicebaytemplate_unique_device_type_name'), + constraint=models.UniqueConstraint( + fields=('device_type', 'name'), name='dcim_devicebaytemplate_unique_device_type_name' + ), ), migrations.AddConstraint( model_name='devicetype', - constraint=models.UniqueConstraint(fields=('manufacturer', 'model'), name='dcim_devicetype_unique_manufacturer_model'), + constraint=models.UniqueConstraint( + fields=('manufacturer', 'model'), name='dcim_devicetype_unique_manufacturer_model' + ), ), migrations.AddConstraint( model_name='devicetype', - constraint=models.UniqueConstraint(fields=('manufacturer', 'slug'), name='dcim_devicetype_unique_manufacturer_slug'), + constraint=models.UniqueConstraint( + fields=('manufacturer', 'slug'), name='dcim_devicetype_unique_manufacturer_slug' + ), ), migrations.AddConstraint( model_name='frontport', @@ -339,19 +371,27 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='frontport', - constraint=models.UniqueConstraint(fields=('rear_port', 'rear_port_position'), name='dcim_frontport_unique_rear_port_position'), + constraint=models.UniqueConstraint( + fields=('rear_port', 'rear_port_position'), name='dcim_frontport_unique_rear_port_position' + ), ), migrations.AddConstraint( model_name='frontporttemplate', - constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_frontporttemplate_unique_device_type_name'), + constraint=models.UniqueConstraint( + fields=('device_type', 'name'), name='dcim_frontporttemplate_unique_device_type_name' + ), ), migrations.AddConstraint( model_name='frontporttemplate', - constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_frontporttemplate_unique_module_type_name'), + constraint=models.UniqueConstraint( + fields=('module_type', 'name'), name='dcim_frontporttemplate_unique_module_type_name' + ), ), migrations.AddConstraint( model_name='frontporttemplate', - constraint=models.UniqueConstraint(fields=('rear_port', 'rear_port_position'), name='dcim_frontporttemplate_unique_rear_port_position'), + constraint=models.UniqueConstraint( + fields=('rear_port', 'rear_port_position'), name='dcim_frontporttemplate_unique_rear_port_position' + ), ), migrations.AddConstraint( model_name='interface', @@ -359,27 +399,46 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='interfacetemplate', - constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_interfacetemplate_unique_device_type_name'), + constraint=models.UniqueConstraint( + fields=('device_type', 'name'), name='dcim_interfacetemplate_unique_device_type_name' + ), ), migrations.AddConstraint( model_name='interfacetemplate', - constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_interfacetemplate_unique_module_type_name'), + constraint=models.UniqueConstraint( + fields=('module_type', 'name'), name='dcim_interfacetemplate_unique_module_type_name' + ), ), migrations.AddConstraint( model_name='inventoryitem', - constraint=models.UniqueConstraint(fields=('device', 'parent', 'name'), name='dcim_inventoryitem_unique_device_parent_name'), + constraint=models.UniqueConstraint( + fields=('device', 'parent', 'name'), name='dcim_inventoryitem_unique_device_parent_name' + ), ), migrations.AddConstraint( model_name='inventoryitemtemplate', - constraint=models.UniqueConstraint(fields=('device_type', 'parent', 'name'), name='dcim_inventoryitemtemplate_unique_device_type_parent_name'), + constraint=models.UniqueConstraint( + fields=('device_type', 'parent', 'name'), + name='dcim_inventoryitemtemplate_unique_device_type_parent_name', + ), ), migrations.AddConstraint( model_name='location', - constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('site', 'name'), name='dcim_location_name', violation_error_message='A location with this name already exists within the specified site.'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent__isnull', True)), + fields=('site', 'name'), + name='dcim_location_name', + violation_error_message='A location with this name already exists within the specified site.', + ), ), migrations.AddConstraint( model_name='location', - constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('site', 'slug'), name='dcim_location_slug', violation_error_message='A location with this slug already exists within the specified site.'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent__isnull', True)), + fields=('site', 'slug'), + name='dcim_location_slug', + violation_error_message='A location with this slug already exists within the specified site.', + ), ), migrations.AddConstraint( model_name='modulebay', @@ -387,15 +446,21 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='modulebaytemplate', - constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_modulebaytemplate_unique_device_type_name'), + constraint=models.UniqueConstraint( + fields=('device_type', 'name'), name='dcim_modulebaytemplate_unique_device_type_name' + ), ), migrations.AddConstraint( model_name='moduletype', - constraint=models.UniqueConstraint(fields=('manufacturer', 'model'), name='dcim_moduletype_unique_manufacturer_model'), + constraint=models.UniqueConstraint( + fields=('manufacturer', 'model'), name='dcim_moduletype_unique_manufacturer_model' + ), ), migrations.AddConstraint( model_name='powerfeed', - constraint=models.UniqueConstraint(fields=('power_panel', 'name'), name='dcim_powerfeed_unique_power_panel_name'), + constraint=models.UniqueConstraint( + fields=('power_panel', 'name'), name='dcim_powerfeed_unique_power_panel_name' + ), ), migrations.AddConstraint( model_name='poweroutlet', @@ -403,11 +468,15 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='poweroutlettemplate', - constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_poweroutlettemplate_unique_device_type_name'), + constraint=models.UniqueConstraint( + fields=('device_type', 'name'), name='dcim_poweroutlettemplate_unique_device_type_name' + ), ), migrations.AddConstraint( model_name='poweroutlettemplate', - constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_poweroutlettemplate_unique_module_type_name'), + constraint=models.UniqueConstraint( + fields=('module_type', 'name'), name='dcim_poweroutlettemplate_unique_module_type_name' + ), ), migrations.AddConstraint( model_name='powerpanel', @@ -419,11 +488,15 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='powerporttemplate', - constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_powerporttemplate_unique_device_type_name'), + constraint=models.UniqueConstraint( + fields=('device_type', 'name'), name='dcim_powerporttemplate_unique_device_type_name' + ), ), migrations.AddConstraint( model_name='powerporttemplate', - constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_powerporttemplate_unique_module_type_name'), + constraint=models.UniqueConstraint( + fields=('module_type', 'name'), name='dcim_powerporttemplate_unique_module_type_name' + ), ), migrations.AddConstraint( model_name='rack', @@ -431,7 +504,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='rack', - constraint=models.UniqueConstraint(fields=('location', 'facility_id'), name='dcim_rack_unique_location_facility_id'), + constraint=models.UniqueConstraint( + fields=('location', 'facility_id'), name='dcim_rack_unique_location_facility_id' + ), ), migrations.AddConstraint( model_name='rearport', @@ -439,27 +514,51 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='rearporttemplate', - constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_rearporttemplate_unique_device_type_name'), + constraint=models.UniqueConstraint( + fields=('device_type', 'name'), name='dcim_rearporttemplate_unique_device_type_name' + ), ), migrations.AddConstraint( model_name='rearporttemplate', - constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_rearporttemplate_unique_module_type_name'), + constraint=models.UniqueConstraint( + fields=('module_type', 'name'), name='dcim_rearporttemplate_unique_module_type_name' + ), ), migrations.AddConstraint( model_name='region', - constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('name',), name='dcim_region_name', violation_error_message='A top-level region with this name already exists.'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent__isnull', True)), + fields=('name',), + name='dcim_region_name', + violation_error_message='A top-level region with this name already exists.', + ), ), migrations.AddConstraint( model_name='region', - constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('slug',), name='dcim_region_slug', violation_error_message='A top-level region with this slug already exists.'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent__isnull', True)), + fields=('slug',), + name='dcim_region_slug', + violation_error_message='A top-level region with this slug already exists.', + ), ), migrations.AddConstraint( model_name='sitegroup', - constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('name',), name='dcim_sitegroup_name', violation_error_message='A top-level site group with this name already exists.'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent__isnull', True)), + fields=('name',), + name='dcim_sitegroup_name', + violation_error_message='A top-level site group with this name already exists.', + ), ), migrations.AddConstraint( model_name='sitegroup', - constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('slug',), name='dcim_sitegroup_slug', violation_error_message='A top-level site group with this slug already exists.'), + constraint=models.UniqueConstraint( + condition=models.Q(('parent__isnull', True)), + fields=('slug',), + name='dcim_sitegroup_slug', + violation_error_message='A top-level site group with this slug already exists.', + ), ), migrations.AddField( model_name='devicetype', @@ -592,17 +691,56 @@ class Migration(migrations.Migration): ('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)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), ('description', models.CharField(blank=True, max_length=200)), ('name', models.CharField(max_length=64)), ('status', models.CharField(max_length=50)), ('identifier', models.PositiveSmallIntegerField(blank=True, null=True)), ('comments', models.TextField(blank=True)), - ('device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vdcs', to='dcim.device')), - ('primary_ip4', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress')), - ('primary_ip6', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress')), + ( + 'device', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='vdcs', + to='dcim.device', + ), + ), + ( + 'primary_ip4', + models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='ipam.ipaddress', + ), + ), + ( + 'primary_ip6', + models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='ipam.ipaddress', + ), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), - ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vdcs', to='tenancy.tenant')), + ( + 'tenant', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='vdcs', + to='tenancy.tenant', + ), + ), ], options={ 'ordering': ['name'], @@ -615,7 +753,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='virtualdevicecontext', - constraint=models.UniqueConstraint(fields=('device', 'identifier'), name='dcim_virtualdevicecontext_device_identifier'), + constraint=models.UniqueConstraint( + fields=('device', 'identifier'), name='dcim_virtualdevicecontext_device_identifier' + ), ), migrations.AddConstraint( model_name='virtualdevicecontext', diff --git a/netbox/dcim/migrations/0167_squashed_0182.py b/netbox/dcim/migrations/0167_squashed_0182.py index 735cb3efa..d0ad5379f 100644 --- a/netbox/dcim/migrations/0167_squashed_0182.py +++ b/netbox/dcim/migrations/0167_squashed_0182.py @@ -6,7 +6,6 @@ import utilities.fields class Migration(migrations.Migration): - replaces = [ ('dcim', '0167_module_status'), ('dcim', '0168_interface_template_enabled'), @@ -24,7 +23,7 @@ class Migration(migrations.Migration): ('dcim', '0179_interfacetemplate_rf_role'), ('dcim', '0180_powerfeed_tenant'), ('dcim', '0181_rename_device_role_device_role'), - ('dcim', '0182_zero_length_cable_fix') + ('dcim', '0182_zero_length_cable_fix'), ] dependencies = [ @@ -48,27 +47,57 @@ class Migration(migrations.Migration): migrations.AddField( model_name='interfacetemplate', name='bridge', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bridge_interfaces', to='dcim.interfacetemplate'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='bridge_interfaces', + to='dcim.interfacetemplate', + ), ), migrations.AddField( model_name='devicetype', name='default_platform', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.platform'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='dcim.platform', + ), ), migrations.AddField( model_name='device', name='config_template', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='%(class)ss', to='extras.configtemplate'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='%(class)ss', + to='extras.configtemplate', + ), ), migrations.AddField( model_name='devicerole', name='config_template', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='device_roles', to='extras.configtemplate'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='device_roles', + to='extras.configtemplate', + ), ), migrations.AddField( model_name='platform', name='config_template', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='platforms', to='extras.configtemplate'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='platforms', + to='extras.configtemplate', + ), ), migrations.AddField( model_name='cabletermination', @@ -83,22 +112,30 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='powerport', name='allocated_draw', - field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]), + field=models.PositiveIntegerField( + blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)] + ), ), migrations.AlterField( model_name='powerport', name='maximum_draw', - field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]), + field=models.PositiveIntegerField( + blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)] + ), ), migrations.AlterField( model_name='powerporttemplate', name='allocated_draw', - field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]), + field=models.PositiveIntegerField( + blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)] + ), ), migrations.AlterField( model_name='powerporttemplate', name='maximum_draw', - field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]), + field=models.PositiveIntegerField( + blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)] + ), ), migrations.RemoveField( model_name='platform', @@ -126,112 +163,160 @@ class Migration(migrations.Migration): migrations.AddField( model_name='device', name='oob_ip', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress'), + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='ipam.ipaddress', + ), ), migrations.AddField( model_name='device', name='console_port_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.ConsolePort'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device', to_model='dcim.ConsolePort' + ), ), migrations.AddField( model_name='device', name='console_server_port_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.ConsoleServerPort'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device', to_model='dcim.ConsoleServerPort' + ), ), migrations.AddField( model_name='device', name='power_port_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.PowerPort'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device', to_model='dcim.PowerPort' + ), ), migrations.AddField( model_name='device', name='power_outlet_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.PowerOutlet'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device', to_model='dcim.PowerOutlet' + ), ), migrations.AddField( model_name='device', name='interface_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.Interface'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device', to_model='dcim.Interface' + ), ), migrations.AddField( model_name='device', name='front_port_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.FrontPort'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device', to_model='dcim.FrontPort' + ), ), migrations.AddField( model_name='device', name='rear_port_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.RearPort'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device', to_model='dcim.RearPort' + ), ), migrations.AddField( model_name='device', name='device_bay_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.DeviceBay'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device', to_model='dcim.DeviceBay' + ), ), migrations.AddField( model_name='device', name='module_bay_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.ModuleBay'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device', to_model='dcim.ModuleBay' + ), ), migrations.AddField( model_name='device', name='inventory_item_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.InventoryItem'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device', to_model='dcim.InventoryItem' + ), ), migrations.AddField( model_name='devicetype', name='console_port_template_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.ConsolePortTemplate'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device_type', to_model='dcim.ConsolePortTemplate' + ), ), migrations.AddField( model_name='devicetype', name='console_server_port_template_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.ConsoleServerPortTemplate'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device_type', to_model='dcim.ConsoleServerPortTemplate' + ), ), migrations.AddField( model_name='devicetype', name='power_port_template_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.PowerPortTemplate'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device_type', to_model='dcim.PowerPortTemplate' + ), ), migrations.AddField( model_name='devicetype', name='power_outlet_template_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.PowerOutletTemplate'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device_type', to_model='dcim.PowerOutletTemplate' + ), ), migrations.AddField( model_name='devicetype', name='interface_template_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.InterfaceTemplate'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device_type', to_model='dcim.InterfaceTemplate' + ), ), migrations.AddField( model_name='devicetype', name='front_port_template_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.FrontPortTemplate'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device_type', to_model='dcim.FrontPortTemplate' + ), ), migrations.AddField( model_name='devicetype', name='rear_port_template_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.RearPortTemplate'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device_type', to_model='dcim.RearPortTemplate' + ), ), migrations.AddField( model_name='devicetype', name='device_bay_template_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.DeviceBayTemplate'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device_type', to_model='dcim.DeviceBayTemplate' + ), ), migrations.AddField( model_name='devicetype', name='module_bay_template_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.ModuleBayTemplate'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device_type', to_model='dcim.ModuleBayTemplate' + ), ), migrations.AddField( model_name='devicetype', name='inventory_item_template_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.InventoryItemTemplate'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='device_type', to_model='dcim.InventoryItemTemplate' + ), ), migrations.AddField( model_name='virtualchassis', name='member_count', - field=utilities.fields.CounterCacheField(default=0, editable=False, to_field='virtual_chassis', to_model='dcim.Device'), + field=utilities.fields.CounterCacheField( + default=0, editable=False, to_field='virtual_chassis', to_model='dcim.Device' + ), ), migrations.AddField( model_name='interfacetemplate', @@ -241,7 +326,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='powerfeed', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='power_feeds', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='power_feeds', + to='tenancy.tenant', + ), ), migrations.RenameField( model_name='device', diff --git a/netbox/dcim/migrations/0184_protect_child_interfaces.py b/netbox/dcim/migrations/0184_protect_child_interfaces.py index 3459e23fc..58eca506d 100644 --- a/netbox/dcim/migrations/0184_protect_child_interfaces.py +++ b/netbox/dcim/migrations/0184_protect_child_interfaces.py @@ -5,7 +5,6 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ ('dcim', '0183_devicetype_exclude_from_utilization'), ] @@ -14,6 +13,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='interface', name='parent', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.RESTRICT, related_name='child_interfaces', to='dcim.interface'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.RESTRICT, + related_name='child_interfaces', + to='dcim.interface', + ), ), ] diff --git a/netbox/dcim/migrations/0185_gfk_indexes.py b/netbox/dcim/migrations/0185_gfk_indexes.py index 84cdc53ff..5c099b380 100644 --- a/netbox/dcim/migrations/0185_gfk_indexes.py +++ b/netbox/dcim/migrations/0185_gfk_indexes.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('dcim', '0184_protect_child_interfaces'), ] diff --git a/netbox/dcim/migrations/0186_location_facility.py b/netbox/dcim/migrations/0186_location_facility.py index 759ee813b..3d22503b6 100644 --- a/netbox/dcim/migrations/0186_location_facility.py +++ b/netbox/dcim/migrations/0186_location_facility.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('dcim', '0185_gfk_indexes'), ] diff --git a/netbox/dcim/migrations/0187_alter_device_vc_position.py b/netbox/dcim/migrations/0187_alter_device_vc_position.py index d4a42dc20..10b636959 100644 --- a/netbox/dcim/migrations/0187_alter_device_vc_position.py +++ b/netbox/dcim/migrations/0187_alter_device_vc_position.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('dcim', '0186_location_facility'), ] diff --git a/netbox/dcim/migrations/0188_racktype.py b/netbox/dcim/migrations/0188_racktype.py index aa45246e5..a5265d030 100644 --- a/netbox/dcim/migrations/0188_racktype.py +++ b/netbox/dcim/migrations/0188_racktype.py @@ -9,7 +9,6 @@ import utilities.ordering class Migration(migrations.Migration): - dependencies = [ ('extras', '0118_customfield_uniqueness'), ('dcim', '0187_alter_device_vc_position'), @@ -22,36 +21,41 @@ class Migration(migrations.Migration): ('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 - )), + ( + '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)), ('weight', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)), ('weight_unit', models.CharField(blank=True, max_length=50)), ('_abs_weight', models.PositiveBigIntegerField(blank=True, null=True)), - ('manufacturer', models.ForeignKey( - on_delete=django.db.models.deletion.PROTECT, - related_name='rack_types', - to='dcim.manufacturer' - )), + ( + 'manufacturer', + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='rack_types', to='dcim.manufacturer' + ), + ), ('model', models.CharField(max_length=100)), ('slug', models.SlugField(max_length=100, unique=True)), ('form_factor', models.CharField(max_length=50)), ('width', models.PositiveSmallIntegerField(default=19)), - ('u_height', models.PositiveSmallIntegerField( - default=42, - validators=[ - django.core.validators.MinValueValidator(1), - django.core.validators.MaxValueValidator(100), - ] - )), - ('starting_unit', models.PositiveSmallIntegerField( - default=1, - validators=[django.core.validators.MinValueValidator(1)] - )), + ( + 'u_height', + models.PositiveSmallIntegerField( + default=42, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(100), + ], + ), + ), + ( + 'starting_unit', + models.PositiveSmallIntegerField( + default=1, validators=[django.core.validators.MinValueValidator(1)] + ), + ), ('desc_units', models.BooleanField(default=False)), ('outer_width', models.PositiveSmallIntegerField(blank=True, null=True)), ('outer_depth', models.PositiveSmallIntegerField(blank=True, null=True)), diff --git a/netbox/dcim/migrations/0189_moduletype_rack_airflow.py b/netbox/dcim/migrations/0189_moduletype_rack_airflow.py index 31787b67d..c356e32f7 100644 --- a/netbox/dcim/migrations/0189_moduletype_rack_airflow.py +++ b/netbox/dcim/migrations/0189_moduletype_rack_airflow.py @@ -2,7 +2,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('dcim', '0188_racktype'), ] diff --git a/netbox/dcim/migrations/0190_nested_modules.py b/netbox/dcim/migrations/0190_nested_modules.py index 9cef40efb..239e08639 100644 --- a/netbox/dcim/migrations/0190_nested_modules.py +++ b/netbox/dcim/migrations/0190_nested_modules.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('dcim', '0189_moduletype_rack_airflow'), ('extras', '0121_customfield_related_object_filter'), @@ -34,12 +33,25 @@ class Migration(migrations.Migration): migrations.AddField( model_name='modulebay', name='module', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.module', + ), ), migrations.AddField( model_name='modulebay', name='parent', - field=mptt.fields.TreeForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.modulebay'), + field=mptt.fields.TreeForeignKey( + blank=True, + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='children', + to='dcim.modulebay', + ), ), migrations.AddField( model_name='modulebay', @@ -56,19 +68,35 @@ class Migration(migrations.Migration): migrations.AddField( model_name='modulebaytemplate', name='module_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.moduletype', + ), ), migrations.AlterField( model_name='modulebaytemplate', name='device_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(class)ss', + to='dcim.devicetype', + ), ), migrations.AddConstraint( model_name='modulebay', - constraint=models.UniqueConstraint(fields=('device', 'module', 'name'), name='dcim_modulebay_unique_device_module_name'), + constraint=models.UniqueConstraint( + fields=('device', 'module', 'name'), name='dcim_modulebay_unique_device_module_name' + ), ), migrations.AddConstraint( model_name='modulebaytemplate', - constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_modulebaytemplate_unique_module_type_name'), + constraint=models.UniqueConstraint( + fields=('module_type', 'name'), name='dcim_modulebaytemplate_unique_module_type_name' + ), ), ] diff --git a/netbox/dcim/migrations/0191_module_bay_rebuild.py b/netbox/dcim/migrations/0191_module_bay_rebuild.py index 260063213..4f8a461f2 100644 --- a/netbox/dcim/migrations/0191_module_bay_rebuild.py +++ b/netbox/dcim/migrations/0191_module_bay_rebuild.py @@ -13,14 +13,10 @@ def rebuild_mptt(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('dcim', '0190_nested_modules'), ] operations = [ - migrations.RunPython( - code=rebuild_mptt, - reverse_code=migrations.RunPython.noop - ), + migrations.RunPython(code=rebuild_mptt, reverse_code=migrations.RunPython.noop), ] diff --git a/netbox/dcim/migrations/0192_inventoryitem_status.py b/netbox/dcim/migrations/0192_inventoryitem_status.py index 335ab2ca7..027f2daef 100644 --- a/netbox/dcim/migrations/0192_inventoryitem_status.py +++ b/netbox/dcim/migrations/0192_inventoryitem_status.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('dcim', '0191_module_bay_rebuild'), ] diff --git a/netbox/dcim/migrations/0193_poweroutlet_color.py b/netbox/dcim/migrations/0193_poweroutlet_color.py index 0a6c08b48..f7e3c430c 100644 --- a/netbox/dcim/migrations/0193_poweroutlet_color.py +++ b/netbox/dcim/migrations/0193_poweroutlet_color.py @@ -5,7 +5,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ ('dcim', '0192_inventoryitem_status'), ] diff --git a/netbox/dcim/migrations/0194_charfield_null_choices.py b/netbox/dcim/migrations/0194_charfield_null_choices.py new file mode 100644 index 000000000..e13b0e10d --- /dev/null +++ b/netbox/dcim/migrations/0194_charfield_null_choices.py @@ -0,0 +1,291 @@ +import timezone_field.fields +from django.db import migrations, models + + +def set_null_values(apps, schema_editor): + """ + Replace empty strings with null values. + """ + Cable = apps.get_model('dcim', 'Cable') + ConsolePort = apps.get_model('dcim', 'ConsolePort') + ConsolePortTemplate = apps.get_model('dcim', 'ConsolePortTemplate') + ConsoleServerPort = apps.get_model('dcim', 'ConsoleServerPort') + ConsoleServerPortTemplate = apps.get_model('dcim', 'ConsoleServerPortTemplate') + Device = apps.get_model('dcim', 'Device') + DeviceType = apps.get_model('dcim', 'DeviceType') + FrontPort = apps.get_model('dcim', 'FrontPort') + Interface = apps.get_model('dcim', 'Interface') + InterfaceTemplate = apps.get_model('dcim', 'InterfaceTemplate') + ModuleType = apps.get_model('dcim', 'ModuleType') + PowerFeed = apps.get_model('dcim', 'PowerFeed') + PowerOutlet = apps.get_model('dcim', 'PowerOutlet') + PowerOutletTemplate = apps.get_model('dcim', 'PowerOutletTemplate') + PowerPort = apps.get_model('dcim', 'PowerPort') + PowerPortTemplate = apps.get_model('dcim', 'PowerPortTemplate') + Rack = apps.get_model('dcim', 'Rack') + RackType = apps.get_model('dcim', 'RackType') + RearPort = apps.get_model('dcim', 'RearPort') + Site = apps.get_model('dcim', 'Site') + + Cable.objects.filter(length_unit='').update(length_unit=None) + Cable.objects.filter(type='').update(type=None) + ConsolePort.objects.filter(cable_end='').update(cable_end=None) + ConsolePort.objects.filter(type='').update(type=None) + ConsolePortTemplate.objects.filter(type='').update(type=None) + ConsoleServerPort.objects.filter(cable_end='').update(cable_end=None) + ConsoleServerPort.objects.filter(type='').update(type=None) + ConsoleServerPortTemplate.objects.filter(type='').update(type=None) + Device.objects.filter(airflow='').update(airflow=None) + Device.objects.filter(face='').update(face=None) + DeviceType.objects.filter(airflow='').update(airflow=None) + DeviceType.objects.filter(subdevice_role='').update(subdevice_role=None) + DeviceType.objects.filter(weight_unit='').update(weight_unit=None) + FrontPort.objects.filter(cable_end='').update(cable_end=None) + Interface.objects.filter(cable_end='').update(cable_end=None) + Interface.objects.filter(mode='').update(mode=None) + Interface.objects.filter(poe_mode='').update(poe_mode=None) + Interface.objects.filter(poe_type='').update(poe_type=None) + Interface.objects.filter(rf_channel='').update(rf_channel=None) + Interface.objects.filter(rf_role='').update(rf_role=None) + InterfaceTemplate.objects.filter(poe_mode='').update(poe_mode=None) + InterfaceTemplate.objects.filter(poe_type='').update(poe_type=None) + InterfaceTemplate.objects.filter(rf_role='').update(rf_role=None) + ModuleType.objects.filter(airflow='').update(airflow=None) + ModuleType.objects.filter(weight_unit='').update(weight_unit=None) + PowerFeed.objects.filter(cable_end='').update(cable_end=None) + PowerOutlet.objects.filter(cable_end='').update(cable_end=None) + PowerOutlet.objects.filter(feed_leg='').update(feed_leg=None) + PowerOutlet.objects.filter(type='').update(type=None) + PowerOutletTemplate.objects.filter(feed_leg='').update(feed_leg=None) + PowerOutletTemplate.objects.filter(type='').update(type=None) + PowerPort.objects.filter(cable_end='').update(cable_end=None) + PowerPort.objects.filter(type='').update(type=None) + PowerPortTemplate.objects.filter(type='').update(type=None) + Rack.objects.filter(airflow='').update(airflow=None) + Rack.objects.filter(form_factor='').update(form_factor=None) + Rack.objects.filter(outer_unit='').update(outer_unit=None) + Rack.objects.filter(weight_unit='').update(weight_unit=None) + RackType.objects.filter(outer_unit='').update(outer_unit=None) + RackType.objects.filter(weight_unit='').update(weight_unit=None) + RearPort.objects.filter(cable_end='').update(cable_end=None) + Site.objects.filter(time_zone='').update(time_zone=None) + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0193_poweroutlet_color'), + ] + + operations = [ + migrations.AlterField( + model_name='cable', + name='length_unit', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='cable', + name='type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='consoleport', + name='cable_end', + field=models.CharField(blank=True, max_length=1, null=True), + ), + migrations.AlterField( + model_name='consoleport', + name='type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='consoleporttemplate', + name='type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='consoleserverport', + name='cable_end', + field=models.CharField(blank=True, max_length=1, null=True), + ), + migrations.AlterField( + model_name='consoleserverport', + name='type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='consoleserverporttemplate', + name='type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='device', + name='airflow', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='device', + name='face', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='devicetype', + name='airflow', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='devicetype', + name='subdevice_role', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='devicetype', + name='weight_unit', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='frontport', + name='cable_end', + field=models.CharField(blank=True, max_length=1, null=True), + ), + migrations.AlterField( + model_name='interface', + name='cable_end', + field=models.CharField(blank=True, max_length=1, null=True), + ), + migrations.AlterField( + model_name='interface', + name='mode', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='interface', + name='poe_mode', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='interface', + name='poe_type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='interface', + name='rf_channel', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='interface', + name='rf_role', + field=models.CharField(blank=True, max_length=30, null=True), + ), + migrations.AlterField( + model_name='interfacetemplate', + name='poe_mode', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='interfacetemplate', + name='poe_type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='interfacetemplate', + name='rf_role', + field=models.CharField(blank=True, max_length=30, null=True), + ), + migrations.AlterField( + model_name='moduletype', + name='airflow', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='moduletype', + name='weight_unit', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='powerfeed', + name='cable_end', + field=models.CharField(blank=True, max_length=1, null=True), + ), + migrations.AlterField( + model_name='poweroutlet', + name='cable_end', + field=models.CharField(blank=True, max_length=1, null=True), + ), + migrations.AlterField( + model_name='poweroutlet', + name='feed_leg', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='poweroutlet', + name='type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='poweroutlettemplate', + name='feed_leg', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='poweroutlettemplate', + name='type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='powerport', + name='cable_end', + field=models.CharField(blank=True, max_length=1, null=True), + ), + migrations.AlterField( + model_name='powerport', + name='type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='powerporttemplate', + name='type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='rack', + name='airflow', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='rack', + name='form_factor', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='rack', + name='outer_unit', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='rack', + name='weight_unit', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='racktype', + name='outer_unit', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='racktype', + name='weight_unit', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='rearport', + name='cable_end', + field=models.CharField(blank=True, max_length=1, null=True), + ), + migrations.AlterField( + model_name='site', + name='time_zone', + field=timezone_field.fields.TimeZoneField(blank=True, null=True), + ), + migrations.RunPython(code=set_null_values, reverse_code=migrations.RunPython.noop), + ] diff --git a/netbox/dcim/migrations/0195_interface_vlan_translation_policy.py b/netbox/dcim/migrations/0195_interface_vlan_translation_policy.py new file mode 100644 index 000000000..9ec404886 --- /dev/null +++ b/netbox/dcim/migrations/0195_interface_vlan_translation_policy.py @@ -0,0 +1,21 @@ +# Generated by Django 5.0.9 on 2024-10-11 19:45 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0194_charfield_null_choices'), + ('ipam', '0074_vlantranslationpolicy_vlantranslationrule'), + ] + + operations = [ + migrations.AddField( + model_name='interface', + name='vlan_translation_policy', + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ipam.vlantranslationpolicy' + ), + ), + ] diff --git a/netbox/dcim/migrations/0196_qinq_svlan.py b/netbox/dcim/migrations/0196_qinq_svlan.py new file mode 100644 index 000000000..a03ad144a --- /dev/null +++ b/netbox/dcim/migrations/0196_qinq_svlan.py @@ -0,0 +1,39 @@ +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0195_interface_vlan_translation_policy'), + ('ipam', '0075_vlan_qinq'), + ] + + operations = [ + migrations.AddField( + model_name='interface', + name='qinq_svlan', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='%(class)ss_svlan', + to='ipam.vlan', + ), + ), + migrations.AlterField( + model_name='interface', + name='tagged_vlans', + field=models.ManyToManyField(blank=True, related_name='%(class)ss_as_tagged', to='ipam.vlan'), + ), + migrations.AlterField( + model_name='interface', + name='untagged_vlan', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='%(class)ss_as_untagged', + to='ipam.vlan', + ), + ), + ] diff --git a/netbox/dcim/migrations/0197_natural_sort_collation.py b/netbox/dcim/migrations/0197_natural_sort_collation.py new file mode 100644 index 000000000..268bda7eb --- /dev/null +++ b/netbox/dcim/migrations/0197_natural_sort_collation.py @@ -0,0 +1,16 @@ +from django.contrib.postgres.operations import CreateCollation +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0196_qinq_svlan'), + ] + + operations = [ + CreateCollation( + 'natural_sort', + provider='icu', + locale='und-u-kn-true', + ), + ] diff --git a/netbox/dcim/migrations/0198_natural_ordering.py b/netbox/dcim/migrations/0198_natural_ordering.py new file mode 100644 index 000000000..cf4361a2b --- /dev/null +++ b/netbox/dcim/migrations/0198_natural_ordering.py @@ -0,0 +1,317 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0197_natural_sort_collation'), + ] + + operations = [ + migrations.AlterModelOptions( + name='site', + options={'ordering': ('name',)}, + ), + migrations.AlterField( + model_name='site', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=100, unique=True), + ), + migrations.AlterModelOptions( + name='consoleport', + options={'ordering': ('device', 'name')}, + ), + migrations.AlterModelOptions( + name='consoleporttemplate', + options={'ordering': ('device_type', 'module_type', 'name')}, + ), + migrations.AlterModelOptions( + name='consoleserverport', + options={'ordering': ('device', 'name')}, + ), + migrations.AlterModelOptions( + name='consoleserverporttemplate', + options={'ordering': ('device_type', 'module_type', 'name')}, + ), + migrations.AlterModelOptions( + name='device', + options={'ordering': ('name', 'pk')}, + ), + migrations.AlterModelOptions( + name='devicebay', + options={'ordering': ('device', 'name')}, + ), + migrations.AlterModelOptions( + name='devicebaytemplate', + options={'ordering': ('device_type', 'name')}, + ), + migrations.AlterModelOptions( + name='frontport', + options={'ordering': ('device', 'name')}, + ), + migrations.AlterModelOptions( + name='frontporttemplate', + options={'ordering': ('device_type', 'module_type', 'name')}, + ), + migrations.AlterModelOptions( + name='interfacetemplate', + options={'ordering': ('device_type', 'module_type', 'name')}, + ), + migrations.AlterModelOptions( + name='inventoryitem', + options={'ordering': ('device__id', 'parent__id', 'name')}, + ), + migrations.AlterModelOptions( + name='inventoryitemtemplate', + options={'ordering': ('device_type__id', 'parent__id', 'name')}, + ), + migrations.AlterModelOptions( + name='modulebay', + options={'ordering': ('device', 'name')}, + ), + migrations.AlterModelOptions( + name='modulebaytemplate', + options={'ordering': ('device_type', 'module_type', 'name')}, + ), + migrations.AlterModelOptions( + name='poweroutlet', + options={'ordering': ('device', 'name')}, + ), + migrations.AlterModelOptions( + name='poweroutlettemplate', + options={'ordering': ('device_type', 'module_type', 'name')}, + ), + migrations.AlterModelOptions( + name='powerport', + options={'ordering': ('device', 'name')}, + ), + migrations.AlterModelOptions( + name='powerporttemplate', + options={'ordering': ('device_type', 'module_type', 'name')}, + ), + migrations.AlterModelOptions( + name='rack', + options={'ordering': ('site', 'location', 'name', 'pk')}, + ), + migrations.AlterModelOptions( + name='rearport', + options={'ordering': ('device', 'name')}, + ), + migrations.AlterModelOptions( + name='rearporttemplate', + options={'ordering': ('device_type', 'module_type', 'name')}, + ), + migrations.RemoveField( + model_name='consoleport', + name='_name', + ), + migrations.RemoveField( + model_name='consoleporttemplate', + name='_name', + ), + migrations.RemoveField( + model_name='consoleserverport', + name='_name', + ), + migrations.RemoveField( + model_name='consoleserverporttemplate', + name='_name', + ), + migrations.RemoveField( + model_name='device', + name='_name', + ), + migrations.RemoveField( + model_name='devicebay', + name='_name', + ), + migrations.RemoveField( + model_name='devicebaytemplate', + name='_name', + ), + migrations.RemoveField( + model_name='frontport', + name='_name', + ), + migrations.RemoveField( + model_name='frontporttemplate', + name='_name', + ), + migrations.RemoveField( + model_name='inventoryitem', + name='_name', + ), + migrations.RemoveField( + model_name='inventoryitemtemplate', + name='_name', + ), + migrations.RemoveField( + model_name='modulebay', + name='_name', + ), + migrations.RemoveField( + model_name='modulebaytemplate', + name='_name', + ), + migrations.RemoveField( + model_name='poweroutlet', + name='_name', + ), + migrations.RemoveField( + model_name='poweroutlettemplate', + name='_name', + ), + migrations.RemoveField( + model_name='powerport', + name='_name', + ), + migrations.RemoveField( + model_name='powerporttemplate', + name='_name', + ), + migrations.RemoveField( + model_name='rack', + name='_name', + ), + migrations.RemoveField( + model_name='rearport', + name='_name', + ), + migrations.RemoveField( + model_name='rearporttemplate', + name='_name', + ), + migrations.RemoveField( + model_name='site', + name='_name', + ), + migrations.AlterField( + model_name='consoleport', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='consoleporttemplate', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='consoleserverport', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='consoleserverporttemplate', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='device', + name='name', + field=models.CharField(blank=True, db_collation='natural_sort', max_length=64, null=True), + ), + migrations.AlterField( + model_name='devicebay', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='devicebaytemplate', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='frontport', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='frontporttemplate', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='interface', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='interfacetemplate', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='inventoryitem', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='inventoryitemtemplate', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='modulebay', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='modulebaytemplate', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='poweroutlet', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='poweroutlettemplate', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='powerport', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='powerporttemplate', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='rack', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=100), + ), + migrations.AlterField( + model_name='rearport', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='rearporttemplate', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='powerfeed', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=100), + ), + migrations.AlterField( + model_name='powerpanel', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=100), + ), + migrations.AlterField( + model_name='virtualchassis', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + migrations.AlterField( + model_name='virtualdevicecontext', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=64), + ), + ] diff --git a/netbox/dcim/migrations/0199_macaddress.py b/netbox/dcim/migrations/0199_macaddress.py new file mode 100644 index 000000000..ae18d5f63 --- /dev/null +++ b/netbox/dcim/migrations/0199_macaddress.py @@ -0,0 +1,51 @@ +import django.db.models.deletion +import taggit.managers +from django.db import migrations, models + +import dcim.fields +import utilities.json + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0198_natural_ordering'), + ('extras', '0122_charfield_null_choices'), + ] + + operations = [ + migrations.CreateModel( + name='MACAddress', + 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)), + ('mac_address', dcim.fields.MACAddressField()), + ('assigned_object_id', models.PositiveBigIntegerField(blank=True, null=True)), + ( + 'assigned_object_type', + models.ForeignKey( + blank=True, + limit_choices_to=models.Q( + models.Q( + models.Q(('app_label', 'dcim'), ('model', 'interface')), + models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), + _connector='OR', + ) + ), + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), + ), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={'abstract': False, 'ordering': ('mac_address',)}, + ), + ] diff --git a/netbox/dcim/migrations/0200_populate_mac_addresses.py b/netbox/dcim/migrations/0200_populate_mac_addresses.py new file mode 100644 index 000000000..0cd18d78e --- /dev/null +++ b/netbox/dcim/migrations/0200_populate_mac_addresses.py @@ -0,0 +1,46 @@ +import django.db.models.deletion +from django.db import migrations, models + + +def populate_mac_addresses(apps, schema_editor): + ContentType = apps.get_model('contenttypes', 'ContentType') + Interface = apps.get_model('dcim', 'Interface') + MACAddress = apps.get_model('dcim', 'MACAddress') + interface_ct = ContentType.objects.get_for_model(Interface) + + mac_addresses = [ + MACAddress( + mac_address=interface.mac_address, assigned_object_type=interface_ct, assigned_object_id=interface.pk + ) + for interface in Interface.objects.filter(mac_address__isnull=False) + ] + MACAddress.objects.bulk_create(mac_addresses, batch_size=100) + + # TODO: Optimize interface updates + for mac_address in mac_addresses: + Interface.objects.filter(pk=mac_address.assigned_object_id).update(primary_mac_address=mac_address) + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0199_macaddress'), + ] + + operations = [ + migrations.AddField( + model_name='interface', + name='primary_mac_address', + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='dcim.macaddress', + ), + ), + migrations.RunPython(code=populate_mac_addresses, reverse_code=migrations.RunPython.noop), + migrations.RemoveField( + model_name='interface', + name='mac_address', + ), + ] diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 96861a13e..744ec025c 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -42,7 +42,8 @@ class Cable(PrimaryModel): verbose_name=_('type'), max_length=50, choices=CableTypeChoices, - blank=True + blank=True, + null=True ) status = models.CharField( verbose_name=_('status'), @@ -78,6 +79,7 @@ class Cable(PrimaryModel): max_length=50, choices=CableLengthUnitChoices, blank=True, + null=True ) # Stores the normalized length (in meters) for database ordering _abs_length = models.DecimalField( @@ -160,7 +162,7 @@ class Cable(PrimaryModel): if self.length is not None and not self.length_unit: raise ValidationError(_("Must specify a unit when setting a cable length")) - if self._state.adding and (not self.a_terminations or not self.b_terminations): + if self._state.adding and self.pk is None and (not self.a_terminations or not self.b_terminations): raise ValidationError(_("Must define A and B terminations when creating a new cable.")) if self._terminations_modified: @@ -206,7 +208,7 @@ class Cable(PrimaryModel): # Clear length_unit if no length is defined if self.length is None: - self.length_unit = '' + self.length_unit = None super().save(*args, **kwargs) @@ -342,7 +344,7 @@ class CableTermination(ChangeLoggedModel): ) # A CircuitTermination attached to a ProviderNetwork cannot have a Cable - if self.termination_type.model == 'circuittermination' and self.termination.provider_network is not None: + if self.termination_type.model == 'circuittermination' and self.termination._provider_network is not None: raise ValidationError(_("Circuit terminations attached to a provider network may not be cabled.")) def save(self, *args, **kwargs): @@ -365,7 +367,7 @@ class CableTermination(ChangeLoggedModel): termination = self.termination._meta.model.objects.get(pk=self.termination_id) termination.snapshot() termination.cable = None - termination.cable_end = '' + termination.cable_end = None termination.save() super().delete(*args, **kwargs) @@ -688,19 +690,19 @@ class CablePath(models.Model): ).first() if circuit_termination is None: break - elif circuit_termination.provider_network: + elif circuit_termination._provider_network: # Circuit terminates to a ProviderNetwork path.extend([ [object_to_path_node(circuit_termination)], - [object_to_path_node(circuit_termination.provider_network)], + [object_to_path_node(circuit_termination._provider_network)], ]) is_complete = True break - elif circuit_termination.site and not circuit_termination.cable: - # Circuit terminates to a Site + elif circuit_termination.termination and not circuit_termination.cable: + # Circuit terminates to a Region/Site/etc. path.extend([ [object_to_path_node(circuit_termination)], - [object_to_path_node(circuit_termination.site)], + [object_to_path_node(circuit_termination.termination)], ]) break diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index 3a71c424d..b4f057711 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -44,12 +44,8 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin): max_length=64, help_text=_( "{module} is accepted as a substitution for the module bay position when attached to a module type." - ) - ) - _name = NaturalOrderingField( - target_field='name', - max_length=100, - blank=True + ), + db_collation="natural_sort" ) label = models.CharField( verbose_name=_('label'), @@ -65,7 +61,7 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin): class Meta: abstract = True - ordering = ('device_type', '_name') + ordering = ('device_type', 'name') constraints = ( models.UniqueConstraint( fields=('device_type', 'name'), @@ -125,7 +121,7 @@ class ModularComponentTemplateModel(ComponentTemplateModel): class Meta: abstract = True - ordering = ('device_type', 'module_type', '_name') + ordering = ('device_type', 'module_type', 'name') constraints = ( models.UniqueConstraint( fields=('device_type', 'name'), @@ -203,7 +199,8 @@ class ConsolePortTemplate(ModularComponentTemplateModel): verbose_name=_('type'), max_length=50, choices=ConsolePortTypeChoices, - blank=True + blank=True, + null=True ) component_model = ConsolePort @@ -237,7 +234,8 @@ class ConsoleServerPortTemplate(ModularComponentTemplateModel): verbose_name=_('type'), max_length=50, choices=ConsolePortTypeChoices, - blank=True + blank=True, + null=True ) component_model = ConsoleServerPort @@ -272,7 +270,8 @@ class PowerPortTemplate(ModularComponentTemplateModel): verbose_name=_('type'), max_length=50, choices=PowerPortTypeChoices, - blank=True + blank=True, + null=True ) maximum_draw = models.PositiveIntegerField( verbose_name=_('maximum draw'), @@ -312,7 +311,9 @@ class PowerPortTemplate(ModularComponentTemplateModel): if self.maximum_draw is not None and self.allocated_draw is not None: if self.allocated_draw > self.maximum_draw: raise ValidationError({ - 'allocated_draw': _("Allocated draw cannot exceed the maximum draw ({maximum_draw}W).").format(maximum_draw=self.maximum_draw) + 'allocated_draw': _( + "Allocated draw cannot exceed the maximum draw ({maximum_draw}W)." + ).format(maximum_draw=self.maximum_draw) }) def to_yaml(self): @@ -334,7 +335,8 @@ class PowerOutletTemplate(ModularComponentTemplateModel): verbose_name=_('type'), max_length=50, choices=PowerOutletTypeChoices, - blank=True + blank=True, + null=True ) power_port = models.ForeignKey( to='dcim.PowerPortTemplate', @@ -348,6 +350,7 @@ class PowerOutletTemplate(ModularComponentTemplateModel): max_length=50, choices=PowerOutletFeedLegChoices, blank=True, + null=True, help_text=_('Phase (for three-phase feeds)') ) @@ -364,11 +367,15 @@ class PowerOutletTemplate(ModularComponentTemplateModel): if self.power_port: if self.device_type and self.power_port.device_type != self.device_type: raise ValidationError( - _("Parent power port ({power_port}) must belong to the same device type").format(power_port=self.power_port) + _("Parent power port ({power_port}) must belong to the same device type").format( + power_port=self.power_port + ) ) if self.module_type and self.power_port.module_type != self.module_type: raise ValidationError( - _("Parent power port ({power_port}) must belong to the same module type").format(power_port=self.power_port) + _("Parent power port ({power_port}) must belong to the same module type").format( + power_port=self.power_port + ) ) def instantiate(self, **kwargs): @@ -434,18 +441,21 @@ class InterfaceTemplate(ModularComponentTemplateModel): max_length=50, choices=InterfacePoEModeChoices, blank=True, + null=True, verbose_name=_('PoE mode') ) poe_type = models.CharField( max_length=50, choices=InterfacePoETypeChoices, blank=True, + null=True, verbose_name=_('PoE type') ) rf_role = models.CharField( max_length=30, choices=WirelessRoleChoices, blank=True, + null=True, verbose_name=_('wireless role') ) @@ -463,11 +473,15 @@ class InterfaceTemplate(ModularComponentTemplateModel): raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")}) if self.device_type and self.device_type != self.bridge.device_type: raise ValidationError({ - 'bridge': _("Bridge interface ({bridge}) must belong to the same device type").format(bridge=self.bridge) + 'bridge': _( + "Bridge interface ({bridge}) must belong to the same device type" + ).format(bridge=self.bridge) }) if self.module_type and self.module_type != self.bridge.module_type: raise ValidationError({ - 'bridge': _("Bridge interface ({bridge}) must belong to the same module type").format(bridge=self.bridge) + 'bridge': _( + "Bridge interface ({bridge}) must belong to the same module type" + ).format(bridge=self.bridge) }) if self.rf_role and self.type not in WIRELESS_IFACE_TYPES: @@ -710,7 +724,9 @@ class DeviceBayTemplate(ComponentTemplateModel): def clean(self): if self.device_type and self.device_type.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT: raise ValidationError( - _("Subdevice role of device type ({device_type}) must be set to \"parent\" to allow device bays.").format(device_type=self.device_type) + _( + 'Subdevice role of device type ({device_type}) must be set to "parent" to allow device bays.' + ).format(device_type=self.device_type) ) def to_yaml(self): @@ -774,7 +790,7 @@ class InventoryItemTemplate(MPTTModel, ComponentTemplateModel): component_model = InventoryItem class Meta: - ordering = ('device_type__id', 'parent__id', '_name') + ordering = ('device_type__id', 'parent__id', 'name') indexes = ( models.Index(fields=('component_type', 'component_id')), ) diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 1a86a250c..ce9e5607f 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -10,7 +10,7 @@ from mptt.models import MPTTModel, TreeForeignKey from dcim.choices import * from dcim.constants import * -from dcim.fields import MACAddressField, WWNField +from dcim.fields import WWNField from netbox.choices import ColorChoices from netbox.models import OrganizationalModel, NetBoxModel from utilities.fields import ColorField, NaturalOrderingField @@ -50,12 +50,8 @@ class ComponentModel(NetBoxModel): ) name = models.CharField( verbose_name=_('name'), - max_length=64 - ) - _name = NaturalOrderingField( - target_field='name', - max_length=100, - blank=True + max_length=64, + db_collation="natural_sort" ) label = models.CharField( verbose_name=_('label'), @@ -71,7 +67,7 @@ class ComponentModel(NetBoxModel): class Meta: abstract = True - ordering = ('device', '_name') + ordering = ('device', 'name') constraints = ( models.UniqueConstraint( fields=('device', 'name'), @@ -142,8 +138,9 @@ class CabledObjectModel(models.Model): cable_end = models.CharField( verbose_name=_('cable end'), max_length=1, + choices=CableEndChoices, blank=True, - choices=CableEndChoices + null=True ) mark_connected = models.BooleanField( verbose_name=_('mark connected'), @@ -283,6 +280,7 @@ class ConsolePort(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracki max_length=50, choices=ConsolePortTypeChoices, blank=True, + null=True, help_text=_('Physical port type') ) speed = models.PositiveIntegerField( @@ -309,6 +307,7 @@ class ConsoleServerPort(ModularComponentModel, CabledObjectModel, PathEndpoint, max_length=50, choices=ConsolePortTypeChoices, blank=True, + null=True, help_text=_('Physical port type') ) speed = models.PositiveIntegerField( @@ -339,6 +338,7 @@ class PowerPort(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracking max_length=50, choices=PowerPortTypeChoices, blank=True, + null=True, help_text=_('Physical port type') ) maximum_draw = models.PositiveIntegerField( @@ -454,6 +454,7 @@ class PowerOutlet(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracki max_length=50, choices=PowerOutletTypeChoices, blank=True, + null=True, help_text=_('Physical port type') ) power_port = models.ForeignKey( @@ -468,6 +469,7 @@ class PowerOutlet(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracki max_length=50, choices=PowerOutletFeedLegChoices, blank=True, + null=True, help_text=_('Phase (for three-phase feeds)') ) color = ColorField( @@ -503,11 +505,6 @@ class BaseInterface(models.Model): verbose_name=_('enabled'), default=True ) - mac_address = MACAddressField( - null=True, - blank=True, - verbose_name=_('MAC address') - ) mtu = models.PositiveIntegerField( blank=True, null=True, @@ -522,6 +519,7 @@ class BaseInterface(models.Model): max_length=50, choices=InterfaceModeChoices, blank=True, + null=True, help_text=_('IEEE 802.1Q tagging strategy') ) parent = models.ForeignKey( @@ -540,10 +538,64 @@ class BaseInterface(models.Model): blank=True, verbose_name=_('bridge interface') ) + untagged_vlan = models.ForeignKey( + to='ipam.VLAN', + on_delete=models.SET_NULL, + related_name='%(class)ss_as_untagged', + null=True, + blank=True, + verbose_name=_('untagged VLAN') + ) + tagged_vlans = models.ManyToManyField( + to='ipam.VLAN', + related_name='%(class)ss_as_tagged', + blank=True, + verbose_name=_('tagged VLANs') + ) + qinq_svlan = models.ForeignKey( + to='ipam.VLAN', + on_delete=models.SET_NULL, + related_name='%(class)ss_svlan', + null=True, + blank=True, + verbose_name=_('Q-in-Q SVLAN') + ) + vlan_translation_policy = models.ForeignKey( + to='ipam.VLANTranslationPolicy', + on_delete=models.PROTECT, + null=True, + blank=True, + verbose_name=_('VLAN Translation Policy') + ) + primary_mac_address = models.OneToOneField( + to='dcim.MACAddress', + on_delete=models.SET_NULL, + related_name='+', + blank=True, + null=True, + verbose_name=_('primary MAC address') + ) class Meta: abstract = True + def clean(self): + super().clean() + + # SVLAN can be defined only for Q-in-Q interfaces + if self.qinq_svlan and self.mode != InterfaceModeChoices.MODE_Q_IN_Q: + raise ValidationError({ + 'qinq_svlan': _("Only Q-in-Q interfaces may specify a service VLAN.") + }) + + # Check that the primary MAC address (if any) is assigned to this interface + if self.primary_mac_address and self.primary_mac_address.assigned_object != self: + raise ValidationError({ + 'primary_mac_address': _("MAC address {mac_address} is not assigned to this interface.").format( + mac_address=self.primary_mac_address + ) + }) + def save(self, *args, **kwargs): # Remove untagged VLAN assignment for non-802.1Q interfaces @@ -568,6 +620,11 @@ class BaseInterface(models.Model): def count_fhrp_groups(self): return self.fhrp_group_assignments.count() + @cached_property + def mac_address(self): + if self.primary_mac_address: + return self.primary_mac_address.mac_address + class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEndpoint, TrackingModelMixin): """ @@ -624,12 +681,14 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd max_length=30, choices=WirelessRoleChoices, blank=True, + null=True, verbose_name=_('wireless role') ) rf_channel = models.CharField( max_length=50, choices=WirelessChannelChoices, blank=True, + null=True, verbose_name=_('wireless channel') ) rf_channel_frequency = models.DecimalField( @@ -658,12 +717,14 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd max_length=50, choices=InterfacePoEModeChoices, blank=True, + null=True, verbose_name=_('PoE mode') ) poe_type = models.CharField( max_length=50, choices=InterfacePoETypeChoices, blank=True, + null=True, verbose_name=_('PoE type') ) wireless_link = models.ForeignKey( @@ -679,20 +740,6 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd blank=True, verbose_name=_('wireless LANs') ) - untagged_vlan = models.ForeignKey( - to='ipam.VLAN', - on_delete=models.SET_NULL, - related_name='interfaces_as_untagged', - null=True, - blank=True, - verbose_name=_('untagged VLAN') - ) - tagged_vlans = models.ManyToManyField( - to='ipam.VLAN', - related_name='interfaces_as_tagged', - blank=True, - verbose_name=_('tagged VLANs') - ) vrf = models.ForeignKey( to='ipam.VRF', on_delete=models.SET_NULL, @@ -707,6 +754,12 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd object_id_field='assigned_object_id', related_query_name='interface' ) + mac_addresses = GenericRelation( + to='dcim.MACAddress', + content_type_field='assigned_object_type', + object_id_field='assigned_object_id', + related_query_name='interface' + ) fhrp_group_assignments = GenericRelation( to='ipam.FHRPGroupAssignment', content_type_field='interface_type', @@ -945,6 +998,14 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd def l2vpn_termination(self): return self.l2vpn_terminations.first() + @cached_property + def connected_endpoints(self): + # If this is a virtual interface, return the remote endpoint of the connected + # virtual circuit, if any. + if self.is_virtual and hasattr(self, 'virtual_circuit_termination'): + return self.virtual_circuit_termination.peer_terminations + return super().connected_endpoints + # # Pass-through ports @@ -1266,7 +1327,7 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin): clone_fields = ('device', 'parent', 'role', 'manufacturer', 'status', 'part_id') class Meta: - ordering = ('device__id', 'parent__id', '_name') + ordering = ('device__id', 'parent__id', 'name') indexes = ( models.Index(fields=('component_type', 'component_id')), ) diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index e472303a6..1fbffa54b 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -3,6 +3,7 @@ import yaml from functools import cached_property +from django.contrib.contenttypes.fields import GenericForeignKey from django.core.exceptions import ValidationError from django.core.files.storage import default_storage from django.core.validators import MaxValueValidator, MinValueValidator @@ -16,6 +17,7 @@ from django.utils.translation import gettext_lazy as _ from dcim.choices import * from dcim.constants import * +from dcim.fields import MACAddressField from extras.models import ConfigContextModel, CustomField from extras.querysets import ConfigContextModelQuerySet from netbox.choices import ColorChoices @@ -23,7 +25,7 @@ from netbox.config import ConfigItem from netbox.models import OrganizationalModel, PrimaryModel from netbox.models.mixins import WeightMixin from netbox.models.features import ContactsMixin, ImageAttachmentsMixin -from utilities.fields import ColorField, CounterCacheField, NaturalOrderingField +from utilities.fields import ColorField, CounterCacheField from utilities.tracking import TrackingModelMixin from .device_components import * from .mixins import RenderConfigMixin @@ -33,6 +35,7 @@ __all__ = ( 'Device', 'DeviceRole', 'DeviceType', + 'MACAddress', 'Manufacturer', 'Module', 'ModuleType', @@ -118,6 +121,7 @@ class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): max_length=50, choices=SubdeviceRoleChoices, blank=True, + null=True, verbose_name=_('parent/child status'), help_text=_('Parent devices house child devices in device bays. Leave blank ' 'if this device type is neither a parent nor a child.') @@ -126,7 +130,8 @@ class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): verbose_name=_('airflow'), max_length=50, choices=DeviceAirflowChoices, - blank=True + blank=True, + null=True ) front_image = models.ImageField( upload_to='devicetype-images', @@ -387,7 +392,8 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): verbose_name=_('airflow'), max_length=50, choices=ModuleAirflowChoices, - blank=True + blank=True, + null=True ) clone_fields = ('manufacturer', 'weight', 'weight_unit', 'airflow') @@ -526,7 +532,10 @@ def update_interface_bridges(device, interface_templates, module=None): interface = Interface.objects.get(device=device, name=interface_template.resolve_name(module=module)) if interface_template.bridge: - interface.bridge = Interface.objects.get(device=device, name=interface_template.bridge.resolve_name(module=module)) + interface.bridge = Interface.objects.get( + device=device, + name=interface_template.bridge.resolve_name(module=module) + ) interface.full_clean() interface.save() @@ -579,13 +588,8 @@ class Device( verbose_name=_('name'), max_length=64, blank=True, - null=True - ) - _name = NaturalOrderingField( - target_field='name', - max_length=100, - blank=True, - null=True + null=True, + db_collation="natural_sort" ) serial = models.CharField( max_length=50, @@ -632,6 +636,7 @@ class Device( face = models.CharField( max_length=50, blank=True, + null=True, choices=DeviceFaceChoices, verbose_name=_('rack face') ) @@ -645,7 +650,8 @@ class Device( verbose_name=_('airflow'), max_length=50, choices=DeviceAirflowChoices, - blank=True + blank=True, + null=True ) primary_ip4 = models.OneToOneField( to='ipam.IPAddress', @@ -770,7 +776,7 @@ class Device( ) class Meta: - ordering = ('_name', 'pk') # Name may be null + ordering = ('name', 'pk') # Name may be null constraints = ( models.UniqueConstraint( Lower('name'), 'site', 'tenant', @@ -906,7 +912,10 @@ class Device( }) if self.primary_ip4.assigned_object in vc_interfaces: pass - elif self.primary_ip4.nat_inside is not None and self.primary_ip4.nat_inside.assigned_object in vc_interfaces: + elif ( + self.primary_ip4.nat_inside is not None and + self.primary_ip4.nat_inside.assigned_object in vc_interfaces + ): pass else: raise ValidationError({ @@ -921,7 +930,10 @@ class Device( }) if self.primary_ip6.assigned_object in vc_interfaces: pass - elif self.primary_ip6.nat_inside is not None and self.primary_ip6.nat_inside.assigned_object in vc_interfaces: + elif ( + self.primary_ip6.nat_inside is not None and + self.primary_ip6.nat_inside.assigned_object in vc_interfaces + ): pass else: raise ValidationError({ @@ -953,10 +965,17 @@ class Device( }) # A Device can only be assigned to a Cluster in the same Site (or no Site) - if self.cluster and self.cluster.site is not None and self.cluster.site != self.site: + if self.cluster and self.cluster._site is not None and self.cluster._site != self.site: raise ValidationError({ 'cluster': _("The assigned cluster belongs to a different site ({site})").format( - site=self.cluster.site + site=self.cluster._site + ) + }) + + if self.cluster and self.cluster._location is not None and self.cluster._location != self.location: + raise ValidationError({ + 'cluster': _("The assigned cluster belongs to a different location ({location})").format( + site=self.cluster._location ) }) @@ -968,9 +987,10 @@ class Device( if hasattr(self, 'vc_master_for') and self.vc_master_for and self.vc_master_for != self.virtual_chassis: raise ValidationError({ - 'virtual_chassis': _('Device cannot be removed from virtual chassis {virtual_chassis} because it is currently designated as its master.').format( - virtual_chassis=self.vc_master_for - ) + 'virtual_chassis': _( + 'Device cannot be removed from virtual chassis {virtual_chassis} because it is currently ' + 'designated as its master.' + ).format(virtual_chassis=self.vc_master_for) }) def _instantiate_components(self, queryset, bulk_create=True): @@ -1308,7 +1328,8 @@ class VirtualChassis(PrimaryModel): ) name = models.CharField( verbose_name=_('name'), - max_length=64 + max_length=64, + db_collation="natural_sort" ) domain = models.CharField( verbose_name=_('domain'), @@ -1370,7 +1391,8 @@ class VirtualDeviceContext(PrimaryModel): ) name = models.CharField( verbose_name=_('name'), - max_length=64 + max_length=64, + db_collation="natural_sort" ) status = models.CharField( verbose_name=_('status'), @@ -1461,3 +1483,37 @@ class VirtualDeviceContext(PrimaryModel): raise ValidationError({ f'primary_ip{family}': _('Primary IP address must belong to an interface on the assigned device.') }) + + +# +# Addressing +# + +class MACAddress(PrimaryModel): + mac_address = MACAddressField( + verbose_name=_('MAC address') + ) + assigned_object_type = models.ForeignKey( + to='contenttypes.ContentType', + limit_choices_to=MACADDRESS_ASSIGNMENT_MODELS, + on_delete=models.PROTECT, + related_name='+', + blank=True, + null=True + ) + assigned_object_id = models.PositiveBigIntegerField( + blank=True, + null=True + ) + assigned_object = GenericForeignKey( + ct_field='assigned_object_type', + fk_field='assigned_object_id' + ) + + class Meta: + ordering = ('mac_address',) + verbose_name = _('MAC address') + verbose_name_plural = _('MAC addresses') + + def __str__(self): + return str(self.mac_address) diff --git a/netbox/dcim/models/mixins.py b/netbox/dcim/models/mixins.py index c9be451a0..ac4d7dab9 100644 --- a/netbox/dcim/models/mixins.py +++ b/netbox/dcim/models/mixins.py @@ -1,6 +1,10 @@ +from django.apps import apps +from django.contrib.contenttypes.fields import GenericForeignKey from django.db import models +from dcim.constants import LOCATION_SCOPE_TYPES __all__ = ( + 'CachedScopeMixin', 'RenderConfigMixin', ) @@ -27,3 +31,80 @@ class RenderConfigMixin(models.Model): return self.role.config_template if self.platform and self.platform.config_template: return self.platform.config_template + + +class CachedScopeMixin(models.Model): + """ + Mixin for adding a GenericForeignKey scope to a model that can point to a Region, SiteGroup, Site, or Location. + Includes cached fields for each to allow efficient filtering. Appropriate validation must be done in the clean() + method as this does not have any as validation is generally model-specific. + """ + scope_type = models.ForeignKey( + to='contenttypes.ContentType', + on_delete=models.PROTECT, + limit_choices_to=models.Q(model__in=LOCATION_SCOPE_TYPES), + related_name='+', + blank=True, + null=True + ) + scope_id = models.PositiveBigIntegerField( + blank=True, + null=True + ) + scope = GenericForeignKey( + ct_field='scope_type', + fk_field='scope_id' + ) + + _location = models.ForeignKey( + to='dcim.Location', + on_delete=models.CASCADE, + blank=True, + null=True + ) + _site = models.ForeignKey( + to='dcim.Site', + on_delete=models.CASCADE, + blank=True, + null=True + ) + _region = models.ForeignKey( + to='dcim.Region', + on_delete=models.CASCADE, + blank=True, + null=True + ) + _site_group = models.ForeignKey( + to='dcim.SiteGroup', + on_delete=models.CASCADE, + blank=True, + null=True + ) + + class Meta: + abstract = True + + def save(self, *args, **kwargs): + # Cache objects associated with the terminating object (for filtering) + self.cache_related_objects() + + super().save(*args, **kwargs) + + def cache_related_objects(self): + self._region = self._site_group = self._site = self._location = None + if self.scope_type: + scope_type = self.scope_type.model_class() + if scope_type == apps.get_model('dcim', 'region'): + self._region = self.scope + elif scope_type == apps.get_model('dcim', 'sitegroup'): + self._site_group = self.scope + elif scope_type == apps.get_model('dcim', 'site'): + self._region = self.scope.region + self._site_group = self.scope.group + self._site = self.scope + elif scope_type == apps.get_model('dcim', 'location'): + self._region = self.scope.site.region + self._site_group = self.scope.site.group + self._site = self.scope.site + self._location = self.scope + cache_related_objects.alters_data = True diff --git a/netbox/dcim/models/power.py b/netbox/dcim/models/power.py index d0c6b18b6..284cfe832 100644 --- a/netbox/dcim/models/power.py +++ b/netbox/dcim/models/power.py @@ -36,7 +36,8 @@ class PowerPanel(ContactsMixin, ImageAttachmentsMixin, PrimaryModel): ) name = models.CharField( verbose_name=_('name'), - max_length=100 + max_length=100, + db_collation="natural_sort" ) prerequisite_models = ( @@ -86,7 +87,8 @@ class PowerFeed(PrimaryModel, PathEndpoint, CabledObjectModel): ) name = models.CharField( verbose_name=_('name'), - max_length=100 + max_length=100, + db_collation="natural_sort" ) status = models.CharField( verbose_name=_('status'), diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index ae5513fea..78eb0ea4a 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -19,7 +19,7 @@ from netbox.models.mixins import WeightMixin from netbox.models.features import ContactsMixin, ImageAttachmentsMixin from utilities.conversion import to_grams from utilities.data import array_to_string, drange -from utilities.fields import ColorField, NaturalOrderingField +from utilities.fields import ColorField from .device_components import PowerPort from .devices import Device, Module from .power import PowerFeed @@ -83,7 +83,8 @@ class RackBase(WeightMixin, PrimaryModel): verbose_name=_('outer unit'), max_length=50, choices=RackDimensionUnitChoices, - blank=True + blank=True, + null=True ) mounting_depth = models.PositiveSmallIntegerField( verbose_name=_('mounting depth'), @@ -188,7 +189,7 @@ class RackType(RackBase): # Clear unit if outer width & depth are not set if self.outer_width is None and self.outer_depth is None: - self.outer_unit = '' + self.outer_unit = None super().save(*args, **kwargs) @@ -242,6 +243,7 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase): choices=RackFormFactorChoices, max_length=50, blank=True, + null=True, verbose_name=_('form factor') ) rack_type = models.ForeignKey( @@ -253,12 +255,8 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase): ) name = models.CharField( verbose_name=_('name'), - max_length=100 - ) - _name = NaturalOrderingField( - target_field='name', max_length=100, - blank=True + db_collation="natural_sort" ) facility_id = models.CharField( max_length=50, @@ -317,7 +315,8 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase): verbose_name=_('airflow'), max_length=50, choices=RackAirflowChoices, - blank=True + blank=True, + null=True ) # Generic relations @@ -337,7 +336,7 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase): ) class Meta: - ordering = ('site', 'location', '_name', 'pk') # (site, location, name) may be non-unique + ordering = ('site', 'location', 'name', 'pk') # (site, location, name) may be non-unique constraints = ( # Name and facility_id must be unique *only* within a Location models.UniqueConstraint( @@ -380,7 +379,9 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase): min_height = top_device.position + top_device.device_type.u_height - self.starting_unit if self.u_height < min_height: raise ValidationError({ - 'u_height': _("Rack must be at least {min_height}U tall to house currently installed devices.").format(min_height=min_height) + 'u_height': _( + "Rack must be at least {min_height}U tall to house currently installed devices." + ).format(min_height=min_height) }) # Validate that the Rack's starting unit is less than or equal to the position of the lowest mounted Device @@ -409,7 +410,7 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase): # Clear unit if outer width & depth are not set if self.outer_width is None and self.outer_depth is None: - self.outer_unit = '' + self.outer_unit = None super().save(*args, **kwargs) diff --git a/netbox/dcim/models/sites.py b/netbox/dcim/models/sites.py index 37f59045d..7880a067f 100644 --- a/netbox/dcim/models/sites.py +++ b/netbox/dcim/models/sites.py @@ -8,7 +8,6 @@ from dcim.choices import * from dcim.constants import * from netbox.models import NestedGroupModel, PrimaryModel from netbox.models.features import ContactsMixin, ImageAttachmentsMixin -from utilities.fields import NaturalOrderingField __all__ = ( 'Location', @@ -28,6 +27,12 @@ class Region(ContactsMixin, NestedGroupModel): states, and/or cities. Regions are recursively nested into a hierarchy: all sites belonging to a child region are also considered to be members of its parent and ancestor region(s). """ + prefixes = GenericRelation( + to='ipam.Prefix', + content_type_field='scope_type', + object_id_field='scope_id', + related_query_name='region' + ) vlan_groups = GenericRelation( to='ipam.VLANGroup', content_type_field='scope_type', @@ -78,6 +83,12 @@ class SiteGroup(ContactsMixin, NestedGroupModel): within corporate sites you might distinguish between offices and data centers. Like regions, site groups can be nested recursively to form a hierarchy. """ + prefixes = GenericRelation( + to='ipam.Prefix', + content_type_field='scope_type', + object_id_field='scope_id', + related_query_name='site_group' + ) vlan_groups = GenericRelation( to='ipam.VLANGroup', content_type_field='scope_type', @@ -131,12 +142,8 @@ class Site(ContactsMixin, ImageAttachmentsMixin, PrimaryModel): verbose_name=_('name'), max_length=100, unique=True, - help_text=_("Full name of the site") - ) - _name = NaturalOrderingField( - target_field='name', - max_length=100, - blank=True + help_text=_("Full name of the site"), + db_collation="natural_sort" ) slug = models.SlugField( verbose_name=_('slug'), @@ -182,7 +189,8 @@ class Site(ContactsMixin, ImageAttachmentsMixin, PrimaryModel): blank=True ) time_zone = TimeZoneField( - blank=True + blank=True, + null=True ) physical_address = models.CharField( verbose_name=_('physical address'), @@ -214,6 +222,12 @@ class Site(ContactsMixin, ImageAttachmentsMixin, PrimaryModel): ) # Generic relations + prefixes = GenericRelation( + to='ipam.Prefix', + content_type_field='scope_type', + object_id_field='scope_id', + related_query_name='site' + ) vlan_groups = GenericRelation( to='ipam.VLANGroup', content_type_field='scope_type', @@ -227,7 +241,7 @@ class Site(ContactsMixin, ImageAttachmentsMixin, PrimaryModel): ) class Meta: - ordering = ('_name',) + ordering = ('name',) verbose_name = _('site') verbose_name_plural = _('sites') @@ -273,6 +287,12 @@ class Location(ContactsMixin, ImageAttachmentsMixin, NestedGroupModel): ) # Generic relations + prefixes = GenericRelation( + to='ipam.Prefix', + content_type_field='scope_type', + object_id_field='scope_id', + related_query_name='location' + ) vlan_groups = GenericRelation( to='ipam.VLANGroup', content_type_field='scope_type', diff --git a/netbox/dcim/search.py b/netbox/dcim/search.py index 45431cb05..6b03d8b43 100644 --- a/netbox/dcim/search.py +++ b/netbox/dcim/search.py @@ -98,19 +98,28 @@ class FrontPortIndex(SearchIndex): display_attrs = ('device', 'label', 'type', 'description') +@register_search +class MACAddressIndex(SearchIndex): + model = models.MACAddress + fields = ( + ('mac_address', 100), + ('description', 500), + ) + display_attrs = ('mac_address', 'interface') + + @register_search class InterfaceIndex(SearchIndex): model = models.Interface fields = ( ('name', 100), ('label', 200), - ('mac_address', 300), ('wwn', 300), ('description', 500), ('mtu', 2000), ('speed', 2000), ) - display_attrs = ('device', 'label', 'type', 'mac_address', 'wwn', 'description') + display_attrs = ('device', 'label', 'type', 'wwn', 'description') @register_search diff --git a/netbox/dcim/svg/racks.py b/netbox/dcim/svg/racks.py index 0f73095b5..81f8ad3a5 100644 --- a/netbox/dcim/svg/racks.py +++ b/netbox/dcim/svg/racks.py @@ -153,7 +153,10 @@ class RackElevationSVG: if self.rack.desc_units: y += int((position - self.rack.starting_unit) * self.unit_height) else: - y += int((self.rack.u_height - position + self.rack.starting_unit) * self.unit_height) - int(height * self.unit_height) + y += ( + int((self.rack.u_height - position + self.rack.starting_unit) * self.unit_height) - + int(height * self.unit_height) + ) return x, y diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index b39a2b87f..963150a7a 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -29,6 +29,7 @@ __all__ = ( 'InterfaceTable', 'InventoryItemRoleTable', 'InventoryItemTable', + 'MACAddressTable', 'ModuleBayTable', 'PlatformTable', 'PowerOutletTable', @@ -42,6 +43,16 @@ MODULEBAY_STATUS = """ {% badge record.installed_module.get_status_display bg_color=record.installed_module.get_status_color %} """ +MACADDRESS_LINK = """ +{% if record.pk %} + {{ record.mac_address }} +{% endif %} +""" + +MACADDRESS_COPY_BUTTON = """ +{% copy_content record.pk prefix="macaddress_" %} +""" + # # Device roles @@ -132,7 +143,6 @@ class PlatformTable(NetBoxTable): class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): name = tables.TemplateColumn( verbose_name=_('Name'), - order_by=('_name',), template_code=DEVICE_LINK, linkify=True ) @@ -288,7 +298,6 @@ class DeviceComponentTable(NetBoxTable): name = tables.Column( verbose_name=_('Name'), linkify=True, - order_by=('_name',) ) device_status = columns.ChoiceFieldColumn( accessor=tables.A('device__status'), @@ -382,7 +391,8 @@ class ConsolePortTable(ModularDeviceComponentTable, PathEndpointTable): model = models.ConsolePort fields = ( 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'speed', 'description', - 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'inventory_items', 'tags', 'created', 'last_updated', + 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'inventory_items', 'tags', 'created', + 'last_updated', ) default_columns = ('pk', 'name', 'device', 'label', 'type', 'speed', 'description') @@ -391,7 +401,6 @@ class DeviceConsolePortTable(ConsolePortTable): name = tables.TemplateColumn( verbose_name=_('Name'), template_code=' {{ value }}', - order_by=Accessor('_name'), attrs={'td': {'class': 'text-nowrap'}} ) actions = columns.ActionsColumn( @@ -423,7 +432,8 @@ class ConsoleServerPortTable(ModularDeviceComponentTable, PathEndpointTable): model = models.ConsoleServerPort fields = ( 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'speed', 'description', - 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'inventory_items', 'tags', 'created', 'last_updated', + 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'inventory_items', 'tags', 'created', + 'last_updated', ) default_columns = ('pk', 'name', 'device', 'label', 'type', 'speed', 'description') @@ -433,7 +443,6 @@ class DeviceConsoleServerPortTable(ConsoleServerPortTable): verbose_name=_('Name'), template_code=' ' '{{ value }}', - order_by=Accessor('_name'), attrs={'td': {'class': 'text-nowrap'}} ) actions = columns.ActionsColumn( @@ -482,7 +491,6 @@ class DevicePowerPortTable(PowerPortTable): verbose_name=_('Name'), template_code=' ' '{{ value }}', - order_by=Accessor('_name'), attrs={'td': {'class': 'text-nowrap'}} ) actions = columns.ActionsColumn( @@ -531,7 +539,6 @@ class DevicePowerOutletTable(PowerOutletTable): name = tables.TemplateColumn( verbose_name=_('Name'), template_code=' {{ value }}', - order_by=Accessor('_name'), attrs={'td': {'class': 'text-nowrap'}} ) actions = columns.ActionsColumn( @@ -550,6 +557,11 @@ class DevicePowerOutletTable(PowerOutletTable): class BaseInterfaceTable(NetBoxTable): + name = tables.Column( + verbose_name=_('Name'), + linkify=True, + order_by=('_name',) + ) enabled = columns.BooleanColumn( verbose_name=_('Enabled'), ) @@ -585,6 +597,14 @@ class BaseInterfaceTable(NetBoxTable): orderable=False, verbose_name=_('Tagged VLANs') ) + qinq_svlan = tables.Column( + verbose_name=_('Q-in-Q SVLAN'), + linkify=True + ) + primary_mac_address = tables.Column( + verbose_name=_('MAC Address'), + linkify=True + ) def value_ip_addresses(self, value): return ",".join([str(obj.address) for obj in value.all()]) @@ -593,7 +613,7 @@ class BaseInterfaceTable(NetBoxTable): return ",".join([str(obj) for obj in value.all()]) -class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpointTable): +class InterfaceTable(BaseInterfaceTable, ModularDeviceComponentTable, PathEndpointTable): device = tables.Column( verbose_name=_('Device'), linkify={ @@ -627,19 +647,31 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi verbose_name=_('VRF'), linkify=True ) + virtual_circuit_termination = tables.Column( + verbose_name=_('Virtual Circuit'), + linkify=True + ) tags = columns.TagColumn( url_name='dcim:interface_list' ) + # Override PathEndpointTable.connection to accommodate virtual circuits + connection = columns.TemplateColumn( + accessor='_path__destinations', + template_code=INTERFACE_LINKTERMINATION, + verbose_name=_('Connection'), + orderable=False + ) + class Meta(DeviceComponentTable.Meta): model = models.Interface fields = ( 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'enabled', 'type', 'mgmt_only', 'mtu', - 'speed', 'speed_formatted', 'duplex', 'mode', 'mac_address', 'wwn', 'poe_mode', 'poe_type', 'rf_role', 'rf_channel', - 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable', - 'cable_color', 'wireless_link', 'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', - 'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'inventory_items', 'created', - 'last_updated', + 'speed', 'speed_formatted', 'duplex', 'mode', 'primary_mac_address', 'wwn', 'poe_mode', 'poe_type', + 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', + 'mark_connected', 'cable', 'cable_color', 'wireless_link', 'wireless_lans', 'link_peer', 'connection', + 'tags', 'vdcs', 'vrf', 'l2vpn', 'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', + 'qinq_svlan', 'inventory_items', 'created', 'last_updated', ) default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'type', 'description') @@ -676,7 +708,7 @@ class DeviceInterfaceTable(InterfaceTable): 'mgmt_only', 'mtu', 'mode', 'mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link', 'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', 'tunnel', 'ip_addresses', - 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'actions', + 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'actions', ) default_columns = ( 'pk', 'name', 'label', 'enabled', 'type', 'parent', 'lag', 'mtu', 'mode', 'description', 'ip_addresses', @@ -732,7 +764,6 @@ class DeviceFrontPortTable(FrontPortTable): verbose_name=_('Name'), template_code=' ' '{{ value }}', - order_by=Accessor('_name'), attrs={'td': {'class': 'text-nowrap'}} ) actions = columns.ActionsColumn( @@ -779,7 +810,6 @@ class DeviceRearPortTable(RearPortTable): verbose_name=_('Name'), template_code=' ' '{{ value }}', - order_by=Accessor('_name'), attrs={'td': {'class': 'text-nowrap'}} ) actions = columns.ActionsColumn( @@ -842,7 +872,6 @@ class DeviceDeviceBayTable(DeviceBayTable): verbose_name=_('Name'), template_code=' {{ value }}', - order_by=Accessor('_name'), attrs={'td': {'class': 'text-nowrap'}} ) actions = columns.ActionsColumn( @@ -911,7 +940,6 @@ class DeviceModuleBayTable(ModuleBayTable): name = columns.MPTTColumn( verbose_name=_('Name'), linkify=True, - order_by=Accessor('_name') ) actions = columns.ActionsColumn( extra_buttons=MODULEBAY_BUTTONS @@ -965,8 +993,8 @@ class InventoryItemTable(DeviceComponentTable): class Meta(NetBoxTable.Meta): model = models.InventoryItem fields = ( - 'pk', 'id', 'name', 'device', 'parent', 'component', 'label', 'status', 'role', 'manufacturer', 'part_id', 'serial', - 'asset_tag', 'description', 'discovered', 'tags', 'created', 'last_updated', + 'pk', 'id', 'name', 'device', 'parent', 'component', 'label', 'status', 'role', 'manufacturer', 'part_id', + 'serial', 'asset_tag', 'description', 'discovered', 'tags', 'created', 'last_updated', ) default_columns = ( 'pk', 'name', 'device', 'label', 'status', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', @@ -978,15 +1006,14 @@ class DeviceInventoryItemTable(InventoryItemTable): verbose_name=_('Name'), template_code='' '{{ value }}', - order_by=Accessor('_name'), attrs={'td': {'class': 'text-nowrap'}} ) class Meta(NetBoxTable.Meta): model = models.InventoryItem fields = ( - 'pk', 'id', 'name', 'label', 'status', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'component', - 'description', 'discovered', 'tags', 'actions', + 'pk', 'id', 'name', 'label', 'status', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', + 'component', 'description', 'discovered', 'tags', 'actions', ) default_columns = ( 'pk', 'name', 'label', 'status', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'component', @@ -1100,3 +1127,34 @@ class VirtualDeviceContextTable(TenancyColumnsMixin, NetBoxTable): default_columns = ( 'pk', 'name', 'identifier', 'status', 'tenant', 'primary_ip', ) + + +class MACAddressTable(NetBoxTable): + mac_address = tables.TemplateColumn( + template_code=MACADDRESS_LINK, + verbose_name=_('MAC Address') + ) + assigned_object = tables.Column( + linkify=True, + orderable=False, + verbose_name=_('Interface') + ) + assigned_object_parent = tables.Column( + accessor='assigned_object__parent_object', + linkify=True, + orderable=False, + verbose_name=_('Parent') + ) + tags = columns.TagColumn( + url_name='dcim:macaddress_list' + ) + actions = columns.ActionsColumn( + extra_buttons=MACADDRESS_COPY_BUTTON + ) + + class Meta(DeviceComponentTable.Meta): + model = models.MACAddress + fields = ( + 'pk', 'id', 'mac_address', 'assigned_object_parent', 'assigned_object', 'created', 'last_updated', + ) + default_columns = ('pk', 'mac_address', 'assigned_object_parent', 'assigned_object') diff --git a/netbox/dcim/tables/devicetypes.py b/netbox/dcim/tables/devicetypes.py index e8a4e35f1..a7f8f08e8 100644 --- a/netbox/dcim/tables/devicetypes.py +++ b/netbox/dcim/tables/devicetypes.py @@ -163,9 +163,7 @@ class ComponentTemplateTable(NetBoxTable): id = tables.Column( verbose_name=_('ID') ) - name = tables.Column( - order_by=('_name',) - ) + name = tables.Column() class Meta(NetBoxTable.Meta): exclude = ('id', ) @@ -220,6 +218,10 @@ class PowerOutletTemplateTable(ComponentTemplateTable): class InterfaceTemplateTable(ComponentTemplateTable): + name = tables.Column( + verbose_name=_('Name'), + order_by=('_name',) + ) enabled = columns.BooleanColumn( verbose_name=_('Enabled'), ) diff --git a/netbox/dcim/tables/racks.py b/netbox/dcim/tables/racks.py index a6b704161..dbd99ca24 100644 --- a/netbox/dcim/tables/racks.py +++ b/netbox/dcim/tables/racks.py @@ -111,7 +111,6 @@ class RackTypeTable(NetBoxTable): class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): name = tables.Column( verbose_name=_('Name'), - order_by=('_name',), linkify=True ) location = tables.Column( diff --git a/netbox/dcim/tables/template_code.py b/netbox/dcim/tables/template_code.py index 96ab803e6..449d55e14 100644 --- a/netbox/dcim/tables/template_code.py +++ b/netbox/dcim/tables/template_code.py @@ -10,6 +10,20 @@ LINKTERMINATION = """ {% endfor %} """ +INTERFACE_LINKTERMINATION = """ +{% load i18n %} +{% if record.is_virtual and record.virtual_circuit_termination %} + {% for termination in record.connected_endpoints %} + {{ termination.interface.parent_object }} + + {{ termination.interface }} + {% trans "via" %} + {{ termination.parent_object }} + {% if not forloop.last %}
{% endif %} + {% endfor %} +{% else %}""" + LINKTERMINATION + """{% endif %} +""" + CABLE_LENGTH = """ {% load helpers %} {% if record.length %}{{ record.length|floatformat:"-2" }} {{ record.length_unit }}{% endif %} @@ -314,6 +328,9 @@ INTERFACE_BUTTONS = """ {% if perms.ipam.add_ipaddress %}
  • IP Address
  • {% endif %} + {% if perms.dcim.add_macaddress %} +
  • MAC Address
  • + {% endif %} {% if perms.dcim.add_inventoryitem %}
  • Inventory Item
  • {% endif %} @@ -373,6 +390,15 @@ INTERFACE_BUTTONS = """ {% endif %} + {% if perms.circuits.add_virtualcircuittermination and not record.virtual_circuit_termination %} + + + + {% elif perms.circuits.delete_virtualcircuittermination and record.virtual_circuit_termination %} + + + + {% endif %} {% elif record.is_wired and perms.dcim.add_cable %} diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index 1b460cd59..c273e02dd 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -7,6 +7,7 @@ from dcim.choices import * from dcim.constants import * from dcim.models import * from extras.models import ConfigTemplate +from ipam.choices import VLANQinQRoleChoices from ipam.models import ASN, RIR, VLAN, VRF from netbox.api.serializers import GenericObjectSerializer from tenancy.models import Tenant @@ -204,13 +205,41 @@ class LocationTest(APIViewTestCases.APIViewTestCase): Site.objects.bulk_create(sites) parent_locations = ( - Location.objects.create(site=sites[0], name='Parent Location 1', slug='parent-location-1', status=LocationStatusChoices.STATUS_ACTIVE), - Location.objects.create(site=sites[1], name='Parent Location 2', slug='parent-location-2', status=LocationStatusChoices.STATUS_ACTIVE), + Location.objects.create( + site=sites[0], + name='Parent Location 1', + slug='parent-location-1', + status=LocationStatusChoices.STATUS_ACTIVE, + ), + Location.objects.create( + site=sites[1], + name='Parent Location 2', + slug='parent-location-2', + status=LocationStatusChoices.STATUS_ACTIVE, + ), ) - Location.objects.create(site=sites[0], name='Location 1', slug='location-1', parent=parent_locations[0], status=LocationStatusChoices.STATUS_ACTIVE) - Location.objects.create(site=sites[0], name='Location 2', slug='location-2', parent=parent_locations[0], status=LocationStatusChoices.STATUS_ACTIVE) - Location.objects.create(site=sites[0], name='Location 3', slug='location-3', parent=parent_locations[0], status=LocationStatusChoices.STATUS_ACTIVE) + Location.objects.create( + site=sites[0], + name='Location 1', + slug='location-1', + parent=parent_locations[0], + status=LocationStatusChoices.STATUS_ACTIVE, + ) + Location.objects.create( + site=sites[0], + name='Location 2', + slug='location-2', + parent=parent_locations[0], + status=LocationStatusChoices.STATUS_ACTIVE, + ) + Location.objects.create( + site=sites[0], + name='Location 3', + slug='location-3', + parent=parent_locations[0], + status=LocationStatusChoices.STATUS_ACTIVE, + ) cls.create_data = [ { @@ -289,9 +318,24 @@ class RackTypeTest(APIViewTestCases.APIViewTestCase): Manufacturer.objects.bulk_create(manufacturers) rack_types = ( - RackType(manufacturer=manufacturers[0], model='Rack Type 1', slug='rack-type-1', form_factor=RackFormFactorChoices.TYPE_CABINET,), - RackType(manufacturer=manufacturers[0], model='Rack Type 2', slug='rack-type-2', form_factor=RackFormFactorChoices.TYPE_CABINET,), - RackType(manufacturer=manufacturers[0], model='Rack Type 3', slug='rack-type-3', form_factor=RackFormFactorChoices.TYPE_CABINET,), + RackType( + manufacturer=manufacturers[0], + model='Rack Type 1', + slug='rack-type-1', + form_factor=RackFormFactorChoices.TYPE_CABINET, + ), + RackType( + manufacturer=manufacturers[0], + model='Rack Type 2', + slug='rack-type-2', + form_factor=RackFormFactorChoices.TYPE_CABINET, + ), + RackType( + manufacturer=manufacturers[0], + model='Rack Type 3', + slug='rack-type-3', + form_factor=RackFormFactorChoices.TYPE_CABINET, + ), ) RackType.objects.bulk_create(rack_types) @@ -1049,10 +1093,18 @@ class InventoryItemTemplateTest(APIViewTestCases.APIViewTestCase): role = InventoryItemRole.objects.create(name='Inventory Item Role 1', slug='inventory-item-role-1') inventory_item_templates = ( - InventoryItemTemplate(device_type=devicetype, name='Inventory Item Template 1', manufacturer=manufacturer, role=role), - InventoryItemTemplate(device_type=devicetype, name='Inventory Item Template 2', manufacturer=manufacturer, role=role), - InventoryItemTemplate(device_type=devicetype, name='Inventory Item Template 3', manufacturer=manufacturer, role=role), - InventoryItemTemplate(device_type=devicetype, name='Inventory Item Template 4', manufacturer=manufacturer, role=role), + InventoryItemTemplate( + device_type=devicetype, name='Inventory Item Template 1', manufacturer=manufacturer, role=role + ), + InventoryItemTemplate( + device_type=devicetype, name='Inventory Item Template 2', manufacturer=manufacturer, role=role + ), + InventoryItemTemplate( + device_type=devicetype, name='Inventory Item Template 3', manufacturer=manufacturer, role=role + ), + InventoryItemTemplate( + device_type=devicetype, name='Inventory Item Template 4', manufacturer=manufacturer, role=role + ), ) for item in inventory_item_templates: item.save() @@ -1618,6 +1670,7 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase VLAN(name='VLAN 1', vid=1), VLAN(name='VLAN 2', vid=2), VLAN(name='VLAN 3', vid=3), + VLAN(name='SVLAN 1', vid=1001, qinq_role=VLANQinQRoleChoices.ROLE_SERVICE), ) VLAN.objects.bulk_create(vlans) @@ -1676,18 +1729,22 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase 'vdcs': [vdcs[1].pk], 'name': 'Interface 7', 'type': InterfaceTypeChoices.TYPE_80211A, + 'mode': InterfaceModeChoices.MODE_Q_IN_Q, 'tx_power': 10, 'wireless_lans': [wireless_lans[0].pk, wireless_lans[1].pk], 'rf_channel': WirelessChannelChoices.CHANNEL_5G_32, + 'qinq_svlan': vlans[3].pk, }, { 'device': device.pk, 'vdcs': [vdcs[1].pk], 'name': 'Interface 8', 'type': InterfaceTypeChoices.TYPE_80211A, + 'mode': InterfaceModeChoices.MODE_Q_IN_Q, 'tx_power': 10, 'wireless_lans': [wireless_lans[0].pk, wireless_lans[1].pk], 'rf_channel': "", + 'qinq_svlan': vlans[3].pk, }, ] @@ -1955,9 +2012,15 @@ class InventoryItemTest(APIViewTestCases.APIViewTestCase): ) Interface.objects.bulk_create(interfaces) - InventoryItem.objects.create(device=device, name='Inventory Item 1', role=roles[0], manufacturer=manufacturer, component=interfaces[0]) - InventoryItem.objects.create(device=device, name='Inventory Item 2', role=roles[0], manufacturer=manufacturer, component=interfaces[1]) - InventoryItem.objects.create(device=device, name='Inventory Item 3', role=roles[0], manufacturer=manufacturer, component=interfaces[2]) + InventoryItem.objects.create( + device=device, name='Inventory Item 1', role=roles[0], manufacturer=manufacturer, component=interfaces[0] + ) + InventoryItem.objects.create( + device=device, name='Inventory Item 2', role=roles[0], manufacturer=manufacturer, component=interfaces[1] + ) + InventoryItem.objects.create( + device=device, name='Inventory Item 3', role=roles[0], manufacturer=manufacturer, component=interfaces[2] + ) cls.create_data = [ { diff --git a/netbox/dcim/tests/test_cablepaths.py b/netbox/dcim/tests/test_cablepaths.py index f7c337bdf..1acc9a8a1 100644 --- a/netbox/dcim/tests/test_cablepaths.py +++ b/netbox/dcim/tests/test_cablepaths.py @@ -661,24 +661,64 @@ class CablePathTestCase(TestCase): ) cable5.save() path1 = self.assertPathExists( - ([interface1, interface2], cable1, frontport1_1, rearport1, cable3, rearport2, frontport2_1, cable4, [interface5, interface6]), + ( + [interface1, interface2], + cable1, + frontport1_1, + rearport1, + cable3, + rearport2, + frontport2_1, + cable4, + [interface5, interface6], + ), is_complete=True, - is_active=True + is_active=True, ) path2 = self.assertPathExists( - ([interface3, interface4], cable2, frontport1_2, rearport1, cable3, rearport2, frontport2_2, cable5, [interface7, interface8]), + ( + [interface3, interface4], + cable2, + frontport1_2, + rearport1, + cable3, + rearport2, + frontport2_2, + cable5, + [interface7, interface8], + ), is_complete=True, - is_active=True + is_active=True, ) path3 = self.assertPathExists( - ([interface5, interface6], cable4, frontport2_1, rearport2, cable3, rearport1, frontport1_1, cable1, [interface1, interface2]), + ( + [interface5, interface6], + cable4, + frontport2_1, + rearport2, + cable3, + rearport1, + frontport1_1, + cable1, + [interface1, interface2], + ), is_complete=True, - is_active=True + is_active=True, ) path4 = self.assertPathExists( - ([interface7, interface8], cable5, frontport2_2, rearport2, cable3, rearport1, frontport1_2, cable2, [interface3, interface4]), + ( + [interface7, interface8], + cable5, + frontport2_2, + rearport2, + cable3, + rearport1, + frontport1_2, + cable2, + [interface3, interface4], + ), is_complete=True, - is_active=True + is_active=True, ) self.assertEqual(CablePath.objects.count(), 4) @@ -1167,7 +1207,11 @@ class CablePathTestCase(TestCase): [IF1] --C1-- [CT1] """ interface1 = Interface.objects.create(device=self.device, name='Interface 1') - circuittermination1 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='A') + circuittermination1 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=self.site, + term_side='A' + ) # Create cable 1 cable1 = Cable( @@ -1198,7 +1242,11 @@ class CablePathTestCase(TestCase): """ interface1 = Interface.objects.create(device=self.device, name='Interface 1') interface2 = Interface.objects.create(device=self.device, name='Interface 2') - circuittermination1 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='A') + circuittermination1 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=self.site, + term_side='A' + ) # Create cable 1 cable1 = Cable( @@ -1214,7 +1262,11 @@ class CablePathTestCase(TestCase): ) # Create CT2 - circuittermination2 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='Z') + circuittermination2 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=self.site, + term_side='Z' + ) # Check for partial path to site self.assertPathExists( @@ -1266,7 +1318,11 @@ class CablePathTestCase(TestCase): interface2 = Interface.objects.create(device=self.device, name='Interface 2') interface3 = Interface.objects.create(device=self.device, name='Interface 3') interface4 = Interface.objects.create(device=self.device, name='Interface 4') - circuittermination1 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='A') + circuittermination1 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=self.site, + term_side='A' + ) # Create cable 1 cable1 = Cable( @@ -1282,7 +1338,11 @@ class CablePathTestCase(TestCase): ) # Create CT2 - circuittermination2 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='Z') + circuittermination2 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=self.site, + term_side='Z' + ) # Check for partial path to site self.assertPathExists( @@ -1299,14 +1359,28 @@ class CablePathTestCase(TestCase): # Check for complete path in each direction self.assertPathExists( - ([interface1, interface2], cable1, circuittermination1, circuittermination2, cable2, [interface3, interface4]), + ( + [interface1, interface2], + cable1, + circuittermination1, + circuittermination2, + cable2, + [interface3, interface4], + ), is_complete=True, - is_active=True + is_active=True, ) self.assertPathExists( - ([interface3, interface4], cable2, circuittermination2, circuittermination1, cable1, [interface1, interface2]), + ( + [interface3, interface4], + cable2, + circuittermination2, + circuittermination1, + cable1, + [interface1, interface2], + ), is_complete=True, - is_active=True + is_active=True, ) self.assertEqual(CablePath.objects.count(), 2) @@ -1335,8 +1409,16 @@ class CablePathTestCase(TestCase): """ interface1 = Interface.objects.create(device=self.device, name='Interface 1') site2 = Site.objects.create(name='Site 2', slug='site-2') - circuittermination1 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='A') - circuittermination2 = CircuitTermination.objects.create(circuit=self.circuit, site=site2, term_side='Z') + circuittermination1 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=self.site, + term_side='A' + ) + circuittermination2 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=site2, + term_side='Z' + ) # Create cable 1 cable1 = Cable( @@ -1365,8 +1447,16 @@ class CablePathTestCase(TestCase): """ interface1 = Interface.objects.create(device=self.device, name='Interface 1') providernetwork = ProviderNetwork.objects.create(name='Provider Network 1', provider=self.circuit.provider) - circuittermination1 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='A') - circuittermination2 = CircuitTermination.objects.create(circuit=self.circuit, provider_network=providernetwork, term_side='Z') + circuittermination1 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=self.site, + term_side='A' + ) + circuittermination2 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=providernetwork, + term_side='Z' + ) # Create cable 1 cable1 = Cable( @@ -1413,8 +1503,15 @@ class CablePathTestCase(TestCase): frontport2_2 = FrontPort.objects.create( device=self.device, name='Front Port 2:2', rear_port=rearport2, rear_port_position=2 ) - circuittermination1 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='A') - circuittermination2 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='Z') + circuittermination1 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=self.site, + term_side='A' + ) + circuittermination2 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=self.site, term_side='Z' + ) # Create cables cable1 = Cable( @@ -1499,10 +1596,26 @@ class CablePathTestCase(TestCase): interface1 = Interface.objects.create(device=self.device, name='Interface 1') interface2 = Interface.objects.create(device=self.device, name='Interface 2') circuit2 = Circuit.objects.create(provider=self.circuit.provider, type=self.circuit.type, cid='Circuit 2') - circuittermination1 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='A') - circuittermination2 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='Z') - circuittermination3 = CircuitTermination.objects.create(circuit=circuit2, site=self.site, term_side='A') - circuittermination4 = CircuitTermination.objects.create(circuit=circuit2, site=self.site, term_side='Z') + circuittermination1 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=self.site, + term_side='A' + ) + circuittermination2 = CircuitTermination.objects.create( + circuit=self.circuit, + termination=self.site, + term_side='Z' + ) + circuittermination3 = CircuitTermination.objects.create( + circuit=circuit2, + termination=self.site, + term_side='A' + ) + circuittermination4 = CircuitTermination.objects.create( + circuit=circuit2, + termination=self.site, + term_side='Z' + ) # Create cables cable1 = Cable( @@ -1706,45 +1819,95 @@ class CablePathTestCase(TestCase): ) cable3.save() self.assertPathExists( - (interface1, cable1, (frontport1_1, frontport1_2), rearport1, cable3, rearport2, (frontport2_1, frontport2_2)), - is_complete=False + ( + interface1, + cable1, + (frontport1_1, frontport1_2), + rearport1, + cable3, + rearport2, + (frontport2_1, frontport2_2), + ), + is_complete=False, ) self.assertPathExists( - (interface2, cable2, (frontport1_3, frontport1_4), rearport1, cable3, rearport2, (frontport2_3, frontport2_4)), - is_complete=False + ( + interface2, + cable2, + (frontport1_3, frontport1_4), + rearport1, + cable3, + rearport2, + (frontport2_3, frontport2_4), + ), + is_complete=False, ) self.assertEqual(CablePath.objects.count(), 2) # Create cables 4-5 - cable4 = Cable( - a_terminations=[frontport2_1, frontport2_2], - b_terminations=[interface3] - ) + cable4 = Cable(a_terminations=[frontport2_1, frontport2_2], b_terminations=[interface3]) cable4.save() - cable5 = Cable( - a_terminations=[frontport2_3, frontport2_4], - b_terminations=[interface4] - ) + cable5 = Cable(a_terminations=[frontport2_3, frontport2_4], b_terminations=[interface4]) cable5.save() path1 = self.assertPathExists( - (interface1, cable1, (frontport1_1, frontport1_2), rearport1, cable3, rearport2, (frontport2_1, frontport2_2), cable4, interface3), + ( + interface1, + cable1, + (frontport1_1, frontport1_2), + rearport1, + cable3, + rearport2, + (frontport2_1, frontport2_2), + cable4, + interface3, + ), is_complete=True, - is_active=True + is_active=True, ) path2 = self.assertPathExists( - (interface2, cable2, (frontport1_3, frontport1_4), rearport1, cable3, rearport2, (frontport2_3, frontport2_4), cable5, interface4), + ( + interface2, + cable2, + (frontport1_3, frontport1_4), + rearport1, + cable3, + rearport2, + (frontport2_3, frontport2_4), + cable5, + interface4, + ), is_complete=True, - is_active=True + is_active=True, ) path3 = self.assertPathExists( - (interface3, cable4, (frontport2_1, frontport2_2), rearport2, cable3, rearport1, (frontport1_1, frontport1_2), cable1, interface1), + ( + interface3, + cable4, + (frontport2_1, frontport2_2), + rearport2, + cable3, + rearport1, + (frontport1_1, frontport1_2), + cable1, + interface1, + ), is_complete=True, - is_active=True + is_active=True, ) path4 = self.assertPathExists( - (interface4, cable5, (frontport2_3, frontport2_4), rearport2, cable3, rearport1, (frontport1_3, frontport1_4), cable2, interface2), + ( + interface4, + cable5, + (frontport2_3, frontport2_4), + rearport2, + cable3, + rearport1, + (frontport1_3, frontport1_4), + cable2, + interface2, + ), is_complete=True, - is_active=True + is_active=True, ) self.assertEqual(CablePath.objects.count(), 4) @@ -1809,7 +1972,10 @@ class CablePathTestCase(TestCase): ) cable1.save() self.assertPathExists( - (interface1, cable1, (frontport1, frontport3), (rearport1, rearport3), (cable2, cable4), (rearport2, rearport4), (frontport2, frontport4)), + ( + interface1, cable1, (frontport1, frontport3), (rearport1, rearport3), (cable2, cable4), + (rearport2, rearport4), (frontport2, frontport4) + ), is_complete=False ) self.assertEqual(CablePath.objects.count(), 1) diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index ae738b57f..ede1e2a09 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -4,12 +4,13 @@ from circuits.models import Circuit, CircuitTermination, CircuitType, Provider from dcim.choices import * from dcim.filtersets import * from dcim.models import * -from ipam.models import ASN, IPAddress, RIR, VRF +from ipam.choices import VLANQinQRoleChoices +from ipam.models import ASN, IPAddress, RIR, VLAN, VLANTranslationPolicy, VRF from netbox.choices import ColorChoices, WeightUnitChoices from tenancy.models import Tenant, TenantGroup from users.models import User -from utilities.testing import ChangeLoggedFilterSetTests, create_test_device -from virtualization.models import Cluster, ClusterType, ClusterGroup +from utilities.testing import ChangeLoggedFilterSetTests, create_test_device, create_test_virtualmachine +from virtualization.models import Cluster, ClusterType, ClusterGroup, VMInterface, VirtualMachine from wireless.choices import WirelessChannelChoices, WirelessRoleChoices @@ -242,9 +243,41 @@ class SiteTestCase(TestCase, ChangeLoggedFilterSetTests): ASN.objects.bulk_create(asns) sites = ( - Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0], tenant=tenants[0], status=SiteStatusChoices.STATUS_ACTIVE, facility='Facility 1', latitude=10, longitude=10, description='foobar1'), - Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1], tenant=tenants[1], status=SiteStatusChoices.STATUS_PLANNED, facility='Facility 2', latitude=20, longitude=20, description='foobar2'), - Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2], tenant=tenants[2], status=SiteStatusChoices.STATUS_RETIRED, facility='Facility 3', latitude=30, longitude=30), + Site( + name='Site 1', + slug='site-1', + region=regions[0], + group=groups[0], + tenant=tenants[0], + status=SiteStatusChoices.STATUS_ACTIVE, + facility='Facility 1', + latitude=10, + longitude=10, + description='foobar1', + ), + Site( + name='Site 2', + slug='site-2', + region=regions[1], + group=groups[1], + tenant=tenants[1], + status=SiteStatusChoices.STATUS_PLANNED, + facility='Facility 2', + latitude=20, + longitude=20, + description='foobar2', + ), + Site( + name='Site 3', + slug='site-3', + region=regions[2], + group=groups[2], + tenant=tenants[2], + status=SiteStatusChoices.STATUS_RETIRED, + facility='Facility 3', + latitude=30, + longitude=30, + ), ) Site.objects.bulk_create(sites) sites[0].asns.set([asns[0]]) @@ -360,9 +393,33 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests): location.save() locations = ( - Location(name='Location 1A', slug='location-1a', site=sites[0], parent=parent_locations[0], status=LocationStatusChoices.STATUS_PLANNED, facility='Facility 1', description='foobar1'), - Location(name='Location 2A', slug='location-2a', site=sites[1], parent=parent_locations[1], status=LocationStatusChoices.STATUS_STAGING, facility='Facility 2', description='foobar2'), - Location(name='Location 3A', slug='location-3a', site=sites[2], parent=parent_locations[2], status=LocationStatusChoices.STATUS_DECOMMISSIONING, facility='Facility 3', description='foobar3'), + Location( + name='Location 1A', + slug='location-1a', + site=sites[0], + parent=parent_locations[0], + status=LocationStatusChoices.STATUS_PLANNED, + facility='Facility 1', + description='foobar1', + ), + Location( + name='Location 2A', + slug='location-2a', + site=sites[1], + parent=parent_locations[1], + status=LocationStatusChoices.STATUS_STAGING, + facility='Facility 2', + description='foobar2', + ), + Location( + name='Location 3A', + slug='location-3a', + site=sites[2], + parent=parent_locations[2], + status=LocationStatusChoices.STATUS_DECOMMISSIONING, + facility='Facility 3', + description='foobar3', + ), ) for location in locations: location.save() @@ -871,7 +928,6 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_outer_unit(self): - self.assertEqual(Rack.objects.filter(outer_unit__isnull=False).count(), 5) params = {'outer_unit': RackDimensionUnitChoices.UNIT_MILLIMETER} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -1222,10 +1278,22 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests): RearPortTemplate(device_type=device_types[1], name='Rear Port 2', type=PortTypeChoices.TYPE_8P8C), ) RearPortTemplate.objects.bulk_create(rear_ports) - FrontPortTemplate.objects.bulk_create(( - FrontPortTemplate(device_type=device_types[0], name='Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[0]), - FrontPortTemplate(device_type=device_types[1], name='Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[1]), - )) + FrontPortTemplate.objects.bulk_create( + ( + FrontPortTemplate( + device_type=device_types[0], + name='Front Port 1', + type=PortTypeChoices.TYPE_8P8C, + rear_port=rear_ports[0], + ), + FrontPortTemplate( + device_type=device_types[1], + name='Front Port 2', + type=PortTypeChoices.TYPE_8P8C, + rear_port=rear_ports[1], + ), + ) + ) ModuleBayTemplate.objects.bulk_create(( ModuleBayTemplate(device_type=device_types[0], name='Module Bay 1'), ModuleBayTemplate(device_type=device_types[1], name='Module Bay 2'), @@ -1435,10 +1503,22 @@ class ModuleTypeTestCase(TestCase, ChangeLoggedFilterSetTests): RearPortTemplate(module_type=module_types[1], name='Rear Port 2', type=PortTypeChoices.TYPE_8P8C), ) RearPortTemplate.objects.bulk_create(rear_ports) - FrontPortTemplate.objects.bulk_create(( - FrontPortTemplate(module_type=module_types[0], name='Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[0]), - FrontPortTemplate(module_type=module_types[1], name='Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[1]), - )) + FrontPortTemplate.objects.bulk_create( + ( + FrontPortTemplate( + module_type=module_types[0], + name='Front Port 1', + type=PortTypeChoices.TYPE_8P8C, + rear_port=rear_ports[0], + ), + FrontPortTemplate( + module_type=module_types[1], + name='Front Port 2', + type=PortTypeChoices.TYPE_8P8C, + rear_port=rear_ports[1], + ), + ) + ) def test_q(self): params = {'q': 'foobar1'} @@ -1893,11 +1973,19 @@ class ModuleBayTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTests, ) ModuleType.objects.bulk_create(module_types) - ModuleBayTemplate.objects.bulk_create(( - ModuleBayTemplate(device_type=device_types[0], name='Module Bay 1', description='foobar1'), - ModuleBayTemplate(device_type=device_types[1], name='Module Bay 2', description='foobar2', module_type=module_types[0]), - ModuleBayTemplate(device_type=device_types[2], name='Module Bay 3', description='foobar3', module_type=module_types[1]), - )) + ModuleBayTemplate.objects.bulk_create( + ( + ModuleBayTemplate( + device_type=device_types[0], name='Module Bay 1', description='foobar1' + ), + ModuleBayTemplate( + device_type=device_types[1], name='Module Bay 2', description='foobar2', module_type=module_types[0] + ), + ModuleBayTemplate( + device_type=device_types[2], name='Module Bay 3', description='foobar3', module_type=module_types[1] + ), + ) + ) def test_name(self): params = {'name': ['Module Bay 1', 'Module Bay 2']} @@ -1996,9 +2084,15 @@ class InventoryItemTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTe item.save() child_inventory_item_templates = ( - InventoryItemTemplate(device_type=device_types[0], name='Inventory Item 1A', parent=inventory_item_templates[0]), - InventoryItemTemplate(device_type=device_types[1], name='Inventory Item 2A', parent=inventory_item_templates[1]), - InventoryItemTemplate(device_type=device_types[2], name='Inventory Item 3A', parent=inventory_item_templates[2]), + InventoryItemTemplate( + device_type=device_types[0], name='Inventory Item 1A', parent=inventory_item_templates[0] + ), + InventoryItemTemplate( + device_type=device_types[1], name='Inventory Item 2A', parent=inventory_item_templates[1] + ), + InventoryItemTemplate( + device_type=device_types[2], name='Inventory Item 3A', parent=inventory_item_templates[2] + ), ) for item in child_inventory_item_templates: item.save() @@ -2323,10 +2417,17 @@ class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests): PowerOutlet(device=devices[1], name='Power Outlet 2'), )) interfaces = ( - Interface(device=devices[0], name='Interface 1', mac_address='00-00-00-00-00-01'), - Interface(device=devices[1], name='Interface 2', mac_address='00-00-00-00-00-02'), + Interface(device=devices[0], name='Interface 1'), + Interface(device=devices[1], name='Interface 2'), ) Interface.objects.bulk_create(interfaces) + mac_addresses = ( + MACAddress(mac_address='00-00-00-00-00-01'), + MACAddress(mac_address='00-00-00-00-00-02'), + ) + MACAddress.objects.bulk_create(mac_addresses) + interfaces[0].mac_addresses.set([mac_addresses[0]]) + interfaces[1].mac_addresses.set([mac_addresses[1]]) rear_ports = ( RearPort(device=devices[0], name='Rear Port 1', type=PortTypeChoices.TYPE_8P8C), RearPort(device=devices[1], name='Rear Port 2', type=PortTypeChoices.TYPE_8P8C), @@ -2841,10 +2942,41 @@ class ConsolePortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF Rack.objects.bulk_create(racks) devices = ( - Device(name='Device 1', device_type=device_types[0], role=roles[0], site=sites[0], location=locations[0], rack=racks[0], status='active'), - Device(name='Device 2', device_type=device_types[1], role=roles[1], site=sites[1], location=locations[1], rack=racks[1], status='planned'), - Device(name='Device 3', device_type=device_types[2], role=roles[2], site=sites[2], location=locations[2], rack=racks[2], status='offline'), - Device(name=None, device_type=device_types[0], role=roles[0], site=sites[3], status='offline'), # For cable connections + Device( + name='Device 1', + device_type=device_types[0], + role=roles[0], + site=sites[0], + location=locations[0], + rack=racks[0], + status='active', + ), + Device( + name='Device 2', + device_type=device_types[1], + role=roles[1], + site=sites[1], + location=locations[1], + rack=racks[1], + status='planned', + ), + Device( + name='Device 3', + device_type=device_types[2], + role=roles[2], + site=sites[2], + location=locations[2], + rack=racks[2], + status='offline', + ), + # For cable connections + Device( + name=None, + device_type=device_types[0], + role=roles[0], + site=sites[3], + status='offline' + ), ) Device.objects.bulk_create(devices) @@ -3022,10 +3154,41 @@ class ConsoleServerPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeL Rack.objects.bulk_create(racks) devices = ( - Device(name='Device 1', device_type=device_types[0], role=roles[0], site=sites[0], location=locations[0], rack=racks[0], status='active'), - Device(name='Device 2', device_type=device_types[1], role=roles[1], site=sites[1], location=locations[1], rack=racks[1], status='planned'), - Device(name='Device 3', device_type=device_types[2], role=roles[2], site=sites[2], location=locations[2], rack=racks[2], status='offline'), - Device(name=None, device_type=device_types[2], role=roles[2], site=sites[3], status='offline'), # For cable connections + Device( + name='Device 1', + device_type=device_types[0], + role=roles[0], + site=sites[0], + location=locations[0], + rack=racks[0], + status='active', + ), + Device( + name='Device 2', + device_type=device_types[1], + role=roles[1], + site=sites[1], + location=locations[1], + rack=racks[1], + status='planned', + ), + Device( + name='Device 3', + device_type=device_types[2], + role=roles[2], + site=sites[2], + location=locations[2], + rack=racks[2], + status='offline', + ), + # For cable connections + Device( + name=None, + device_type=device_types[2], + role=roles[2], + site=sites[3], + status='offline' + ), ) Device.objects.bulk_create(devices) @@ -3051,9 +3214,15 @@ class ConsoleServerPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeL ConsolePort.objects.bulk_create(console_ports) console_server_ports = ( - ConsoleServerPort(device=devices[0], module=modules[0], name='Console Server Port 1', label='A', description='First'), - ConsoleServerPort(device=devices[1], module=modules[1], name='Console Server Port 2', label='B', description='Second'), - ConsoleServerPort(device=devices[2], module=modules[2], name='Console Server Port 3', label='C', description='Third'), + ConsoleServerPort( + device=devices[0], module=modules[0], name='Console Server Port 1', label='A', description='First' + ), + ConsoleServerPort( + device=devices[1], module=modules[1], name='Console Server Port 2', label='B', description='Second' + ), + ConsoleServerPort( + device=devices[2], module=modules[2], name='Console Server Port 3', label='C', description='Third' + ), ) ConsoleServerPort.objects.bulk_create(console_server_ports) @@ -3203,10 +3372,41 @@ class PowerPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil Rack.objects.bulk_create(racks) devices = ( - Device(name='Device 1', device_type=device_types[0], role=roles[0], site=sites[0], location=locations[0], rack=racks[0], status='active'), - Device(name='Device 2', device_type=device_types[1], role=roles[1], site=sites[1], location=locations[1], rack=racks[1], status='planned'), - Device(name='Device 3', device_type=device_types[2], role=roles[2], site=sites[2], location=locations[2], rack=racks[2], status='offline'), - Device(name=None, device_type=device_types[2], role=roles[2], site=sites[3], status='offline'), # For cable connections + Device( + name='Device 1', + device_type=device_types[0], + role=roles[0], + site=sites[0], + location=locations[0], + rack=racks[0], + status='active', + ), + Device( + name='Device 2', + device_type=device_types[1], + role=roles[1], + site=sites[1], + location=locations[1], + rack=racks[1], + status='planned', + ), + Device( + name='Device 3', + device_type=device_types[2], + role=roles[2], + site=sites[2], + location=locations[2], + rack=racks[2], + status='offline', + ), + # For cable connections + Device( + name=None, + device_type=device_types[2], + role=roles[2], + site=sites[3], + status='offline' + ), ) Device.objects.bulk_create(devices) @@ -3232,9 +3432,33 @@ class PowerPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil PowerOutlet.objects.bulk_create(power_outlets) power_ports = ( - PowerPort(device=devices[0], module=modules[0], name='Power Port 1', label='A', maximum_draw=100, allocated_draw=50, description='First'), - PowerPort(device=devices[1], module=modules[1], name='Power Port 2', label='B', maximum_draw=200, allocated_draw=100, description='Second'), - PowerPort(device=devices[2], module=modules[2], name='Power Port 3', label='C', maximum_draw=300, allocated_draw=150, description='Third'), + PowerPort( + device=devices[0], + module=modules[0], + name='Power Port 1', + label='A', + maximum_draw=100, + allocated_draw=50, + description='First', + ), + PowerPort( + device=devices[1], + module=modules[1], + name='Power Port 2', + label='B', + maximum_draw=200, + allocated_draw=100, + description='Second', + ), + PowerPort( + device=devices[2], + module=modules[2], + name='Power Port 3', + label='C', + maximum_draw=300, + allocated_draw=150, + description='Third', + ), ) PowerPort.objects.bulk_create(power_ports) @@ -3392,10 +3616,41 @@ class PowerOutletTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF Rack.objects.bulk_create(racks) devices = ( - Device(name='Device 1', device_type=device_types[0], role=roles[0], site=sites[0], location=locations[0], rack=racks[0], status='active'), - Device(name='Device 2', device_type=device_types[1], role=roles[1], site=sites[1], location=locations[1], rack=racks[1], status='planned'), - Device(name='Device 3', device_type=device_types[2], role=roles[2], site=sites[2], location=locations[2], rack=racks[2], status='offline'), - Device(name=None, device_type=device_types[2], role=roles[2], site=sites[3], status='offline'), # For cable connections + Device( + name='Device 1', + device_type=device_types[0], + role=roles[0], + site=sites[0], + location=locations[0], + rack=racks[0], + status='active', + ), + Device( + name='Device 2', + device_type=device_types[1], + role=roles[1], + site=sites[1], + location=locations[1], + rack=racks[1], + status='planned', + ), + Device( + name='Device 3', + device_type=device_types[2], + role=roles[2], + site=sites[2], + location=locations[2], + rack=racks[2], + status='offline', + ), + # For cable connections + Device( + name=None, + device_type=device_types[2], + role=roles[2], + site=sites[3], + status='offline' + ), ) Device.objects.bulk_create(devices) @@ -3421,9 +3676,33 @@ class PowerOutletTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF PowerPort.objects.bulk_create(power_ports) power_outlets = ( - PowerOutlet(device=devices[0], module=modules[0], name='Power Outlet 1', label='A', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A, description='First', color='ff0000'), - PowerOutlet(device=devices[1], module=modules[1], name='Power Outlet 2', label='B', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_B, description='Second', color='00ff00'), - PowerOutlet(device=devices[2], module=modules[2], name='Power Outlet 3', label='C', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_C, description='Third', color='0000ff'), + PowerOutlet( + device=devices[0], + module=modules[0], + name='Power Outlet 1', + label='A', + feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A, + description='First', + color='ff0000', + ), + PowerOutlet( + device=devices[1], + module=modules[1], + name='Power Outlet 2', + label='B', + feed_leg=PowerOutletFeedLegChoices.FEED_LEG_B, + description='Second', + color='00ff00', + ), + PowerOutlet( + device=devices[2], + module=modules[2], + name='Power Outlet 3', + label='C', + feed_leg=PowerOutletFeedLegChoices.FEED_LEG_C, + description='Third', + color='0000ff', + ), ) PowerOutlet.objects.bulk_create(power_outlets) @@ -3521,7 +3800,7 @@ class PowerOutletTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests): queryset = Interface.objects.all() filterset = InterfaceFilterSet - ignore_fields = ('tagged_vlans', 'untagged_vlan', 'vdcs') + ignore_fields = ('tagged_vlans', 'untagged_vlan', 'qinq_svlan', 'vdcs') @classmethod def setUpTestData(cls): @@ -3665,11 +3944,36 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil # Virtual Device Context Creation vdcs = ( - VirtualDeviceContext(device=devices[4], name='VDC 1', identifier=1, status=VirtualDeviceContextStatusChoices.STATUS_ACTIVE), - VirtualDeviceContext(device=devices[4], name='VDC 2', identifier=2, status=VirtualDeviceContextStatusChoices.STATUS_PLANNED), + VirtualDeviceContext( + device=devices[4], name='VDC 1', identifier=1, status=VirtualDeviceContextStatusChoices.STATUS_ACTIVE + ), + VirtualDeviceContext( + device=devices[4], name='VDC 2', identifier=2, status=VirtualDeviceContextStatusChoices.STATUS_PLANNED + ), ) VirtualDeviceContext.objects.bulk_create(vdcs) + mac_addresses = ( + MACAddress(mac_address='00-00-00-00-00-01'), + MACAddress(mac_address='00-00-00-00-00-02'), + MACAddress(mac_address='00-00-00-00-00-03'), + ) + MACAddress.objects.bulk_create(mac_addresses) + + vlans = ( + VLAN(name='SVLAN 1', vid=1001, qinq_role=VLANQinQRoleChoices.ROLE_SERVICE), + VLAN(name='SVLAN 2', vid=1002, qinq_role=VLANQinQRoleChoices.ROLE_SERVICE), + VLAN(name='SVLAN 3', vid=1003, qinq_role=VLANQinQRoleChoices.ROLE_SERVICE), + ) + VLAN.objects.bulk_create(vlans) + + vlan_translation_policies = ( + VLANTranslationPolicy(name='Policy 1'), + VLANTranslationPolicy(name='Policy 2'), + VLANTranslationPolicy(name='Policy 3'), + ) + VLANTranslationPolicy.objects.bulk_create(vlan_translation_policies) + interfaces = ( Interface( device=devices[0], @@ -3681,13 +3985,13 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil mgmt_only=True, mtu=100, mode=InterfaceModeChoices.MODE_ACCESS, - mac_address='00-00-00-00-00-01', description='First', vrf=vrfs[0], speed=1000000, duplex='half', poe_mode=InterfacePoEModeChoices.MODE_PSE, - poe_type=InterfacePoETypeChoices.TYPE_1_8023AF + poe_type=InterfacePoETypeChoices.TYPE_1_8023AF, + vlan_translation_policy=vlan_translation_policies[0], ), Interface( device=devices[1], @@ -3706,13 +4010,13 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil mgmt_only=True, mtu=200, mode=InterfaceModeChoices.MODE_TAGGED, - mac_address='00-00-00-00-00-02', description='Second', vrf=vrfs[1], speed=1000000, duplex='full', poe_mode=InterfacePoEModeChoices.MODE_PD, - poe_type=InterfacePoETypeChoices.TYPE_1_8023AF + poe_type=InterfacePoETypeChoices.TYPE_1_8023AF, + vlan_translation_policy=vlan_translation_policies[0], ), Interface( device=devices[3], @@ -3724,13 +4028,13 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil mgmt_only=False, mtu=300, mode=InterfaceModeChoices.MODE_TAGGED_ALL, - mac_address='00-00-00-00-00-03', description='Third', vrf=vrfs[2], speed=100000, duplex='half', poe_mode=InterfacePoEModeChoices.MODE_PSE, - poe_type=InterfacePoETypeChoices.TYPE_2_8023AT + poe_type=InterfacePoETypeChoices.TYPE_2_8023AT, + vlan_translation_policy=vlan_translation_policies[1], ), Interface( device=devices[4], @@ -3743,7 +4047,10 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil speed=100000, duplex='full', poe_mode=InterfacePoEModeChoices.MODE_PD, - poe_type=InterfacePoETypeChoices.TYPE_2_8023AT + poe_type=InterfacePoETypeChoices.TYPE_2_8023AT, + mode=InterfaceModeChoices.MODE_Q_IN_Q, + qinq_svlan=vlans[0], + vlan_translation_policy=vlan_translation_policies[1], ), Interface( device=devices[4], @@ -3752,7 +4059,9 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil type=InterfaceTypeChoices.TYPE_OTHER, enabled=True, mgmt_only=True, - tx_power=40 + tx_power=40, + mode=InterfaceModeChoices.MODE_Q_IN_Q, + qinq_svlan=vlans[1] ), Interface( device=devices[4], @@ -3761,7 +4070,9 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil type=InterfaceTypeChoices.TYPE_OTHER, enabled=False, mgmt_only=False, - tx_power=40 + tx_power=40, + mode=InterfaceModeChoices.MODE_Q_IN_Q, + qinq_svlan=vlans[2] ), Interface( device=devices[4], @@ -3790,6 +4101,10 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil interfaces[6].vdcs.set([vdcs[0]]) interfaces[7].vdcs.set([vdcs[1]]) + interfaces[0].mac_addresses.set([mac_addresses[0]]) + interfaces[2].mac_addresses.set([mac_addresses[1]]) + interfaces[3].mac_addresses.set([mac_addresses[2]]) + # Cables Cable(a_terminations=[interfaces[0]], b_terminations=[interfaces[5]]).save() Cable(a_terminations=[interfaces[1]], b_terminations=[interfaces[6]]).save() @@ -3847,9 +4162,24 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil # Create child interfaces parent_interface = Interface.objects.first() child_interfaces = ( - Interface(device=parent_interface.device, name='Child 1', parent=parent_interface, type=InterfaceTypeChoices.TYPE_VIRTUAL), - Interface(device=parent_interface.device, name='Child 2', parent=parent_interface, type=InterfaceTypeChoices.TYPE_VIRTUAL), - Interface(device=parent_interface.device, name='Child 3', parent=parent_interface, type=InterfaceTypeChoices.TYPE_VIRTUAL), + Interface( + device=parent_interface.device, + name='Child 1', + parent=parent_interface, + type=InterfaceTypeChoices.TYPE_VIRTUAL, + ), + Interface( + device=parent_interface.device, + name='Child 2', + parent=parent_interface, + type=InterfaceTypeChoices.TYPE_VIRTUAL, + ), + Interface( + device=parent_interface.device, + name='Child 3', + parent=parent_interface, + type=InterfaceTypeChoices.TYPE_VIRTUAL, + ), ) Interface.objects.bulk_create(child_interfaces) @@ -3860,9 +4190,24 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil # Create bridged interfaces bridge_interface = Interface.objects.first() bridged_interfaces = ( - Interface(device=bridge_interface.device, name='Bridged 1', bridge=bridge_interface, type=InterfaceTypeChoices.TYPE_1GE_FIXED), - Interface(device=bridge_interface.device, name='Bridged 2', bridge=bridge_interface, type=InterfaceTypeChoices.TYPE_1GE_FIXED), - Interface(device=bridge_interface.device, name='Bridged 3', bridge=bridge_interface, type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface( + device=bridge_interface.device, + name='Bridged 1', + bridge=bridge_interface, + type=InterfaceTypeChoices.TYPE_1GE_FIXED, + ), + Interface( + device=bridge_interface.device, + name='Bridged 2', + bridge=bridge_interface, + type=InterfaceTypeChoices.TYPE_1GE_FIXED, + ), + Interface( + device=bridge_interface.device, + name='Bridged 3', + bridge=bridge_interface, + type=InterfaceTypeChoices.TYPE_1GE_FIXED, + ), ) Interface.objects.bulk_create(bridged_interfaces) @@ -4017,6 +4362,20 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil params = {'vdc_identifier': vdc.values_list('identifier', flat=True)} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) + def test_vlan(self): + vlan = VLAN.objects.filter(qinq_role=VLANQinQRoleChoices.ROLE_SERVICE).first() + params = {'vlan_id': vlan.pk} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + params = {'vlan': vlan.vid} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + def test_vlan_translation_policy(self): + vlan_translation_policies = VLANTranslationPolicy.objects.all()[:2] + params = {'vlan_translation_policy_id': [vlan_translation_policies[0].pk, vlan_translation_policies[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + params = {'vlan_translation_policy': [vlan_translation_policies[0].name, vlan_translation_policies[1].name]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + class FrontPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests): queryset = FrontPort.objects.all() @@ -4081,10 +4440,41 @@ class FrontPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil Rack.objects.bulk_create(racks) devices = ( - Device(name='Device 1', device_type=device_types[0], role=roles[0], site=sites[0], location=locations[0], rack=racks[0], status='active'), - Device(name='Device 2', device_type=device_types[1], role=roles[1], site=sites[1], location=locations[1], rack=racks[1], status='planned'), - Device(name='Device 3', device_type=device_types[2], role=roles[2], site=sites[2], location=locations[2], rack=racks[2], status='offline'), - Device(name=None, device_type=device_types[2], role=roles[2], site=sites[3], status='offline'), # For cable connections + Device( + name='Device 1', + device_type=device_types[0], + role=roles[0], + site=sites[0], + location=locations[0], + rack=racks[0], + status='active', + ), + Device( + name='Device 2', + device_type=device_types[1], + role=roles[1], + site=sites[1], + location=locations[1], + rack=racks[1], + status='planned', + ), + Device( + name='Device 3', + device_type=device_types[2], + role=roles[2], + site=sites[2], + location=locations[2], + rack=racks[2], + status='offline', + ), + # For cable connections + Device( + name=None, + device_type=device_types[2], + role=roles[2], + site=sites[3], + status='offline' + ), ) Device.objects.bulk_create(devices) @@ -4114,12 +4504,63 @@ class FrontPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil RearPort.objects.bulk_create(rear_ports) front_ports = ( - FrontPort(device=devices[0], module=modules[0], name='Front Port 1', label='A', type=PortTypeChoices.TYPE_8P8C, color=ColorChoices.COLOR_RED, rear_port=rear_ports[0], rear_port_position=1, description='First'), - FrontPort(device=devices[1], module=modules[1], name='Front Port 2', label='B', type=PortTypeChoices.TYPE_110_PUNCH, color=ColorChoices.COLOR_GREEN, rear_port=rear_ports[1], rear_port_position=2, description='Second'), - FrontPort(device=devices[2], module=modules[2], name='Front Port 3', label='C', type=PortTypeChoices.TYPE_BNC, color=ColorChoices.COLOR_BLUE, rear_port=rear_ports[2], rear_port_position=3, description='Third'), - FrontPort(device=devices[3], name='Front Port 4', label='D', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[3], rear_port_position=1), - FrontPort(device=devices[3], name='Front Port 5', label='E', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[4], rear_port_position=1), - FrontPort(device=devices[3], name='Front Port 6', label='F', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[5], rear_port_position=1), + FrontPort( + device=devices[0], + module=modules[0], + name='Front Port 1', + label='A', + type=PortTypeChoices.TYPE_8P8C, + color=ColorChoices.COLOR_RED, + rear_port=rear_ports[0], + rear_port_position=1, + description='First', + ), + FrontPort( + device=devices[1], + module=modules[1], + name='Front Port 2', + label='B', + type=PortTypeChoices.TYPE_110_PUNCH, + color=ColorChoices.COLOR_GREEN, + rear_port=rear_ports[1], + rear_port_position=2, + description='Second', + ), + FrontPort( + device=devices[2], + module=modules[2], + name='Front Port 3', + label='C', + type=PortTypeChoices.TYPE_BNC, + color=ColorChoices.COLOR_BLUE, + rear_port=rear_ports[2], + rear_port_position=3, + description='Third', + ), + FrontPort( + device=devices[3], + name='Front Port 4', + label='D', + type=PortTypeChoices.TYPE_FC, + rear_port=rear_ports[3], + rear_port_position=1, + ), + FrontPort( + device=devices[3], + name='Front Port 5', + label='E', + type=PortTypeChoices.TYPE_FC, + rear_port=rear_ports[4], + rear_port_position=1, + ), + FrontPort( + device=devices[3], + name='Front Port 6', + label='F', + type=PortTypeChoices.TYPE_FC, + rear_port=rear_ports[5], + rear_port_position=1, + ), ) FrontPort.objects.bulk_create(front_ports) @@ -4271,10 +4712,41 @@ class RearPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilt Rack.objects.bulk_create(racks) devices = ( - Device(name='Device 1', device_type=device_types[0], role=roles[0], site=sites[0], location=locations[0], rack=racks[0], status='active'), - Device(name='Device 2', device_type=device_types[1], role=roles[1], site=sites[1], location=locations[1], rack=racks[1], status='planned'), - Device(name='Device 3', device_type=device_types[2], role=roles[2], site=sites[2], location=locations[2], rack=racks[2], status='offline'), - Device(name=None, device_type=device_types[2], role=roles[2], site=sites[3], status='offline'), # For cable connections + Device( + name='Device 1', + device_type=device_types[0], + role=roles[0], + site=sites[0], + location=locations[0], + rack=racks[0], + status='active', + ), + Device( + name='Device 2', + device_type=device_types[1], + role=roles[1], + site=sites[1], + location=locations[1], + rack=racks[1], + status='planned', + ), + Device( + name='Device 3', + device_type=device_types[2], + role=roles[2], + site=sites[2], + location=locations[2], + rack=racks[2], + status='offline', + ), + # For cable connections + Device( + name=None, + device_type=device_types[2], + role=roles[2], + site=sites[3], + status='offline' + ), ) Device.objects.bulk_create(devices) @@ -4294,9 +4766,36 @@ class RearPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilt Module.objects.bulk_create(modules) rear_ports = ( - RearPort(device=devices[0], module=modules[0], name='Rear Port 1', label='A', type=PortTypeChoices.TYPE_8P8C, color=ColorChoices.COLOR_RED, positions=1, description='First'), - RearPort(device=devices[1], module=modules[1], name='Rear Port 2', label='B', type=PortTypeChoices.TYPE_110_PUNCH, color=ColorChoices.COLOR_GREEN, positions=2, description='Second'), - RearPort(device=devices[2], module=modules[2], name='Rear Port 3', label='C', type=PortTypeChoices.TYPE_BNC, color=ColorChoices.COLOR_BLUE, positions=3, description='Third'), + RearPort( + device=devices[0], + module=modules[0], + name='Rear Port 1', + label='A', + type=PortTypeChoices.TYPE_8P8C, + color=ColorChoices.COLOR_RED, + positions=1, + description='First', + ), + RearPort( + device=devices[1], + module=modules[1], + name='Rear Port 2', + label='B', + type=PortTypeChoices.TYPE_110_PUNCH, + color=ColorChoices.COLOR_GREEN, + positions=2, + description='Second', + ), + RearPort( + device=devices[2], + module=modules[2], + name='Rear Port 3', + label='C', + type=PortTypeChoices.TYPE_BNC, + color=ColorChoices.COLOR_BLUE, + positions=3, + description='Third', + ), RearPort(device=devices[3], name='Rear Port 4', label='D', type=PortTypeChoices.TYPE_FC, positions=4), RearPort(device=devices[3], name='Rear Port 5', label='E', type=PortTypeChoices.TYPE_FC, positions=5), RearPort(device=devices[3], name='Rear Port 6', label='F', type=PortTypeChoices.TYPE_FC, positions=6), @@ -4453,9 +4952,33 @@ class ModuleBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil Rack.objects.bulk_create(racks) devices = ( - Device(name='Device 1', device_type=device_types[0], role=roles[0], site=sites[0], location=locations[0], rack=racks[0], status='active'), - Device(name='Device 2', device_type=device_types[1], role=roles[1], site=sites[1], location=locations[1], rack=racks[1], status='planned'), - Device(name='Device 3', device_type=device_types[2], role=roles[2], site=sites[2], location=locations[2], rack=racks[2], status='offline'), + Device( + name='Device 1', + device_type=device_types[0], + role=roles[0], + site=sites[0], + location=locations[0], + rack=racks[0], + status='active', + ), + Device( + name='Device 2', + device_type=device_types[1], + role=roles[1], + site=sites[1], + location=locations[1], + rack=racks[1], + status='planned', + ), + Device( + name='Device 3', + device_type=device_types[2], + role=roles[2], + site=sites[2], + location=locations[2], + rack=racks[2], + status='offline', + ), ) Device.objects.bulk_create(devices) @@ -4601,9 +5124,33 @@ class DeviceBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil Rack.objects.bulk_create(racks) devices = ( - Device(name='Device 1', device_type=device_types[0], role=roles[0], site=sites[0], location=locations[0], rack=racks[0], status='active'), - Device(name='Device 2', device_type=device_types[1], role=roles[1], site=sites[1], location=locations[1], rack=racks[1], status='planned'), - Device(name='Device 3', device_type=device_types[2], role=roles[2], site=sites[2], location=locations[2], rack=racks[2], status='offline'), + Device( + name='Device 1', + device_type=device_types[0], + role=roles[0], + site=sites[0], + location=locations[0], + rack=racks[0], + status='active', + ), + Device( + name='Device 2', + device_type=device_types[1], + role=roles[1], + site=sites[1], + location=locations[1], + rack=racks[1], + status='planned', + ), + Device( + name='Device 3', + device_type=device_types[2], + role=roles[2], + site=sites[2], + location=locations[2], + rack=racks[2], + status='offline', + ), ) Device.objects.bulk_create(devices) @@ -4735,9 +5282,30 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests): Rack.objects.bulk_create(racks) devices = ( - Device(name='Device 1', device_type=device_types[0], role=roles[0], site=sites[0], location=locations[0], rack=racks[0]), - Device(name='Device 2', device_type=device_types[1], role=roles[1], site=sites[1], location=locations[1], rack=racks[1]), - Device(name='Device 3', device_type=device_types[2], role=roles[2], site=sites[2], location=locations[2], rack=racks[2]), + Device( + name='Device 1', + device_type=device_types[0], + role=roles[0], + site=sites[0], + location=locations[0], + rack=racks[0], + ), + Device( + name='Device 2', + device_type=device_types[1], + role=roles[1], + site=sites[1], + location=locations[1], + rack=racks[1], + ), + Device( + name='Device 3', + device_type=device_types[2], + role=roles[2], + site=sites[2], + location=locations[2], + rack=racks[2], + ), ) Device.objects.bulk_create(devices) @@ -4755,9 +5323,48 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests): ) inventory_items = ( - InventoryItem(device=devices[0], role=roles[0], manufacturer=manufacturers[0], name='Inventory Item 1', label='A', part_id='1001', serial='ABC', asset_tag='1001', discovered=True, status=ModuleStatusChoices.STATUS_ACTIVE, description='First', component=components[0]), - InventoryItem(device=devices[1], role=roles[1], manufacturer=manufacturers[1], name='Inventory Item 2', label='B', part_id='1002', serial='DEF', asset_tag='1002', discovered=True, status=ModuleStatusChoices.STATUS_PLANNED, description='Second', component=components[1]), - InventoryItem(device=devices[2], role=roles[2], manufacturer=manufacturers[2], name='Inventory Item 3', label='C', part_id='1003', serial='GHI', asset_tag='1003', discovered=False, status=ModuleStatusChoices.STATUS_FAILED, description='Third', component=components[2]), + InventoryItem( + device=devices[0], + role=roles[0], + manufacturer=manufacturers[0], + name='Inventory Item 1', + label='A', + part_id='1001', + serial='ABC', + asset_tag='1001', + discovered=True, + status=ModuleStatusChoices.STATUS_ACTIVE, + description='First', + component=components[0], + ), + InventoryItem( + device=devices[1], + role=roles[1], + manufacturer=manufacturers[1], + name='Inventory Item 2', + label='B', + part_id='1002', + serial='DEF', + asset_tag='1002', + discovered=True, + status=ModuleStatusChoices.STATUS_PLANNED, + description='Second', + component=components[1], + ), + InventoryItem( + device=devices[2], + role=roles[2], + manufacturer=manufacturers[2], + name='Inventory Item 3', + label='C', + part_id='1003', + serial='GHI', + asset_tag='1003', + discovered=False, + status=ModuleStatusChoices.STATUS_FAILED, + description='Third', + component=components[2], + ), ) for i in inventory_items: i.save() @@ -5074,12 +5681,60 @@ class CableTestCase(TestCase, ChangeLoggedFilterSetTests): role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1') devices = ( - Device(name='Device 1', device_type=device_type, role=role, site=sites[0], rack=racks[0], location=locations[0], position=1), - Device(name='Device 2', device_type=device_type, role=role, site=sites[0], rack=racks[0], location=locations[0], position=2), - Device(name='Device 3', device_type=device_type, role=role, site=sites[1], rack=racks[1], location=locations[1], position=1), - Device(name='Device 4', device_type=device_type, role=role, site=sites[1], rack=racks[1], location=locations[1], position=2), - Device(name='Device 5', device_type=device_type, role=role, site=sites[2], rack=racks[2], location=locations[2], position=1), - Device(name='Device 6', device_type=device_type, role=role, site=sites[2], rack=racks[2], location=locations[2], position=2), + Device( + name='Device 1', + device_type=device_type, + role=role, + site=sites[0], + rack=racks[0], + location=locations[0], + position=1, + ), + Device( + name='Device 2', + device_type=device_type, + role=role, + site=sites[0], + rack=racks[0], + location=locations[0], + position=2, + ), + Device( + name='Device 3', + device_type=device_type, + role=role, + site=sites[1], + rack=racks[1], + location=locations[1], + position=1, + ), + Device( + name='Device 4', + device_type=device_type, + role=role, + site=sites[1], + rack=racks[1], + location=locations[1], + position=2, + ), + Device( + name='Device 5', + device_type=device_type, + role=role, + site=sites[2], + rack=racks[2], + location=locations[2], + position=1, + ), + Device( + name='Device 6', + device_type=device_type, + role=role, + site=sites[2], + rack=racks[2], + location=locations[2], + position=2, + ), ) Device.objects.bulk_create(devices) @@ -5118,7 +5773,7 @@ class CableTestCase(TestCase, ChangeLoggedFilterSetTests): provider = Provider.objects.create(name='Provider 1', slug='provider-1') circuit_type = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1') circuit = Circuit.objects.create(cid='Circuit 1', provider=provider, type=circuit_type) - circuit_termination = CircuitTermination.objects.create(circuit=circuit, term_side='A', site=sites[0]) + circuit_termination = CircuitTermination.objects.create(circuit=circuit, term_side='A', termination=sites[0]) # Cables cables = ( @@ -5291,9 +5946,9 @@ class CableTestCase(TestCase, ChangeLoggedFilterSetTests): def test_site(self): site = Site.objects.all()[:2] params = {'site_id': [site[0].pk, site[1].pk]} - self.assertEqual(self.filterset(params, self.queryset).qs.count(), 12) + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 11) params = {'site': [site[0].slug, site[1].slug]} - self.assertEqual(self.filterset(params, self.queryset).qs.count(), 12) + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 11) def test_tenant(self): tenant = Tenant.objects.all()[:2] @@ -5804,3 +6459,80 @@ class VirtualDeviceContextTestCase(TestCase, ChangeLoggedFilterSetTests): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) params = {'primary_ip6_id': [addresses[2].pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) + + +class MACAddressTestCase(TestCase, ChangeLoggedFilterSetTests): + queryset = MACAddress.objects.all() + filterset = MACAddressFilterSet + + @classmethod + def setUpTestData(cls): + devices = ( + create_test_device('Device 1'), + create_test_device('Device 2'), + create_test_device('Device 3'), + ) + interfaces = ( + Interface(device=devices[0], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface(device=devices[1], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + Interface(device=devices[2], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED), + ) + Interface.objects.bulk_create(interfaces) + + virtual_machines = ( + create_test_virtualmachine('Virtual Machine 1'), + create_test_virtualmachine('Virtual Machine 2'), + create_test_virtualmachine('Virtual Machine 3'), + ) + vm_interfaces = ( + VMInterface(virtual_machine=virtual_machines[0], name='Interface 1'), + VMInterface(virtual_machine=virtual_machines[1], name='Interface 2'), + VMInterface(virtual_machine=virtual_machines[2], name='Interface 3'), + ) + VMInterface.objects.bulk_create(vm_interfaces) + + mac_addresses = ( + # Device MACs + MACAddress(mac_address='00-00-00-01-01-01', assigned_object=interfaces[0]), + MACAddress(mac_address='00-00-00-02-01-01', assigned_object=interfaces[1]), + MACAddress(mac_address='00-00-00-03-01-01', assigned_object=interfaces[2]), + MACAddress(mac_address='00-00-00-03-01-02', assigned_object=interfaces[2]), + # VM MACs + MACAddress(mac_address='00-00-00-04-01-01', assigned_object=vm_interfaces[0]), + MACAddress(mac_address='00-00-00-05-01-01', assigned_object=vm_interfaces[1]), + MACAddress(mac_address='00-00-00-06-01-01', assigned_object=vm_interfaces[2]), + MACAddress(mac_address='00-00-00-06-01-02', assigned_object=vm_interfaces[2]), + ) + MACAddress.objects.bulk_create(mac_addresses) + + def test_mac_address(self): + params = {'mac_address': ['00-00-00-01-01-01', '00-00-00-02-01-01']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_device(self): + devices = Device.objects.all()[:2] + params = {'device_id': [devices[0].pk, devices[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'device': [devices[0].name, devices[1].name]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_virtual_machine(self): + virtual_machines = VirtualMachine.objects.all()[:2] + params = {'virtual_machine_id': [virtual_machines[0].pk, virtual_machines[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'virtual_machine': [virtual_machines[0].name, virtual_machines[1].name]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_interface(self): + interfaces = Interface.objects.all()[:2] + params = {'interface_id': [interfaces[0].pk, interfaces[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'interface': [interfaces[0].name, interfaces[1].name]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_vminterface(self): + vm_interfaces = VMInterface.objects.all()[:2] + params = {'vminterface_id': [vm_interfaces[0].pk, vm_interfaces[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'vminterface': [vm_interfaces[0].name, vm_interfaces[1].name]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index c11badbdd..ff1eddd56 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -601,24 +601,42 @@ class DeviceTestCase(TestCase): Site.objects.bulk_create(sites) clusters = ( - Cluster(name='Cluster 1', type=cluster_type, site=sites[0]), - Cluster(name='Cluster 2', type=cluster_type, site=sites[1]), - Cluster(name='Cluster 3', type=cluster_type, site=None), + Cluster(name='Cluster 1', type=cluster_type, scope=sites[0]), + Cluster(name='Cluster 2', type=cluster_type, scope=sites[1]), + Cluster(name='Cluster 3', type=cluster_type, scope=None), ) - Cluster.objects.bulk_create(clusters) + for cluster in clusters: + cluster.save() device_type = DeviceType.objects.first() device_role = DeviceRole.objects.first() # Device with site only should pass - Device(name='device1', site=sites[0], device_type=device_type, role=device_role).full_clean() + Device( + name='device1', + site=sites[0], + device_type=device_type, + role=device_role + ).full_clean() # Device with site, cluster non-site should pass - Device(name='device1', site=sites[0], device_type=device_type, role=device_role, cluster=clusters[2]).full_clean() + Device( + name='device1', + site=sites[0], + device_type=device_type, + role=device_role, + cluster=clusters[2] + ).full_clean() # Device with mismatched site & cluster should fail with self.assertRaises(ValidationError): - Device(name='device1', site=sites[0], device_type=device_type, role=device_role, cluster=clusters[1]).full_clean() + Device( + name='device1', + site=sites[0], + device_type=device_type, + role=device_role, + cluster=clusters[1] + ).full_clean() class ModuleBayTestCase(TestCase): @@ -635,7 +653,9 @@ class ModuleBayTestCase(TestCase): # Create a CustomField with a default value & assign it to all component models location = Location.objects.create(name='Location 1', slug='location-1', site=site) rack = Rack.objects.create(name='Rack 1', site=site) - device = Device.objects.create(name='Device 1', device_type=device_type, role=device_role, site=site, location=location, rack=rack) + device = Device.objects.create( + name='Device 1', device_type=device_type, role=device_role, site=site, location=location, rack=rack + ) module_bays = ( ModuleBay(device=device, name='Module Bay 1', label='A', description='First'), @@ -762,9 +782,9 @@ class CableTestCase(TestCase): circuittype = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1') circuit1 = Circuit.objects.create(provider=provider, type=circuittype, cid='1') circuit2 = Circuit.objects.create(provider=provider, type=circuittype, cid='2') - CircuitTermination.objects.create(circuit=circuit1, site=site, term_side='A') - CircuitTermination.objects.create(circuit=circuit1, site=site, term_side='Z') - CircuitTermination.objects.create(circuit=circuit2, provider_network=provider_network, term_side='A') + CircuitTermination.objects.create(circuit=circuit1, termination=site, term_side='A') + CircuitTermination.objects.create(circuit=circuit1, termination=site, term_side='Z') + CircuitTermination.objects.create(circuit=circuit2, termination=provider_network, term_side='A') def test_cable_creation(self): """ diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index ca0db5588..bb942c685 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -196,9 +196,27 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase): tenant = Tenant.objects.create(name='Tenant 1', slug='tenant-1') locations = ( - Location(name='Location 1', slug='location-1', site=site, status=LocationStatusChoices.STATUS_ACTIVE, tenant=tenant), - Location(name='Location 2', slug='location-2', site=site, status=LocationStatusChoices.STATUS_ACTIVE, tenant=tenant), - Location(name='Location 3', slug='location-3', site=site, status=LocationStatusChoices.STATUS_ACTIVE, tenant=tenant), + Location( + name='Location 1', + slug='location-1', + site=site, + status=LocationStatusChoices.STATUS_ACTIVE, + tenant=tenant, + ), + Location( + name='Location 2', + slug='location-2', + site=site, + status=LocationStatusChoices.STATUS_ACTIVE, + tenant=tenant, + ), + Location( + name='Location 3', + slug='location-3', + site=site, + status=LocationStatusChoices.STATUS_ACTIVE, + tenant=tenant, + ), ) for location in locations: location.save() @@ -346,9 +364,24 @@ class RackTypeTestCase(ViewTestCases.PrimaryObjectViewTestCase): Manufacturer.objects.bulk_create(manufacturers) rack_types = ( - RackType(manufacturer=manufacturers[0], model='RackType 1', slug='rack-type-1', form_factor=RackFormFactorChoices.TYPE_CABINET,), - RackType(manufacturer=manufacturers[0], model='RackType 2', slug='rack-type-2', form_factor=RackFormFactorChoices.TYPE_CABINET,), - RackType(manufacturer=manufacturers[0], model='RackType 3', slug='rack-type-3', form_factor=RackFormFactorChoices.TYPE_CABINET,), + RackType( + manufacturer=manufacturers[0], + model='RackType 1', + slug='rack-type-1', + form_factor=RackFormFactorChoices.TYPE_CABINET, + ), + RackType( + manufacturer=manufacturers[0], + model='RackType 2', + slug='rack-type-2', + form_factor=RackFormFactorChoices.TYPE_CABINET, + ), + RackType( + manufacturer=manufacturers[0], + model='RackType 3', + slug='rack-type-3', + form_factor=RackFormFactorChoices.TYPE_CABINET, + ), ) RackType.objects.bulk_create(rack_types) @@ -592,7 +625,7 @@ class DeviceTypeTestCase( 'part_number': '123ABC', 'u_height': 2, 'is_full_depth': True, - 'subdevice_role': '', # CharField + 'subdevice_role': None, 'comments': 'Some comments', 'tags': [t.pk for t in tags], } @@ -692,9 +725,15 @@ class DeviceTypeTestCase( ) RearPortTemplate.objects.bulk_create(rear_ports) front_ports = ( - FrontPortTemplate(device_type=devicetype, name='Front Port 1', rear_port=rear_ports[0], rear_port_position=1), - FrontPortTemplate(device_type=devicetype, name='Front Port 2', rear_port=rear_ports[1], rear_port_position=1), - FrontPortTemplate(device_type=devicetype, name='Front Port 3', rear_port=rear_ports[2], rear_port_position=1), + FrontPortTemplate( + device_type=devicetype, name='Front Port 1', rear_port=rear_ports[0], rear_port_position=1 + ), + FrontPortTemplate( + device_type=devicetype, name='Front Port 2', rear_port=rear_ports[1], rear_port_position=1 + ), + FrontPortTemplate( + device_type=devicetype, name='Front Port 3', rear_port=rear_ports[2], rear_port_position=1 + ), ) FrontPortTemplate.objects.bulk_create(front_ports) @@ -861,7 +900,7 @@ inventory-items: 'data': IMPORT_DATA, 'format': 'yaml' } - response = self.client.post(reverse('dcim:devicetype_import'), data=form_data, follow=True) + response = self.client.post(reverse('dcim:devicetype_bulk_import'), data=form_data, follow=True) self.assertHttpStatus(response, 200) device_type = DeviceType.objects.get(model='TEST-1000') @@ -1081,9 +1120,15 @@ class ModuleTypeTestCase( ) RearPortTemplate.objects.bulk_create(rear_ports) front_ports = ( - FrontPortTemplate(module_type=moduletype, name='Front Port 1', rear_port=rear_ports[0], rear_port_position=1), - FrontPortTemplate(module_type=moduletype, name='Front Port 2', rear_port=rear_ports[1], rear_port_position=1), - FrontPortTemplate(module_type=moduletype, name='Front Port 3', rear_port=rear_ports[2], rear_port_position=1), + FrontPortTemplate( + module_type=moduletype, name='Front Port 1', rear_port=rear_ports[0], rear_port_position=1 + ), + FrontPortTemplate( + module_type=moduletype, name='Front Port 2', rear_port=rear_ports[1], rear_port_position=1 + ), + FrontPortTemplate( + module_type=moduletype, name='Front Port 3', rear_port=rear_ports[2], rear_port_position=1 + ), ) FrontPortTemplate.objects.bulk_create(front_ports) @@ -1183,7 +1228,7 @@ front-ports: 'data': IMPORT_DATA, 'format': 'yaml' } - response = self.client.post(reverse('dcim:moduletype_import'), data=form_data, follow=True) + response = self.client.post(reverse('dcim:moduletype_bulk_import'), data=form_data, follow=True) self.assertHttpStatus(response, 200) module_type = ModuleType.objects.get(model='TEST-1000') @@ -1453,11 +1498,19 @@ class FrontPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCas ) RearPortTemplate.objects.bulk_create(rearports) - FrontPortTemplate.objects.bulk_create(( - FrontPortTemplate(device_type=devicetype, name='Front Port Template 1', rear_port=rearports[0], rear_port_position=1), - FrontPortTemplate(device_type=devicetype, name='Front Port Template 2', rear_port=rearports[1], rear_port_position=1), - FrontPortTemplate(device_type=devicetype, name='Front Port Template 3', rear_port=rearports[2], rear_port_position=1), - )) + FrontPortTemplate.objects.bulk_create( + ( + FrontPortTemplate( + device_type=devicetype, name='Front Port Template 1', rear_port=rearports[0], rear_port_position=1 + ), + FrontPortTemplate( + device_type=devicetype, name='Front Port Template 2', rear_port=rearports[1], rear_port_position=1 + ), + FrontPortTemplate( + device_type=devicetype, name='Front Port Template 3', rear_port=rearports[2], rear_port_position=1 + ), + ) + ) cls.form_data = { 'device_type': devicetype.pk, @@ -1550,7 +1603,12 @@ class DeviceBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCas @classmethod def setUpTestData(cls): manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1') - devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1', subdevice_role=SubdeviceRoleChoices.ROLE_PARENT) + devicetype = DeviceType.objects.create( + manufacturer=manufacturer, + model='Device Type 1', + slug='device-type-1', + subdevice_role=SubdeviceRoleChoices.ROLE_PARENT + ) DeviceBayTemplate.objects.bulk_create(( DeviceBayTemplate(device_type=devicetype, name='Device Bay Template 1'), @@ -1584,12 +1642,20 @@ class InventoryItemTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTes Manufacturer(name='Manufacturer 2', slug='manufacturer-2'), ) Manufacturer.objects.bulk_create(manufacturers) - devicetype = DeviceType.objects.create(manufacturer=manufacturers[0], model='Device Type 1', slug='device-type-1') + devicetype = DeviceType.objects.create( + manufacturer=manufacturers[0], model='Device Type 1', slug='device-type-1' + ) inventory_item_templates = ( - InventoryItemTemplate(device_type=devicetype, name='Inventory Item Template 1', manufacturer=manufacturers[0]), - InventoryItemTemplate(device_type=devicetype, name='Inventory Item Template 2', manufacturer=manufacturers[0]), - InventoryItemTemplate(device_type=devicetype, name='Inventory Item Template 3', manufacturer=manufacturers[0]), + InventoryItemTemplate( + device_type=devicetype, name='Inventory Item Template 1', manufacturer=manufacturers[0] + ), + InventoryItemTemplate( + device_type=devicetype, name='Inventory Item Template 2', manufacturer=manufacturers[0] + ), + InventoryItemTemplate( + device_type=devicetype, name='Inventory Item Template 3', manufacturer=manufacturers[0] + ), ) for item in inventory_item_templates: item.save() @@ -1741,9 +1807,30 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase): Platform.objects.bulk_create(platforms) devices = ( - Device(name='Device 1', site=sites[0], rack=racks[0], device_type=devicetypes[0], role=roles[0], platform=platforms[0]), - Device(name='Device 2', site=sites[0], rack=racks[0], device_type=devicetypes[0], role=roles[0], platform=platforms[0]), - Device(name='Device 3', site=sites[0], rack=racks[0], device_type=devicetypes[0], role=roles[0], platform=platforms[0]), + Device( + name='Device 1', + site=sites[0], + rack=racks[0], + device_type=devicetypes[0], + role=roles[0], + platform=platforms[0], + ), + Device( + name='Device 2', + site=sites[0], + rack=racks[0], + device_type=devicetypes[0], + role=roles[0], + platform=platforms[0], + ), + Device( + name='Device 3', + site=sites[0], + rack=racks[0], + device_type=devicetypes[0], + role=roles[0], + platform=platforms[0], + ), ) Device.objects.bulk_create(devices) @@ -1778,10 +1865,22 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase): } cls.csv_data = ( - "role,manufacturer,device_type,status,name,site,location,rack,position,face,virtual_chassis,vc_position,vc_priority", - "Device Role 1,Manufacturer 1,Device Type 1,active,Device 4,Site 1,Location 1,Rack 1,10,front,Virtual Chassis 1,1,10", - "Device Role 1,Manufacturer 1,Device Type 1,active,Device 5,Site 1,Location 1,Rack 1,20,front,Virtual Chassis 1,2,20", - "Device Role 1,Manufacturer 1,Device Type 1,active,Device 6,Site 1,Location 1,Rack 1,30,front,Virtual Chassis 1,3,30", + ( + "role,manufacturer,device_type,status,name,site,location,rack,position,face,virtual_chassis," + "vc_position,vc_priority" + ), + ( + "Device Role 1,Manufacturer 1,Device Type 1,active,Device 4,Site 1,Location 1,Rack 1,10,front," + "Virtual Chassis 1,1,10" + ), + ( + "Device Role 1,Manufacturer 1,Device Type 1,active,Device 5,Site 1,Location 1,Rack 1,20,front," + "Virtual Chassis 1,2,20" + ), + ( + "Device Role 1,Manufacturer 1,Device Type 1,active,Device 6,Site 1,Location 1,Rack 1,30,front," + "Virtual Chassis 1,3,30" + ), ) cls.csv_update_data = ( @@ -2071,7 +2170,7 @@ class ModuleTestCase( f"{device.name},{module_bay.name},{module_type.model},active,false" ] request = { - 'path': self._get_url('import'), + 'path': self._get_url('bulk_import'), 'data': { 'data': '\n'.join(csv_data), 'format': ImportFormatChoices.CSV, @@ -2088,7 +2187,7 @@ class ModuleTestCase( module_bay = ModuleBay.objects.get(device=device, name='Module Bay 5') csv_data[1] = f"{device.name},{module_bay.name},{module_type.model},active,true" request = { - 'path': self._get_url('import'), + 'path': self._get_url('bulk_import'), 'data': { 'data': '\n'.join(csv_data), 'format': ImportFormatChoices.CSV, @@ -2165,7 +2264,7 @@ class ModuleTestCase( f"{device.name},{module_bay.name},{module_type.model},active,false,true" ] request = { - 'path': self._get_url('import'), + 'path': self._get_url('bulk_import'), 'data': { 'data': '\n'.join(csv_data), 'format': ImportFormatChoices.CSV, @@ -2508,7 +2607,6 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase): 'enabled': False, 'bridge': interfaces[4].pk, 'lag': interfaces[3].pk, - 'mac_address': EUI('01:02:03:04:05:06'), 'wwn': EUI('01:02:03:04:05:06:07:08', version=64), 'mtu': 65000, 'speed': 1000000, @@ -2533,7 +2631,6 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase): 'enabled': False, 'bridge': interfaces[4].pk, 'lag': interfaces[3].pk, - 'mac_address': EUI('01:02:03:04:05:06'), 'wwn': EUI('01:02:03:04:05:06:07:08', version=64), 'mtu': 2000, 'speed': 100000, @@ -2554,7 +2651,6 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase): 'type': InterfaceTypeChoices.TYPE_1GE_FIXED, 'enabled': True, 'lag': interfaces[3].pk, - 'mac_address': EUI('01:02:03:04:05:06'), 'wwn': EUI('01:02:03:04:05:06:07:08', version=64), 'mtu': 2000, 'speed': 1000000, @@ -2887,9 +2983,15 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase): ) InventoryItemRole.objects.bulk_create(roles) - inventory_item1 = InventoryItem.objects.create(device=device, name='Inventory Item 1', role=roles[0], manufacturer=manufacturer) - inventory_item2 = InventoryItem.objects.create(device=device, name='Inventory Item 2', role=roles[0], manufacturer=manufacturer) - inventory_item3 = InventoryItem.objects.create(device=device, name='Inventory Item 3', role=roles[0], manufacturer=manufacturer) + inventory_item1 = InventoryItem.objects.create( + device=device, name='Inventory Item 1', role=roles[0], manufacturer=manufacturer + ) + inventory_item2 = InventoryItem.objects.create( + device=device, name='Inventory Item 2', role=roles[0], manufacturer=manufacturer + ) + inventory_item3 = InventoryItem.objects.create( + device=device, name='Inventory Item 3', role=roles[0], manufacturer=manufacturer + ) tags = create_tags('Alpha', 'Bravo', 'Charlie') diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 627136bf9..bcfd32707 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -6,327 +6,144 @@ from . import views app_name = 'dcim' urlpatterns = [ - # Regions - path('regions/', views.RegionListView.as_view(), name='region_list'), - path('regions/add/', views.RegionEditView.as_view(), name='region_add'), - path('regions/import/', views.RegionBulkImportView.as_view(), name='region_import'), - path('regions/edit/', views.RegionBulkEditView.as_view(), name='region_bulk_edit'), - path('regions/delete/', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'), + path('regions/', include(get_model_urls('dcim', 'region', detail=False))), path('regions//', include(get_model_urls('dcim', 'region'))), - # Site groups - path('site-groups/', views.SiteGroupListView.as_view(), name='sitegroup_list'), - path('site-groups/add/', views.SiteGroupEditView.as_view(), name='sitegroup_add'), - path('site-groups/import/', views.SiteGroupBulkImportView.as_view(), name='sitegroup_import'), - path('site-groups/edit/', views.SiteGroupBulkEditView.as_view(), name='sitegroup_bulk_edit'), - path('site-groups/delete/', views.SiteGroupBulkDeleteView.as_view(), name='sitegroup_bulk_delete'), + path('site-groups/', include(get_model_urls('dcim', 'sitegroup', detail=False))), path('site-groups//', include(get_model_urls('dcim', 'sitegroup'))), - # Sites - path('sites/', views.SiteListView.as_view(), name='site_list'), - path('sites/add/', views.SiteEditView.as_view(), name='site_add'), - path('sites/import/', views.SiteBulkImportView.as_view(), name='site_import'), - path('sites/edit/', views.SiteBulkEditView.as_view(), name='site_bulk_edit'), - path('sites/delete/', views.SiteBulkDeleteView.as_view(), name='site_bulk_delete'), + path('sites/', include(get_model_urls('dcim', 'site', detail=False))), path('sites//', include(get_model_urls('dcim', 'site'))), - # Locations - path('locations/', views.LocationListView.as_view(), name='location_list'), - path('locations/add/', views.LocationEditView.as_view(), name='location_add'), - path('locations/import/', views.LocationBulkImportView.as_view(), name='location_import'), - path('locations/edit/', views.LocationBulkEditView.as_view(), name='location_bulk_edit'), - path('locations/delete/', views.LocationBulkDeleteView.as_view(), name='location_bulk_delete'), + path('locations/', include(get_model_urls('dcim', 'location', detail=False))), path('locations//', include(get_model_urls('dcim', 'location'))), - # Rack roles - path('rack-roles/', views.RackRoleListView.as_view(), name='rackrole_list'), - path('rack-roles/add/', views.RackRoleEditView.as_view(), name='rackrole_add'), - path('rack-roles/import/', views.RackRoleBulkImportView.as_view(), name='rackrole_import'), - path('rack-roles/edit/', views.RackRoleBulkEditView.as_view(), name='rackrole_bulk_edit'), - path('rack-roles/delete/', views.RackRoleBulkDeleteView.as_view(), name='rackrole_bulk_delete'), + path('rack-roles/', include(get_model_urls('dcim', 'rackrole', detail=False))), path('rack-roles//', include(get_model_urls('dcim', 'rackrole'))), - # Rack reservations - path('rack-reservations/', views.RackReservationListView.as_view(), name='rackreservation_list'), - path('rack-reservations/add/', views.RackReservationEditView.as_view(), name='rackreservation_add'), - path('rack-reservations/import/', views.RackReservationImportView.as_view(), name='rackreservation_import'), - path('rack-reservations/edit/', views.RackReservationBulkEditView.as_view(), name='rackreservation_bulk_edit'), - path('rack-reservations/delete/', views.RackReservationBulkDeleteView.as_view(), name='rackreservation_bulk_delete'), + path('rack-reservations/', include(get_model_urls('dcim', 'rackreservation', detail=False))), path('rack-reservations//', include(get_model_urls('dcim', 'rackreservation'))), - # Racks - path('racks/', views.RackListView.as_view(), name='rack_list'), - path('rack-elevations/', views.RackElevationListView.as_view(), name='rack_elevation_list'), - path('racks/add/', views.RackEditView.as_view(), name='rack_add'), - path('racks/import/', views.RackBulkImportView.as_view(), name='rack_import'), - path('racks/edit/', views.RackBulkEditView.as_view(), name='rack_bulk_edit'), - path('racks/delete/', views.RackBulkDeleteView.as_view(), name='rack_bulk_delete'), + path('racks/', include(get_model_urls('dcim', 'rack', detail=False))), path('racks//', include(get_model_urls('dcim', 'rack'))), + path('rack-elevations/', views.RackElevationListView.as_view(), name='rack_elevation_list'), - # Rack Types - path('rack-types/', views.RackTypeListView.as_view(), name='racktype_list'), - path('rack-types/add/', views.RackTypeEditView.as_view(), name='racktype_add'), - path('rack-types/import/', views.RackTypeBulkImportView.as_view(), name='racktype_import'), - path('rack-types/edit/', views.RackTypeBulkEditView.as_view(), name='racktype_bulk_edit'), - path('rack-types/delete/', views.RackTypeBulkDeleteView.as_view(), name='racktype_bulk_delete'), + path('rack-types/', include(get_model_urls('dcim', 'racktype', detail=False))), path('rack-types//', include(get_model_urls('dcim', 'racktype'))), - # Manufacturers - path('manufacturers/', views.ManufacturerListView.as_view(), name='manufacturer_list'), - path('manufacturers/add/', views.ManufacturerEditView.as_view(), name='manufacturer_add'), - path('manufacturers/import/', views.ManufacturerBulkImportView.as_view(), name='manufacturer_import'), - path('manufacturers/edit/', views.ManufacturerBulkEditView.as_view(), name='manufacturer_bulk_edit'), - path('manufacturers/delete/', views.ManufacturerBulkDeleteView.as_view(), name='manufacturer_bulk_delete'), + path('manufacturers/', include(get_model_urls('dcim', 'manufacturer', detail=False))), path('manufacturers//', include(get_model_urls('dcim', 'manufacturer'))), - # Device types - path('device-types/', views.DeviceTypeListView.as_view(), name='devicetype_list'), - path('device-types/add/', views.DeviceTypeEditView.as_view(), name='devicetype_add'), - path('device-types/import/', views.DeviceTypeImportView.as_view(), name='devicetype_import'), - path('device-types/edit/', views.DeviceTypeBulkEditView.as_view(), name='devicetype_bulk_edit'), - path('device-types/delete/', views.DeviceTypeBulkDeleteView.as_view(), name='devicetype_bulk_delete'), + path('device-types/', include(get_model_urls('dcim', 'devicetype', detail=False))), path('device-types//', include(get_model_urls('dcim', 'devicetype'))), - # Module types - path('module-types/', views.ModuleTypeListView.as_view(), name='moduletype_list'), - path('module-types/add/', views.ModuleTypeEditView.as_view(), name='moduletype_add'), - path('module-types/import/', views.ModuleTypeImportView.as_view(), name='moduletype_import'), - path('module-types/edit/', views.ModuleTypeBulkEditView.as_view(), name='moduletype_bulk_edit'), - path('module-types/delete/', views.ModuleTypeBulkDeleteView.as_view(), name='moduletype_bulk_delete'), + path('module-types/', include(get_model_urls('dcim', 'moduletype', detail=False))), path('module-types//', include(get_model_urls('dcim', 'moduletype'))), - # Console port templates - path('console-port-templates/add/', views.ConsolePortTemplateCreateView.as_view(), name='consoleporttemplate_add'), - path('console-port-templates/edit/', views.ConsolePortTemplateBulkEditView.as_view(), name='consoleporttemplate_bulk_edit'), - path('console-port-templates/rename/', views.ConsolePortTemplateBulkRenameView.as_view(), name='consoleporttemplate_bulk_rename'), - path('console-port-templates/delete/', views.ConsolePortTemplateBulkDeleteView.as_view(), name='consoleporttemplate_bulk_delete'), + path('console-port-templates/', include(get_model_urls('dcim', 'consoleporttemplate', detail=False))), path('console-port-templates//', include(get_model_urls('dcim', 'consoleporttemplate'))), - # Console server port templates - path('console-server-port-templates/add/', views.ConsoleServerPortTemplateCreateView.as_view(), name='consoleserverporttemplate_add'), - path('console-server-port-templates/edit/', views.ConsoleServerPortTemplateBulkEditView.as_view(), name='consoleserverporttemplate_bulk_edit'), - path('console-server-port-templates/rename/', views.ConsoleServerPortTemplateBulkRenameView.as_view(), name='consoleserverporttemplate_bulk_rename'), - path('console-server-port-templates/delete/', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='consoleserverporttemplate_bulk_delete'), + path('console-server-port-templates/', include(get_model_urls('dcim', 'consoleserverporttemplate', detail=False))), path('console-server-port-templates//', include(get_model_urls('dcim', 'consoleserverporttemplate'))), - # Power port templates - path('power-port-templates/add/', views.PowerPortTemplateCreateView.as_view(), name='powerporttemplate_add'), - path('power-port-templates/edit/', views.PowerPortTemplateBulkEditView.as_view(), name='powerporttemplate_bulk_edit'), - path('power-port-templates/rename/', views.PowerPortTemplateBulkRenameView.as_view(), name='powerporttemplate_bulk_rename'), - path('power-port-templates/delete/', views.PowerPortTemplateBulkDeleteView.as_view(), name='powerporttemplate_bulk_delete'), + path('power-port-templates/', include(get_model_urls('dcim', 'powerporttemplate', detail=False))), path('power-port-templates//', include(get_model_urls('dcim', 'powerporttemplate'))), - # Power outlet templates - path('power-outlet-templates/add/', views.PowerOutletTemplateCreateView.as_view(), name='poweroutlettemplate_add'), - path('power-outlet-templates/edit/', views.PowerOutletTemplateBulkEditView.as_view(), name='poweroutlettemplate_bulk_edit'), - path('power-outlet-templates/rename/', views.PowerOutletTemplateBulkRenameView.as_view(), name='poweroutlettemplate_bulk_rename'), - path('power-outlet-templates/delete/', views.PowerOutletTemplateBulkDeleteView.as_view(), name='poweroutlettemplate_bulk_delete'), + path('power-outlet-templates/', include(get_model_urls('dcim', 'poweroutlettemplate', detail=False))), path('power-outlet-templates//', include(get_model_urls('dcim', 'poweroutlettemplate'))), - # Interface templates - path('interface-templates/add/', views.InterfaceTemplateCreateView.as_view(), name='interfacetemplate_add'), - path('interface-templates/edit/', views.InterfaceTemplateBulkEditView.as_view(), name='interfacetemplate_bulk_edit'), - path('interface-templates/rename/', views.InterfaceTemplateBulkRenameView.as_view(), name='interfacetemplate_bulk_rename'), - path('interface-templates/delete/', views.InterfaceTemplateBulkDeleteView.as_view(), name='interfacetemplate_bulk_delete'), + path('interface-templates/', include(get_model_urls('dcim', 'interfacetemplate', detail=False))), path('interface-templates//', include(get_model_urls('dcim', 'interfacetemplate'))), - # Front port templates - path('front-port-templates/add/', views.FrontPortTemplateCreateView.as_view(), name='frontporttemplate_add'), - path('front-port-templates/edit/', views.FrontPortTemplateBulkEditView.as_view(), name='frontporttemplate_bulk_edit'), - path('front-port-templates/rename/', views.FrontPortTemplateBulkRenameView.as_view(), name='frontporttemplate_bulk_rename'), - path('front-port-templates/delete/', views.FrontPortTemplateBulkDeleteView.as_view(), name='frontporttemplate_bulk_delete'), + path('front-port-templates/', include(get_model_urls('dcim', 'frontporttemplate', detail=False))), path('front-port-templates//', include(get_model_urls('dcim', 'frontporttemplate'))), - # Rear port templates - path('rear-port-templates/add/', views.RearPortTemplateCreateView.as_view(), name='rearporttemplate_add'), - path('rear-port-templates/edit/', views.RearPortTemplateBulkEditView.as_view(), name='rearporttemplate_bulk_edit'), - path('rear-port-templates/rename/', views.RearPortTemplateBulkRenameView.as_view(), name='rearporttemplate_bulk_rename'), - path('rear-port-templates/delete/', views.RearPortTemplateBulkDeleteView.as_view(), name='rearporttemplate_bulk_delete'), + path('rear-port-templates/', include(get_model_urls('dcim', 'rearporttemplate', detail=False))), path('rear-port-templates//', include(get_model_urls('dcim', 'rearporttemplate'))), - # Device bay templates - path('device-bay-templates/add/', views.DeviceBayTemplateCreateView.as_view(), name='devicebaytemplate_add'), - path('device-bay-templates/edit/', views.DeviceBayTemplateBulkEditView.as_view(), name='devicebaytemplate_bulk_edit'), - path('device-bay-templates/rename/', views.DeviceBayTemplateBulkRenameView.as_view(), name='devicebaytemplate_bulk_rename'), - path('device-bay-templates/delete/', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicebaytemplate_bulk_delete'), + path('device-bay-templates/', include(get_model_urls('dcim', 'devicebaytemplate', detail=False))), path('device-bay-templates//', include(get_model_urls('dcim', 'devicebaytemplate'))), - # Module bay templates - path('module-bay-templates/add/', views.ModuleBayTemplateCreateView.as_view(), name='modulebaytemplate_add'), - path('module-bay-templates/edit/', views.ModuleBayTemplateBulkEditView.as_view(), name='modulebaytemplate_bulk_edit'), - path('module-bay-templates/rename/', views.ModuleBayTemplateBulkRenameView.as_view(), name='modulebaytemplate_bulk_rename'), - path('module-bay-templates/delete/', views.ModuleBayTemplateBulkDeleteView.as_view(), name='modulebaytemplate_bulk_delete'), + path('module-bay-templates/', include(get_model_urls('dcim', 'modulebaytemplate', detail=False))), path('module-bay-templates//', include(get_model_urls('dcim', 'modulebaytemplate'))), - # Inventory item templates - path('inventory-item-templates/add/', views.InventoryItemTemplateCreateView.as_view(), name='inventoryitemtemplate_add'), - path('inventory-item-templates/edit/', views.InventoryItemTemplateBulkEditView.as_view(), name='inventoryitemtemplate_bulk_edit'), - path('inventory-item-templates/rename/', views.InventoryItemTemplateBulkRenameView.as_view(), name='inventoryitemtemplate_bulk_rename'), - path('inventory-item-templates/delete/', views.InventoryItemTemplateBulkDeleteView.as_view(), name='inventoryitemtemplate_bulk_delete'), + path('inventory-item-templates/', include(get_model_urls('dcim', 'inventoryitemtemplate', detail=False))), path('inventory-item-templates//', include(get_model_urls('dcim', 'inventoryitemtemplate'))), - # Device roles - path('device-roles/', views.DeviceRoleListView.as_view(), name='devicerole_list'), - path('device-roles/add/', views.DeviceRoleEditView.as_view(), name='devicerole_add'), - path('device-roles/import/', views.DeviceRoleBulkImportView.as_view(), name='devicerole_import'), - path('device-roles/edit/', views.DeviceRoleBulkEditView.as_view(), name='devicerole_bulk_edit'), - path('device-roles/delete/', views.DeviceRoleBulkDeleteView.as_view(), name='devicerole_bulk_delete'), + path('device-roles/', include(get_model_urls('dcim', 'devicerole', detail=False))), path('device-roles//', include(get_model_urls('dcim', 'devicerole'))), - # Platforms - path('platforms/', views.PlatformListView.as_view(), name='platform_list'), - path('platforms/add/', views.PlatformEditView.as_view(), name='platform_add'), - path('platforms/import/', views.PlatformBulkImportView.as_view(), name='platform_import'), - path('platforms/edit/', views.PlatformBulkEditView.as_view(), name='platform_bulk_edit'), - path('platforms/delete/', views.PlatformBulkDeleteView.as_view(), name='platform_bulk_delete'), + path('platforms/', include(get_model_urls('dcim', 'platform', detail=False))), path('platforms//', include(get_model_urls('dcim', 'platform'))), - # Devices - path('devices/', views.DeviceListView.as_view(), name='device_list'), - path('devices/add/', views.DeviceEditView.as_view(), name='device_add'), - path('devices/import/', views.DeviceBulkImportView.as_view(), name='device_import'), - path('devices/edit/', views.DeviceBulkEditView.as_view(), name='device_bulk_edit'), - path('devices/rename/', views.DeviceBulkRenameView.as_view(), name='device_bulk_rename'), - path('devices/delete/', views.DeviceBulkDeleteView.as_view(), name='device_bulk_delete'), + path('devices/', include(get_model_urls('dcim', 'device', detail=False))), path('devices//', include(get_model_urls('dcim', 'device'))), - # Virtual Device Context - path('virtual-device-contexts/', views.VirtualDeviceContextListView.as_view(), name='virtualdevicecontext_list'), - path('virtual-device-contexts/add/', views.VirtualDeviceContextEditView.as_view(), name='virtualdevicecontext_add'), - path('virtual-device-contexts/import/', views.VirtualDeviceContextBulkImportView.as_view(), name='virtualdevicecontext_import'), - path('virtual-device-contexts/edit/', views.VirtualDeviceContextBulkEditView.as_view(), name='virtualdevicecontext_bulk_edit'), - path('virtual-device-contexts/delete/', views.VirtualDeviceContextBulkDeleteView.as_view(), name='virtualdevicecontext_bulk_delete'), + path('virtual-device-contexts/', include(get_model_urls('dcim', 'virtualdevicecontext', detail=False))), path('virtual-device-contexts//', include(get_model_urls('dcim', 'virtualdevicecontext'))), - # Modules - path('modules/', views.ModuleListView.as_view(), name='module_list'), - path('modules/add/', views.ModuleEditView.as_view(), name='module_add'), - path('modules/import/', views.ModuleBulkImportView.as_view(), name='module_import'), - path('modules/edit/', views.ModuleBulkEditView.as_view(), name='module_bulk_edit'), - path('modules/delete/', views.ModuleBulkDeleteView.as_view(), name='module_bulk_delete'), + path('modules/', include(get_model_urls('dcim', 'module', detail=False))), path('modules//', include(get_model_urls('dcim', 'module'))), - # Console ports - path('console-ports/', views.ConsolePortListView.as_view(), name='consoleport_list'), - path('console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'), - path('console-ports/import/', views.ConsolePortBulkImportView.as_view(), name='consoleport_import'), - path('console-ports/edit/', views.ConsolePortBulkEditView.as_view(), name='consoleport_bulk_edit'), - path('console-ports/rename/', views.ConsolePortBulkRenameView.as_view(), name='consoleport_bulk_rename'), - path('console-ports/disconnect/', views.ConsolePortBulkDisconnectView.as_view(), name='consoleport_bulk_disconnect'), - path('console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'), + path('console-ports/', include(get_model_urls('dcim', 'consoleport', detail=False))), path('console-ports//', include(get_model_urls('dcim', 'consoleport'))), - path('devices/console-ports/add/', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'), + path( + 'devices/console-ports/add/', + views.DeviceBulkAddConsolePortView.as_view(), + name='device_bulk_add_consoleport' + ), - # Console server ports - path('console-server-ports/', views.ConsoleServerPortListView.as_view(), name='consoleserverport_list'), - path('console-server-ports/add/', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'), - path('console-server-ports/import/', views.ConsoleServerPortBulkImportView.as_view(), name='consoleserverport_import'), - path('console-server-ports/edit/', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'), - path('console-server-ports/rename/', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'), - path('console-server-ports/disconnect/', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'), - path('console-server-ports/delete/', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'), + path('console-server-ports/', include(get_model_urls('dcim', 'consoleserverport', detail=False))), path('console-server-ports//', include(get_model_urls('dcim', 'consoleserverport'))), - path('devices/console-server-ports/add/', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'), + path( + 'devices/console-server-ports/add/', + views.DeviceBulkAddConsoleServerPortView.as_view(), + name='device_bulk_add_consoleserverport' + ), - # Power ports - path('power-ports/', views.PowerPortListView.as_view(), name='powerport_list'), - path('power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'), - path('power-ports/import/', views.PowerPortBulkImportView.as_view(), name='powerport_import'), - path('power-ports/edit/', views.PowerPortBulkEditView.as_view(), name='powerport_bulk_edit'), - path('power-ports/rename/', views.PowerPortBulkRenameView.as_view(), name='powerport_bulk_rename'), - path('power-ports/disconnect/', views.PowerPortBulkDisconnectView.as_view(), name='powerport_bulk_disconnect'), - path('power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'), + path('power-ports/', include(get_model_urls('dcim', 'powerport', detail=False))), path('power-ports//', include(get_model_urls('dcim', 'powerport'))), path('devices/power-ports/add/', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'), - # Power outlets - path('power-outlets/', views.PowerOutletListView.as_view(), name='poweroutlet_list'), - path('power-outlets/add/', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'), - path('power-outlets/import/', views.PowerOutletBulkImportView.as_view(), name='poweroutlet_import'), - path('power-outlets/edit/', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'), - path('power-outlets/rename/', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'), - path('power-outlets/disconnect/', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'), - path('power-outlets/delete/', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'), + path('power-outlets/', include(get_model_urls('dcim', 'poweroutlet', detail=False))), path('power-outlets//', include(get_model_urls('dcim', 'poweroutlet'))), - path('devices/power-outlets/add/', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'), + path( + 'devices/power-outlets/add/', + views.DeviceBulkAddPowerOutletView.as_view(), + name='device_bulk_add_poweroutlet' + ), - # Interfaces - path('interfaces/', views.InterfaceListView.as_view(), name='interface_list'), - path('interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'), - path('interfaces/import/', views.InterfaceBulkImportView.as_view(), name='interface_import'), - path('interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'), - path('interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'), - path('interfaces/disconnect/', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'), - path('interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'), + path('interfaces/', include(get_model_urls('dcim', 'interface', detail=False))), path('interfaces//', include(get_model_urls('dcim', 'interface'))), path('devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'), - # Front ports - path('front-ports/', views.FrontPortListView.as_view(), name='frontport_list'), - path('front-ports/add/', views.FrontPortCreateView.as_view(), name='frontport_add'), - path('front-ports/import/', views.FrontPortBulkImportView.as_view(), name='frontport_import'), - path('front-ports/edit/', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'), - path('front-ports/rename/', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'), - path('front-ports/disconnect/', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'), - path('front-ports/delete/', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'), + path('front-ports/', include(get_model_urls('dcim', 'frontport', detail=False))), path('front-ports//', include(get_model_urls('dcim', 'frontport'))), - # path('devices/front-ports/add/', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'), - # Rear ports - path('rear-ports/', views.RearPortListView.as_view(), name='rearport_list'), - path('rear-ports/add/', views.RearPortCreateView.as_view(), name='rearport_add'), - path('rear-ports/import/', views.RearPortBulkImportView.as_view(), name='rearport_import'), - path('rear-ports/edit/', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'), - path('rear-ports/rename/', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'), - path('rear-ports/disconnect/', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'), - path('rear-ports/delete/', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'), + path('rear-ports/', include(get_model_urls('dcim', 'rearport', detail=False))), path('rear-ports//', include(get_model_urls('dcim', 'rearport'))), path('devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'), - # Module bays - path('module-bays/', views.ModuleBayListView.as_view(), name='modulebay_list'), - path('module-bays/add/', views.ModuleBayCreateView.as_view(), name='modulebay_add'), - path('module-bays/import/', views.ModuleBayBulkImportView.as_view(), name='modulebay_import'), - path('module-bays/edit/', views.ModuleBayBulkEditView.as_view(), name='modulebay_bulk_edit'), - path('module-bays/rename/', views.ModuleBayBulkRenameView.as_view(), name='modulebay_bulk_rename'), - path('module-bays/delete/', views.ModuleBayBulkDeleteView.as_view(), name='modulebay_bulk_delete'), + path('module-bays/', include(get_model_urls('dcim', 'modulebay', detail=False))), path('module-bays//', include(get_model_urls('dcim', 'modulebay'))), path('devices/module-bays/add/', views.DeviceBulkAddModuleBayView.as_view(), name='device_bulk_add_modulebay'), - # Device bays - path('device-bays/', views.DeviceBayListView.as_view(), name='devicebay_list'), - path('device-bays/add/', views.DeviceBayCreateView.as_view(), name='devicebay_add'), - path('device-bays/import/', views.DeviceBayBulkImportView.as_view(), name='devicebay_import'), - path('device-bays/edit/', views.DeviceBayBulkEditView.as_view(), name='devicebay_bulk_edit'), - path('device-bays/rename/', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'), - path('device-bays/delete/', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'), + path('device-bays/', include(get_model_urls('dcim', 'devicebay', detail=False))), path('device-bays//', include(get_model_urls('dcim', 'devicebay'))), path('devices/device-bays/add/', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'), - # Inventory items - path('inventory-items/', views.InventoryItemListView.as_view(), name='inventoryitem_list'), - path('inventory-items/add/', views.InventoryItemCreateView.as_view(), name='inventoryitem_add'), - path('inventory-items/import/', views.InventoryItemBulkImportView.as_view(), name='inventoryitem_import'), - path('inventory-items/edit/', views.InventoryItemBulkEditView.as_view(), name='inventoryitem_bulk_edit'), - path('inventory-items/rename/', views.InventoryItemBulkRenameView.as_view(), name='inventoryitem_bulk_rename'), - path('inventory-items/delete/', views.InventoryItemBulkDeleteView.as_view(), name='inventoryitem_bulk_delete'), + path('inventory-items/', include(get_model_urls('dcim', 'inventoryitem', detail=False))), path('inventory-items//', include(get_model_urls('dcim', 'inventoryitem'))), - path('devices/inventory-items/add/', views.DeviceBulkAddInventoryItemView.as_view(), name='device_bulk_add_inventoryitem'), + path( + 'devices/inventory-items/add/', + views.DeviceBulkAddInventoryItemView.as_view(), + name='device_bulk_add_inventoryitem' + ), - # Inventory item roles - path('inventory-item-roles/', views.InventoryItemRoleListView.as_view(), name='inventoryitemrole_list'), - path('inventory-item-roles/add/', views.InventoryItemRoleEditView.as_view(), name='inventoryitemrole_add'), - path('inventory-item-roles/import/', views.InventoryItemRoleBulkImportView.as_view(), name='inventoryitemrole_import'), - path('inventory-item-roles/edit/', views.InventoryItemRoleBulkEditView.as_view(), name='inventoryitemrole_bulk_edit'), - path('inventory-item-roles/delete/', views.InventoryItemRoleBulkDeleteView.as_view(), name='inventoryitemrole_bulk_delete'), + path('inventory-item-roles/', include(get_model_urls('dcim', 'inventoryitemrole', detail=False))), path('inventory-item-roles//', include(get_model_urls('dcim', 'inventoryitemrole'))), - # Cables - path('cables/', views.CableListView.as_view(), name='cable_list'), - path('cables/add/', views.CableEditView.as_view(), name='cable_add'), - path('cables/import/', views.CableBulkImportView.as_view(), name='cable_import'), - path('cables/edit/', views.CableBulkEditView.as_view(), name='cable_bulk_edit'), - path('cables/delete/', views.CableBulkDeleteView.as_view(), name='cable_bulk_delete'), + path('cables/', include(get_model_urls('dcim', 'cable', detail=False))), path('cables//', include(get_model_urls('dcim', 'cable'))), # Console/power/interface connections (read-only) @@ -334,30 +151,21 @@ urlpatterns = [ path('power-connections/', views.PowerConnectionsListView.as_view(), name='power_connections_list'), path('interface-connections/', views.InterfaceConnectionsListView.as_view(), name='interface_connections_list'), - # Virtual chassis - path('virtual-chassis/', views.VirtualChassisListView.as_view(), name='virtualchassis_list'), - path('virtual-chassis/add/', views.VirtualChassisCreateView.as_view(), name='virtualchassis_add'), - path('virtual-chassis/import/', views.VirtualChassisBulkImportView.as_view(), name='virtualchassis_import'), - path('virtual-chassis/edit/', views.VirtualChassisBulkEditView.as_view(), name='virtualchassis_bulk_edit'), - path('virtual-chassis/delete/', views.VirtualChassisBulkDeleteView.as_view(), name='virtualchassis_bulk_delete'), + path('virtual-chassis/', include(get_model_urls('dcim', 'virtualchassis', detail=False))), path('virtual-chassis//', include(get_model_urls('dcim', 'virtualchassis'))), - path('virtual-chassis-members//delete/', views.VirtualChassisRemoveMemberView.as_view(), name='virtualchassis_remove_member'), + path( + 'virtual-chassis-members//delete/', + views.VirtualChassisRemoveMemberView.as_view(), + name='virtualchassis_remove_member' + ), - # Power panels - path('power-panels/', views.PowerPanelListView.as_view(), name='powerpanel_list'), - path('power-panels/add/', views.PowerPanelEditView.as_view(), name='powerpanel_add'), - path('power-panels/import/', views.PowerPanelBulkImportView.as_view(), name='powerpanel_import'), - path('power-panels/edit/', views.PowerPanelBulkEditView.as_view(), name='powerpanel_bulk_edit'), - path('power-panels/delete/', views.PowerPanelBulkDeleteView.as_view(), name='powerpanel_bulk_delete'), + path('power-panels/', include(get_model_urls('dcim', 'powerpanel', detail=False))), path('power-panels//', include(get_model_urls('dcim', 'powerpanel'))), - # Power feeds - path('power-feeds/', views.PowerFeedListView.as_view(), name='powerfeed_list'), - path('power-feeds/add/', views.PowerFeedEditView.as_view(), name='powerfeed_add'), - path('power-feeds/import/', views.PowerFeedBulkImportView.as_view(), name='powerfeed_import'), - path('power-feeds/edit/', views.PowerFeedBulkEditView.as_view(), name='powerfeed_bulk_edit'), - path('power-feeds/disconnect/', views.PowerFeedBulkDisconnectView.as_view(), name='powerfeed_bulk_disconnect'), - path('power-feeds/delete/', views.PowerFeedBulkDeleteView.as_view(), name='powerfeed_bulk_delete'), + path('power-feeds/', include(get_model_urls('dcim', 'powerfeed', detail=False))), path('power-feeds//', include(get_model_urls('dcim', 'powerfeed'))), + path('mac-addresses/', include(get_model_urls('dcim', 'macaddress', detail=False))), + path('mac-addresses//', include(get_model_urls('dcim', 'macaddress'))), + ] diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 98665a7a0..8b0628de5 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -11,14 +11,14 @@ from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils.html import escape from django.utils.safestring import mark_safe -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from django.views.generic import View from jinja2.exceptions import TemplateError from circuits.models import Circuit, CircuitTermination from extras.views import ObjectConfigContextView from ipam.models import ASN, IPAddress, VLANGroup -from ipam.tables import InterfaceVLANTable +from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable from netbox.constants import DEFAULT_ACTION_PERMISSIONS from netbox.views import generic from tenancy.views import ObjectContactsView @@ -35,7 +35,7 @@ from virtualization.forms import VirtualMachineFilterForm from virtualization.models import VirtualMachine from virtualization.tables import VirtualMachineTable from . import filtersets, forms, tables -from .choices import DeviceFaceChoices +from .choices import DeviceFaceChoices, InterfaceModeChoices from .models import * CABLE_TERMINATION_TYPES = { @@ -215,6 +215,7 @@ class PathTraceView(generic.ObjectView): # Regions # +@register_model_view(Region, 'list', path='', detail=False) class RegionListView(generic.ObjectListView): queryset = Region.objects.add_related_count( Region.objects.all(), @@ -242,11 +243,18 @@ class RegionView(GetRelatedModelsMixin, generic.ObjectView): extra=( (Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'), (Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'), + ( + Circuit.objects.restrict(request.user, 'view').filter( + terminations___region=instance + ).distinct(), + 'region_id' + ), ), ), } +@register_model_view(Region, 'add', detail=False) @register_model_view(Region, 'edit') class RegionEditView(generic.ObjectEditView): queryset = Region.objects.all() @@ -258,11 +266,13 @@ class RegionDeleteView(generic.ObjectDeleteView): queryset = Region.objects.all() +@register_model_view(Region, 'bulk_import', detail=False) class RegionBulkImportView(generic.BulkImportView): queryset = Region.objects.all() model_form = forms.RegionImportForm +@register_model_view(Region, 'bulk_edit', path='edit', detail=False) class RegionBulkEditView(generic.BulkEditView): queryset = Region.objects.add_related_count( Region.objects.all(), @@ -276,6 +286,7 @@ class RegionBulkEditView(generic.BulkEditView): form = forms.RegionBulkEditForm +@register_model_view(Region, 'bulk_delete', path='delete', detail=False) class RegionBulkDeleteView(generic.BulkDeleteView): queryset = Region.objects.add_related_count( Region.objects.all(), @@ -297,6 +308,7 @@ class RegionContactsView(ObjectContactsView): # Site groups # +@register_model_view(SiteGroup, 'list', path='', detail=False) class SiteGroupListView(generic.ObjectListView): queryset = SiteGroup.objects.add_related_count( SiteGroup.objects.all(), @@ -324,11 +336,18 @@ class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView): extra=( (Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'), (Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'), + ( + Circuit.objects.restrict(request.user, 'view').filter( + terminations___site_group=instance + ).distinct(), + 'site_group_id' + ), ), ), } +@register_model_view(SiteGroup, 'add', detail=False) @register_model_view(SiteGroup, 'edit') class SiteGroupEditView(generic.ObjectEditView): queryset = SiteGroup.objects.all() @@ -340,11 +359,13 @@ class SiteGroupDeleteView(generic.ObjectDeleteView): queryset = SiteGroup.objects.all() +@register_model_view(SiteGroup, 'bulk_import', detail=False) class SiteGroupBulkImportView(generic.BulkImportView): queryset = SiteGroup.objects.all() model_form = forms.SiteGroupImportForm +@register_model_view(SiteGroup, 'bulk_edit', path='edit', detail=False) class SiteGroupBulkEditView(generic.BulkEditView): queryset = SiteGroup.objects.add_related_count( SiteGroup.objects.all(), @@ -358,6 +379,7 @@ class SiteGroupBulkEditView(generic.BulkEditView): form = forms.SiteGroupBulkEditForm +@register_model_view(SiteGroup, 'bulk_delete', path='delete', detail=False) class SiteGroupBulkDeleteView(generic.BulkDeleteView): queryset = SiteGroup.objects.add_related_count( SiteGroup.objects.all(), @@ -379,6 +401,7 @@ class SiteGroupContactsView(ObjectContactsView): # Sites # +@register_model_view(Site, 'list', path='', detail=False) class SiteListView(generic.ObjectListView): queryset = Site.objects.annotate( device_count=count_related(Device, 'site') @@ -404,13 +427,16 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView): scope_id=instance.pk ), 'site'), (ASN.objects.restrict(request.user, 'view').filter(sites=instance), 'site_id'), - (Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(), - 'site_id'), + ( + Circuit.objects.restrict(request.user, 'view').filter(terminations___site=instance).distinct(), + 'site_id' + ), ), ), } +@register_model_view(Site, 'add', detail=False) @register_model_view(Site, 'edit') class SiteEditView(generic.ObjectEditView): queryset = Site.objects.all() @@ -422,11 +448,13 @@ class SiteDeleteView(generic.ObjectDeleteView): queryset = Site.objects.all() +@register_model_view(Site, 'bulk_import', detail=False) class SiteBulkImportView(generic.BulkImportView): queryset = Site.objects.all() model_form = forms.SiteImportForm +@register_model_view(Site, 'bulk_edit', path='edit', detail=False) class SiteBulkEditView(generic.BulkEditView): queryset = Site.objects.all() filterset = filtersets.SiteFilterSet @@ -434,6 +462,7 @@ class SiteBulkEditView(generic.BulkEditView): form = forms.SiteBulkEditForm +@register_model_view(Site, 'bulk_delete', path='delete', detail=False) class SiteBulkDeleteView(generic.BulkDeleteView): queryset = Site.objects.all() filterset = filtersets.SiteFilterSet @@ -449,6 +478,7 @@ class SiteContactsView(ObjectContactsView): # Locations # +@register_model_view(Location, 'list', path='', detail=False) class LocationListView(generic.ObjectListView): queryset = Location.objects.add_related_count( Location.objects.add_related_count( @@ -475,10 +505,23 @@ class LocationView(GetRelatedModelsMixin, generic.ObjectView): def get_extra_context(self, request, instance): locations = instance.get_descendants(include_self=True) return { - 'related_models': self.get_related_models(request, locations, [CableTermination]), + 'related_models': self.get_related_models( + request, + locations, + [CableTermination], + ( + ( + Circuit.objects.restrict(request.user, 'view').filter( + terminations___location=instance + ).distinct(), + 'location_id' + ), + ), + ), } +@register_model_view(Location, 'add', detail=False) @register_model_view(Location, 'edit') class LocationEditView(generic.ObjectEditView): queryset = Location.objects.all() @@ -490,11 +533,13 @@ class LocationDeleteView(generic.ObjectDeleteView): queryset = Location.objects.all() +@register_model_view(Location, 'bulk_import', detail=False) class LocationBulkImportView(generic.BulkImportView): queryset = Location.objects.all() model_form = forms.LocationImportForm +@register_model_view(Location, 'bulk_edit', path='edit', detail=False) class LocationBulkEditView(generic.BulkEditView): queryset = Location.objects.add_related_count( Location.objects.all(), @@ -508,6 +553,7 @@ class LocationBulkEditView(generic.BulkEditView): form = forms.LocationBulkEditForm +@register_model_view(Location, 'bulk_delete', path='delete', detail=False) class LocationBulkDeleteView(generic.BulkDeleteView): queryset = Location.objects.add_related_count( Location.objects.all(), @@ -529,6 +575,7 @@ class LocationContactsView(ObjectContactsView): # Rack roles # +@register_model_view(RackRole, 'list', path='', detail=False) class RackRoleListView(generic.ObjectListView): queryset = RackRole.objects.annotate( rack_count=count_related(Rack, 'role') @@ -548,6 +595,7 @@ class RackRoleView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(RackRole, 'add', detail=False) @register_model_view(RackRole, 'edit') class RackRoleEditView(generic.ObjectEditView): queryset = RackRole.objects.all() @@ -559,11 +607,13 @@ class RackRoleDeleteView(generic.ObjectDeleteView): queryset = RackRole.objects.all() +@register_model_view(RackRole, 'bulk_import', detail=False) class RackRoleBulkImportView(generic.BulkImportView): queryset = RackRole.objects.all() model_form = forms.RackRoleImportForm +@register_model_view(RackRole, 'bulk_edit', path='edit', detail=False) class RackRoleBulkEditView(generic.BulkEditView): queryset = RackRole.objects.annotate( rack_count=count_related(Rack, 'role') @@ -573,6 +623,7 @@ class RackRoleBulkEditView(generic.BulkEditView): form = forms.RackRoleBulkEditForm +@register_model_view(RackRole, 'bulk_delete', path='delete', detail=False) class RackRoleBulkDeleteView(generic.BulkDeleteView): queryset = RackRole.objects.annotate( rack_count=count_related(Rack, 'role') @@ -585,6 +636,7 @@ class RackRoleBulkDeleteView(generic.BulkDeleteView): # RackTypes # +@register_model_view(RackType, 'list', path='', detail=False) class RackTypeListView(generic.ObjectListView): queryset = RackType.objects.annotate( instance_count=count_related(Rack, 'rack_type') @@ -604,6 +656,7 @@ class RackTypeView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(RackType, 'add', detail=False) @register_model_view(RackType, 'edit') class RackTypeEditView(generic.ObjectEditView): queryset = RackType.objects.all() @@ -615,11 +668,13 @@ class RackTypeDeleteView(generic.ObjectDeleteView): queryset = RackType.objects.all() +@register_model_view(RackType, 'bulk_import', detail=False) class RackTypeBulkImportView(generic.BulkImportView): queryset = RackType.objects.all() model_form = forms.RackTypeImportForm +@register_model_view(RackType, 'bulk_edit', path='edit', detail=False) class RackTypeBulkEditView(generic.BulkEditView): queryset = RackType.objects.all() filterset = filtersets.RackTypeFilterSet @@ -627,6 +682,7 @@ class RackTypeBulkEditView(generic.BulkEditView): form = forms.RackTypeBulkEditForm +@register_model_view(RackType, 'bulk_delete', path='delete', detail=False) class RackTypeBulkDeleteView(generic.BulkDeleteView): queryset = RackType.objects.all() filterset = filtersets.RackTypeFilterSet @@ -637,6 +693,7 @@ class RackTypeBulkDeleteView(generic.BulkDeleteView): # Racks # +@register_model_view(Rack, 'list', path='', detail=False) class RackListView(generic.ObjectListView): queryset = Rack.objects.annotate( device_count=count_related(Device, 'rack') @@ -668,8 +725,7 @@ class RackElevationListView(generic.ObjectListView): sort = request.GET.get('sort', 'name') if sort not in ORDERING_CHOICES: sort = 'name' - sort_field = sort.replace("name", "_name") # Use natural ordering - racks = racks.order_by(sort_field) + racks = racks.order_by(sort) # Pagination per_page = get_paginate_count(request) @@ -711,8 +767,8 @@ class RackView(GetRelatedModelsMixin, generic.ObjectView): peer_racks = peer_racks.filter(location=instance.location) else: peer_racks = peer_racks.filter(location__isnull=True) - next_rack = peer_racks.filter(_name__gt=instance._name).first() - prev_rack = peer_racks.filter(_name__lt=instance._name).reverse().first() + next_rack = peer_racks.filter(name__gt=instance.name).first() + prev_rack = peer_racks.filter(name__lt=instance.name).reverse().first() # Determine any additional parameters to pass when embedding the rack elevations svg_extra = '&'.join([ @@ -768,6 +824,7 @@ class RackNonRackedView(generic.ObjectChildrenView): ) +@register_model_view(Rack, 'add', detail=False) @register_model_view(Rack, 'edit') class RackEditView(generic.ObjectEditView): queryset = Rack.objects.all() @@ -779,11 +836,13 @@ class RackDeleteView(generic.ObjectDeleteView): queryset = Rack.objects.all() +@register_model_view(Rack, 'bulk_import', detail=False) class RackBulkImportView(generic.BulkImportView): queryset = Rack.objects.all() model_form = forms.RackImportForm +@register_model_view(Rack, 'bulk_edit', path='edit', detail=False) class RackBulkEditView(generic.BulkEditView): queryset = Rack.objects.all() filterset = filtersets.RackFilterSet @@ -791,6 +850,7 @@ class RackBulkEditView(generic.BulkEditView): form = forms.RackBulkEditForm +@register_model_view(Rack, 'bulk_delete', path='delete', detail=False) class RackBulkDeleteView(generic.BulkDeleteView): queryset = Rack.objects.all() filterset = filtersets.RackFilterSet @@ -806,6 +866,7 @@ class RackContactsView(ObjectContactsView): # Rack reservations # +@register_model_view(RackReservation, 'list', path='', detail=False) class RackReservationListView(generic.ObjectListView): queryset = RackReservation.objects.all() filterset = filtersets.RackReservationFilterSet @@ -818,6 +879,7 @@ class RackReservationView(generic.ObjectView): queryset = RackReservation.objects.all() +@register_model_view(RackReservation, 'add', detail=False) @register_model_view(RackReservation, 'edit') class RackReservationEditView(generic.ObjectEditView): queryset = RackReservation.objects.all() @@ -836,6 +898,7 @@ class RackReservationDeleteView(generic.ObjectDeleteView): queryset = RackReservation.objects.all() +@register_model_view(RackReservation, 'bulk_import', detail=False) class RackReservationImportView(generic.BulkImportView): queryset = RackReservation.objects.all() model_form = forms.RackReservationImportForm @@ -851,6 +914,7 @@ class RackReservationImportView(generic.BulkImportView): return instance +@register_model_view(RackReservation, 'bulk_edit', path='edit', detail=False) class RackReservationBulkEditView(generic.BulkEditView): queryset = RackReservation.objects.all() filterset = filtersets.RackReservationFilterSet @@ -858,6 +922,7 @@ class RackReservationBulkEditView(generic.BulkEditView): form = forms.RackReservationBulkEditForm +@register_model_view(RackReservation, 'bulk_delete', path='delete', detail=False) class RackReservationBulkDeleteView(generic.BulkDeleteView): queryset = RackReservation.objects.all() filterset = filtersets.RackReservationFilterSet @@ -868,6 +933,7 @@ class RackReservationBulkDeleteView(generic.BulkDeleteView): # Manufacturers # +@register_model_view(Manufacturer, 'list', path='', detail=False) class ManufacturerListView(generic.ObjectListView): queryset = Manufacturer.objects.annotate( devicetype_count=count_related(DeviceType, 'manufacturer'), @@ -890,6 +956,7 @@ class ManufacturerView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(Manufacturer, 'add', detail=False) @register_model_view(Manufacturer, 'edit') class ManufacturerEditView(generic.ObjectEditView): queryset = Manufacturer.objects.all() @@ -901,11 +968,13 @@ class ManufacturerDeleteView(generic.ObjectDeleteView): queryset = Manufacturer.objects.all() +@register_model_view(Manufacturer, 'bulk_import', detail=False) class ManufacturerBulkImportView(generic.BulkImportView): queryset = Manufacturer.objects.all() model_form = forms.ManufacturerImportForm +@register_model_view(Manufacturer, 'bulk_edit', path='edit', detail=False) class ManufacturerBulkEditView(generic.BulkEditView): queryset = Manufacturer.objects.annotate( devicetype_count=count_related(DeviceType, 'manufacturer'), @@ -918,6 +987,7 @@ class ManufacturerBulkEditView(generic.BulkEditView): form = forms.ManufacturerBulkEditForm +@register_model_view(Manufacturer, 'bulk_delete', path='delete', detail=False) class ManufacturerBulkDeleteView(generic.BulkDeleteView): queryset = Manufacturer.objects.annotate( devicetype_count=count_related(DeviceType, 'manufacturer'), @@ -938,6 +1008,7 @@ class ManufacturerContactsView(ObjectContactsView): # Device types # +@register_model_view(DeviceType, 'list', path='', detail=False) class DeviceTypeListView(generic.ObjectListView): queryset = DeviceType.objects.annotate( instance_count=count_related(Device, 'device_type') @@ -961,6 +1032,7 @@ class DeviceTypeView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(DeviceType, 'add', detail=False) @register_model_view(DeviceType, 'edit') class DeviceTypeEditView(generic.ObjectEditView): queryset = DeviceType.objects.all() @@ -1122,6 +1194,7 @@ class DeviceTypeInventoryItemsView(DeviceTypeComponentsView): ) +@register_model_view(DeviceType, 'bulk_import', detail=False) class DeviceTypeImportView(generic.BulkImportView): additional_permissions = [ 'dcim.add_devicetype', @@ -1156,6 +1229,7 @@ class DeviceTypeImportView(generic.BulkImportView): return data +@register_model_view(DeviceType, 'bulk_edit', path='edit', detail=False) class DeviceTypeBulkEditView(generic.BulkEditView): queryset = DeviceType.objects.annotate( instance_count=count_related(Device, 'device_type') @@ -1165,6 +1239,7 @@ class DeviceTypeBulkEditView(generic.BulkEditView): form = forms.DeviceTypeBulkEditForm +@register_model_view(DeviceType, 'bulk_delete', path='delete', detail=False) class DeviceTypeBulkDeleteView(generic.BulkDeleteView): queryset = DeviceType.objects.annotate( instance_count=count_related(Device, 'device_type') @@ -1177,6 +1252,7 @@ class DeviceTypeBulkDeleteView(generic.BulkDeleteView): # Module types # +@register_model_view(ModuleType, 'list', path='', detail=False) class ModuleTypeListView(generic.ObjectListView): queryset = ModuleType.objects.annotate( instance_count=count_related(Module, 'module_type') @@ -1200,6 +1276,7 @@ class ModuleTypeView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(ModuleType, 'add', detail=False) @register_model_view(ModuleType, 'edit') class ModuleTypeEditView(generic.ObjectEditView): queryset = ModuleType.objects.all() @@ -1331,6 +1408,7 @@ class ModuleTypeModuleBaysView(ModuleTypeComponentsView): ) +@register_model_view(ModuleType, 'bulk_import', detail=False) class ModuleTypeImportView(generic.BulkImportView): additional_permissions = [ 'dcim.add_moduletype', @@ -1359,6 +1437,7 @@ class ModuleTypeImportView(generic.BulkImportView): return data +@register_model_view(ModuleType, 'bulk_edit', path='edit', detail=False) class ModuleTypeBulkEditView(generic.BulkEditView): queryset = ModuleType.objects.annotate( instance_count=count_related(Module, 'module_type') @@ -1368,6 +1447,7 @@ class ModuleTypeBulkEditView(generic.BulkEditView): form = forms.ModuleTypeBulkEditForm +@register_model_view(ModuleType, 'bulk_delete', path='delete', detail=False) class ModuleTypeBulkDeleteView(generic.BulkDeleteView): queryset = ModuleType.objects.annotate( instance_count=count_related(Module, 'module_type') @@ -1380,6 +1460,7 @@ class ModuleTypeBulkDeleteView(generic.BulkDeleteView): # Console port templates # +@register_model_view(ConsolePortTemplate, 'add', detail=False) class ConsolePortTemplateCreateView(generic.ComponentCreateView): queryset = ConsolePortTemplate.objects.all() form = forms.ConsolePortTemplateCreateForm @@ -1397,16 +1478,19 @@ class ConsolePortTemplateDeleteView(generic.ObjectDeleteView): queryset = ConsolePortTemplate.objects.all() +@register_model_view(ConsolePortTemplate, 'bulk_edit', path='edit', detail=False) class ConsolePortTemplateBulkEditView(generic.BulkEditView): queryset = ConsolePortTemplate.objects.all() table = tables.ConsolePortTemplateTable form = forms.ConsolePortTemplateBulkEditForm +@register_model_view(ConsolePortTemplate, 'bulk_rename', path='rename', detail=False) class ConsolePortTemplateBulkRenameView(generic.BulkRenameView): queryset = ConsolePortTemplate.objects.all() +@register_model_view(ConsolePortTemplate, 'bulk_delete', path='delete', detail=False) class ConsolePortTemplateBulkDeleteView(generic.BulkDeleteView): queryset = ConsolePortTemplate.objects.all() table = tables.ConsolePortTemplateTable @@ -1416,6 +1500,7 @@ class ConsolePortTemplateBulkDeleteView(generic.BulkDeleteView): # Console server port templates # +@register_model_view(ConsoleServerPortTemplate, 'add', detail=False) class ConsoleServerPortTemplateCreateView(generic.ComponentCreateView): queryset = ConsoleServerPortTemplate.objects.all() form = forms.ConsoleServerPortTemplateCreateForm @@ -1433,16 +1518,19 @@ class ConsoleServerPortTemplateDeleteView(generic.ObjectDeleteView): queryset = ConsoleServerPortTemplate.objects.all() +@register_model_view(ConsoleServerPortTemplate, 'bulk_edit', path='edit', detail=False) class ConsoleServerPortTemplateBulkEditView(generic.BulkEditView): queryset = ConsoleServerPortTemplate.objects.all() table = tables.ConsoleServerPortTemplateTable form = forms.ConsoleServerPortTemplateBulkEditForm +@register_model_view(ConsoleServerPortTemplate, 'bulk_rename', detail=False) class ConsoleServerPortTemplateBulkRenameView(generic.BulkRenameView): queryset = ConsoleServerPortTemplate.objects.all() +@register_model_view(ConsoleServerPortTemplate, 'bulk_delete', path='delete', detail=False) class ConsoleServerPortTemplateBulkDeleteView(generic.BulkDeleteView): queryset = ConsoleServerPortTemplate.objects.all() table = tables.ConsoleServerPortTemplateTable @@ -1452,6 +1540,7 @@ class ConsoleServerPortTemplateBulkDeleteView(generic.BulkDeleteView): # Power port templates # +@register_model_view(PowerPortTemplate, 'add', detail=False) class PowerPortTemplateCreateView(generic.ComponentCreateView): queryset = PowerPortTemplate.objects.all() form = forms.PowerPortTemplateCreateForm @@ -1469,16 +1558,19 @@ class PowerPortTemplateDeleteView(generic.ObjectDeleteView): queryset = PowerPortTemplate.objects.all() +@register_model_view(PowerPortTemplate, 'bulk_edit', path='edit', detail=False) class PowerPortTemplateBulkEditView(generic.BulkEditView): queryset = PowerPortTemplate.objects.all() table = tables.PowerPortTemplateTable form = forms.PowerPortTemplateBulkEditForm +@register_model_view(PowerPortTemplate, 'bulk_rename', path='rename', detail=False) class PowerPortTemplateBulkRenameView(generic.BulkRenameView): queryset = PowerPortTemplate.objects.all() +@register_model_view(PowerPortTemplate, 'bulk_delete', path='delete', detail=False) class PowerPortTemplateBulkDeleteView(generic.BulkDeleteView): queryset = PowerPortTemplate.objects.all() table = tables.PowerPortTemplateTable @@ -1488,6 +1580,7 @@ class PowerPortTemplateBulkDeleteView(generic.BulkDeleteView): # Power outlet templates # +@register_model_view(PowerOutletTemplate, 'add', detail=False) class PowerOutletTemplateCreateView(generic.ComponentCreateView): queryset = PowerOutletTemplate.objects.all() form = forms.PowerOutletTemplateCreateForm @@ -1505,16 +1598,19 @@ class PowerOutletTemplateDeleteView(generic.ObjectDeleteView): queryset = PowerOutletTemplate.objects.all() +@register_model_view(PowerOutletTemplate, 'bulk_edit', path='edit', detail=False) class PowerOutletTemplateBulkEditView(generic.BulkEditView): queryset = PowerOutletTemplate.objects.all() table = tables.PowerOutletTemplateTable form = forms.PowerOutletTemplateBulkEditForm +@register_model_view(PowerOutletTemplate, 'bulk_rename', path='rename', detail=False) class PowerOutletTemplateBulkRenameView(generic.BulkRenameView): queryset = PowerOutletTemplate.objects.all() +@register_model_view(PowerOutletTemplate, 'bulk_delete', path='delete', detail=False) class PowerOutletTemplateBulkDeleteView(generic.BulkDeleteView): queryset = PowerOutletTemplate.objects.all() table = tables.PowerOutletTemplateTable @@ -1524,6 +1620,7 @@ class PowerOutletTemplateBulkDeleteView(generic.BulkDeleteView): # Interface templates # +@register_model_view(InterfaceTemplate, 'add', detail=False) class InterfaceTemplateCreateView(generic.ComponentCreateView): queryset = InterfaceTemplate.objects.all() form = forms.InterfaceTemplateCreateForm @@ -1541,16 +1638,19 @@ class InterfaceTemplateDeleteView(generic.ObjectDeleteView): queryset = InterfaceTemplate.objects.all() +@register_model_view(InterfaceTemplate, 'bulk_edit', path='edit', detail=False) class InterfaceTemplateBulkEditView(generic.BulkEditView): queryset = InterfaceTemplate.objects.all() table = tables.InterfaceTemplateTable form = forms.InterfaceTemplateBulkEditForm +@register_model_view(InterfaceTemplate, 'bulk_rename', path='rename', detail=False) class InterfaceTemplateBulkRenameView(generic.BulkRenameView): queryset = InterfaceTemplate.objects.all() +@register_model_view(InterfaceTemplate, 'bulk_delete', path='delete', detail=False) class InterfaceTemplateBulkDeleteView(generic.BulkDeleteView): queryset = InterfaceTemplate.objects.all() table = tables.InterfaceTemplateTable @@ -1560,6 +1660,7 @@ class InterfaceTemplateBulkDeleteView(generic.BulkDeleteView): # Front port templates # +@register_model_view(FrontPortTemplate, 'add', detail=False) class FrontPortTemplateCreateView(generic.ComponentCreateView): queryset = FrontPortTemplate.objects.all() form = forms.FrontPortTemplateCreateForm @@ -1577,16 +1678,19 @@ class FrontPortTemplateDeleteView(generic.ObjectDeleteView): queryset = FrontPortTemplate.objects.all() +@register_model_view(FrontPortTemplate, 'bulk_edit', path='edit', detail=False) class FrontPortTemplateBulkEditView(generic.BulkEditView): queryset = FrontPortTemplate.objects.all() table = tables.FrontPortTemplateTable form = forms.FrontPortTemplateBulkEditForm +@register_model_view(FrontPortTemplate, 'bulk_rename', path='rename', detail=False) class FrontPortTemplateBulkRenameView(generic.BulkRenameView): queryset = FrontPortTemplate.objects.all() +@register_model_view(FrontPortTemplate, 'bulk_delete', path='delete', detail=False) class FrontPortTemplateBulkDeleteView(generic.BulkDeleteView): queryset = FrontPortTemplate.objects.all() table = tables.FrontPortTemplateTable @@ -1596,6 +1700,7 @@ class FrontPortTemplateBulkDeleteView(generic.BulkDeleteView): # Rear port templates # +@register_model_view(RearPortTemplate, 'add', detail=False) class RearPortTemplateCreateView(generic.ComponentCreateView): queryset = RearPortTemplate.objects.all() form = forms.RearPortTemplateCreateForm @@ -1613,16 +1718,19 @@ class RearPortTemplateDeleteView(generic.ObjectDeleteView): queryset = RearPortTemplate.objects.all() +@register_model_view(RearPortTemplate, 'bulk_edit', path='edit', detail=False) class RearPortTemplateBulkEditView(generic.BulkEditView): queryset = RearPortTemplate.objects.all() table = tables.RearPortTemplateTable form = forms.RearPortTemplateBulkEditForm +@register_model_view(RearPortTemplate, 'bulk_rename', path='rename', detail=False) class RearPortTemplateBulkRenameView(generic.BulkRenameView): queryset = RearPortTemplate.objects.all() +@register_model_view(RearPortTemplate, 'bulk_delete', path='delete', detail=False) class RearPortTemplateBulkDeleteView(generic.BulkDeleteView): queryset = RearPortTemplate.objects.all() table = tables.RearPortTemplateTable @@ -1632,6 +1740,7 @@ class RearPortTemplateBulkDeleteView(generic.BulkDeleteView): # Module bay templates # +@register_model_view(ModuleBayTemplate, 'add', detail=False) class ModuleBayTemplateCreateView(generic.ComponentCreateView): queryset = ModuleBayTemplate.objects.all() form = forms.ModuleBayTemplateCreateForm @@ -1649,16 +1758,19 @@ class ModuleBayTemplateDeleteView(generic.ObjectDeleteView): queryset = ModuleBayTemplate.objects.all() +@register_model_view(ModuleBayTemplate, 'bulk_edit', path='edit', detail=False) class ModuleBayTemplateBulkEditView(generic.BulkEditView): queryset = ModuleBayTemplate.objects.all() table = tables.ModuleBayTemplateTable form = forms.ModuleBayTemplateBulkEditForm +@register_model_view(ModuleBayTemplate, 'bulk_rename', path='rename', detail=False) class ModuleBayTemplateBulkRenameView(generic.BulkRenameView): queryset = ModuleBayTemplate.objects.all() +@register_model_view(ModuleBayTemplate, 'bulk_delete', path='delete', detail=False) class ModuleBayTemplateBulkDeleteView(generic.BulkDeleteView): queryset = ModuleBayTemplate.objects.all() table = tables.ModuleBayTemplateTable @@ -1668,6 +1780,7 @@ class ModuleBayTemplateBulkDeleteView(generic.BulkDeleteView): # Device bay templates # +@register_model_view(DeviceBayTemplate, 'add', detail=False) class DeviceBayTemplateCreateView(generic.ComponentCreateView): queryset = DeviceBayTemplate.objects.all() form = forms.DeviceBayTemplateCreateForm @@ -1685,16 +1798,19 @@ class DeviceBayTemplateDeleteView(generic.ObjectDeleteView): queryset = DeviceBayTemplate.objects.all() +@register_model_view(DeviceBayTemplate, 'bulk_edit', path='edit', detail=False) class DeviceBayTemplateBulkEditView(generic.BulkEditView): queryset = DeviceBayTemplate.objects.all() table = tables.DeviceBayTemplateTable form = forms.DeviceBayTemplateBulkEditForm +@register_model_view(DeviceBayTemplate, 'bulk_rename', path='rename', detail=False) class DeviceBayTemplateBulkRenameView(generic.BulkRenameView): queryset = DeviceBayTemplate.objects.all() +@register_model_view(DeviceBayTemplate, 'bulk_delete', path='delete', detail=False) class DeviceBayTemplateBulkDeleteView(generic.BulkDeleteView): queryset = DeviceBayTemplate.objects.all() table = tables.DeviceBayTemplateTable @@ -1704,6 +1820,7 @@ class DeviceBayTemplateBulkDeleteView(generic.BulkDeleteView): # Inventory item templates # +@register_model_view(InventoryItemTemplate, 'add', detail=False) class InventoryItemTemplateCreateView(generic.ComponentCreateView): queryset = InventoryItemTemplate.objects.all() form = forms.InventoryItemTemplateCreateForm @@ -1732,16 +1849,19 @@ class InventoryItemTemplateDeleteView(generic.ObjectDeleteView): queryset = InventoryItemTemplate.objects.all() +@register_model_view(InventoryItemTemplate, 'bulk_edit', path='edit', detail=False) class InventoryItemTemplateBulkEditView(generic.BulkEditView): queryset = InventoryItemTemplate.objects.all() table = tables.InventoryItemTemplateTable form = forms.InventoryItemTemplateBulkEditForm +@register_model_view(InventoryItemTemplate, 'bulk_rename', path='rename', detail=False) class InventoryItemTemplateBulkRenameView(generic.BulkRenameView): queryset = InventoryItemTemplate.objects.all() +@register_model_view(InventoryItemTemplate, 'bulk_delete', path='delete', detail=False) class InventoryItemTemplateBulkDeleteView(generic.BulkDeleteView): queryset = InventoryItemTemplate.objects.all() table = tables.InventoryItemTemplateTable @@ -1751,6 +1871,7 @@ class InventoryItemTemplateBulkDeleteView(generic.BulkDeleteView): # Device roles # +@register_model_view(DeviceRole, 'list', path='', detail=False) class DeviceRoleListView(generic.ObjectListView): queryset = DeviceRole.objects.annotate( device_count=count_related(Device, 'role'), @@ -1771,6 +1892,7 @@ class DeviceRoleView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(DeviceRole, 'add', detail=False) @register_model_view(DeviceRole, 'edit') class DeviceRoleEditView(generic.ObjectEditView): queryset = DeviceRole.objects.all() @@ -1782,11 +1904,13 @@ class DeviceRoleDeleteView(generic.ObjectDeleteView): queryset = DeviceRole.objects.all() +@register_model_view(DeviceRole, 'bulk_import', detail=False) class DeviceRoleBulkImportView(generic.BulkImportView): queryset = DeviceRole.objects.all() model_form = forms.DeviceRoleImportForm +@register_model_view(DeviceRole, 'bulk_edit', path='edit', detail=False) class DeviceRoleBulkEditView(generic.BulkEditView): queryset = DeviceRole.objects.annotate( device_count=count_related(Device, 'role'), @@ -1797,6 +1921,7 @@ class DeviceRoleBulkEditView(generic.BulkEditView): form = forms.DeviceRoleBulkEditForm +@register_model_view(DeviceRole, 'bulk_delete', path='delete', detail=False) class DeviceRoleBulkDeleteView(generic.BulkDeleteView): queryset = DeviceRole.objects.annotate( device_count=count_related(Device, 'role'), @@ -1810,6 +1935,7 @@ class DeviceRoleBulkDeleteView(generic.BulkDeleteView): # Platforms # +@register_model_view(Platform, 'list', path='', detail=False) class PlatformListView(generic.ObjectListView): queryset = Platform.objects.annotate( device_count=count_related(Device, 'platform'), @@ -1830,6 +1956,7 @@ class PlatformView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(Platform, 'add', detail=False) @register_model_view(Platform, 'edit') class PlatformEditView(generic.ObjectEditView): queryset = Platform.objects.all() @@ -1841,11 +1968,13 @@ class PlatformDeleteView(generic.ObjectDeleteView): queryset = Platform.objects.all() +@register_model_view(Platform, 'bulk_import', detail=False) class PlatformBulkImportView(generic.BulkImportView): queryset = Platform.objects.all() model_form = forms.PlatformImportForm +@register_model_view(Platform, 'bulk_edit', path='edit', detail=False) class PlatformBulkEditView(generic.BulkEditView): queryset = Platform.objects.all() filterset = filtersets.PlatformFilterSet @@ -1853,6 +1982,7 @@ class PlatformBulkEditView(generic.BulkEditView): form = forms.PlatformBulkEditForm +@register_model_view(Platform, 'bulk_delete', path='delete', detail=False) class PlatformBulkDeleteView(generic.BulkDeleteView): queryset = Platform.objects.all() filterset = filtersets.PlatformFilterSet @@ -1863,6 +1993,7 @@ class PlatformBulkDeleteView(generic.BulkDeleteView): # Devices # +@register_model_view(Device, 'list', path='', detail=False) class DeviceListView(generic.ObjectListView): queryset = Device.objects.all() filterset = filtersets.DeviceFilterSet @@ -1890,6 +2021,7 @@ class DeviceView(generic.ObjectView): } +@register_model_view(Device, 'add', detail=False) @register_model_view(Device, 'edit') class DeviceEditView(generic.ObjectEditView): queryset = Device.objects.all() @@ -2157,6 +2289,7 @@ class DeviceVirtualMachinesView(generic.ObjectChildrenView): return self.child_model.objects.restrict(request.user, 'view').filter(cluster=parent.cluster, device=parent) +@register_model_view(Device, 'bulk_import', detail=False) class DeviceBulkImportView(generic.BulkImportView): queryset = Device.objects.all() model_form = forms.DeviceImportForm @@ -2173,6 +2306,7 @@ class DeviceBulkImportView(generic.BulkImportView): return obj +@register_model_view(Device, 'bulk_edit', path='edit', detail=False) class DeviceBulkEditView(generic.BulkEditView): queryset = Device.objects.prefetch_related('device_type__manufacturer') filterset = filtersets.DeviceFilterSet @@ -2180,12 +2314,14 @@ class DeviceBulkEditView(generic.BulkEditView): form = forms.DeviceBulkEditForm +@register_model_view(Device, 'bulk_delete', path='delete', detail=False) class DeviceBulkDeleteView(generic.BulkDeleteView): queryset = Device.objects.prefetch_related('device_type__manufacturer') filterset = filtersets.DeviceFilterSet table = tables.DeviceTable +@register_model_view(Device, 'bulk_rename', path='rename', detail=False) class DeviceBulkRenameView(generic.BulkRenameView): queryset = Device.objects.all() filterset = filtersets.DeviceFilterSet @@ -2201,6 +2337,7 @@ class DeviceContactsView(ObjectContactsView): # Modules # +@register_model_view(Module, 'list', path='', detail=False) class ModuleListView(generic.ObjectListView): queryset = Module.objects.prefetch_related('module_type__manufacturer') filterset = filtersets.ModuleFilterSet @@ -2218,6 +2355,7 @@ class ModuleView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(Module, 'add', detail=False) @register_model_view(Module, 'edit') class ModuleEditView(generic.ObjectEditView): queryset = Module.objects.all() @@ -2229,11 +2367,13 @@ class ModuleDeleteView(generic.ObjectDeleteView): queryset = Module.objects.all() +@register_model_view(Module, 'bulk_import', detail=False) class ModuleBulkImportView(generic.BulkImportView): queryset = Module.objects.all() model_form = forms.ModuleImportForm +@register_model_view(Module, 'bulk_edit', path='edit', detail=False) class ModuleBulkEditView(generic.BulkEditView): queryset = Module.objects.prefetch_related('module_type__manufacturer') filterset = filtersets.ModuleFilterSet @@ -2241,6 +2381,7 @@ class ModuleBulkEditView(generic.BulkEditView): form = forms.ModuleBulkEditForm +@register_model_view(Module, 'bulk_delete', path='delete', detail=False) class ModuleBulkDeleteView(generic.BulkDeleteView): queryset = Module.objects.prefetch_related('module_type__manufacturer') filterset = filtersets.ModuleFilterSet @@ -2251,6 +2392,7 @@ class ModuleBulkDeleteView(generic.BulkDeleteView): # Console ports # +@register_model_view(ConsolePort, 'list', path='', detail=False) class ConsolePortListView(generic.ObjectListView): queryset = ConsolePort.objects.all() filterset = filtersets.ConsolePortFilterSet @@ -2268,6 +2410,7 @@ class ConsolePortView(generic.ObjectView): queryset = ConsolePort.objects.all() +@register_model_view(ConsolePort, 'add', detail=False) class ConsolePortCreateView(generic.ComponentCreateView): queryset = ConsolePort.objects.all() form = forms.ConsolePortCreateForm @@ -2285,11 +2428,13 @@ class ConsolePortDeleteView(generic.ObjectDeleteView): queryset = ConsolePort.objects.all() +@register_model_view(ConsolePort, 'bulk_import', detail=False) class ConsolePortBulkImportView(generic.BulkImportView): queryset = ConsolePort.objects.all() model_form = forms.ConsolePortImportForm +@register_model_view(ConsolePort, 'bulk_edit', path='edit', detail=False) class ConsolePortBulkEditView(generic.BulkEditView): queryset = ConsolePort.objects.all() filterset = filtersets.ConsolePortFilterSet @@ -2297,14 +2442,17 @@ class ConsolePortBulkEditView(generic.BulkEditView): form = forms.ConsolePortBulkEditForm +@register_model_view(ConsolePort, 'bulk_rename', path='rename', detail=False) class ConsolePortBulkRenameView(generic.BulkRenameView): queryset = ConsolePort.objects.all() +@register_model_view(ConsolePort, 'bulk_disconnect', path='disconnect', detail=False) class ConsolePortBulkDisconnectView(BulkDisconnectView): queryset = ConsolePort.objects.all() +@register_model_view(ConsolePort, 'bulk_delete', path='delete', detail=False) class ConsolePortBulkDeleteView(generic.BulkDeleteView): queryset = ConsolePort.objects.all() filterset = filtersets.ConsolePortFilterSet @@ -2319,6 +2467,7 @@ register_model_view(ConsolePort, 'trace', kwargs={'model': ConsolePort})(PathTra # Console server ports # +@register_model_view(ConsoleServerPort, 'list', path='', detail=False) class ConsoleServerPortListView(generic.ObjectListView): queryset = ConsoleServerPort.objects.all() filterset = filtersets.ConsoleServerPortFilterSet @@ -2336,6 +2485,7 @@ class ConsoleServerPortView(generic.ObjectView): queryset = ConsoleServerPort.objects.all() +@register_model_view(ConsoleServerPort, 'add', detail=False) class ConsoleServerPortCreateView(generic.ComponentCreateView): queryset = ConsoleServerPort.objects.all() form = forms.ConsoleServerPortCreateForm @@ -2353,11 +2503,13 @@ class ConsoleServerPortDeleteView(generic.ObjectDeleteView): queryset = ConsoleServerPort.objects.all() +@register_model_view(ConsoleServerPort, 'bulk_import', detail=False) class ConsoleServerPortBulkImportView(generic.BulkImportView): queryset = ConsoleServerPort.objects.all() model_form = forms.ConsoleServerPortImportForm +@register_model_view(ConsoleServerPort, 'bulk_edit', path='edit', detail=False) class ConsoleServerPortBulkEditView(generic.BulkEditView): queryset = ConsoleServerPort.objects.all() filterset = filtersets.ConsoleServerPortFilterSet @@ -2365,14 +2517,17 @@ class ConsoleServerPortBulkEditView(generic.BulkEditView): form = forms.ConsoleServerPortBulkEditForm +@register_model_view(ConsoleServerPort, 'bulk_rename', path='rename', detail=False) class ConsoleServerPortBulkRenameView(generic.BulkRenameView): queryset = ConsoleServerPort.objects.all() +@register_model_view(ConsoleServerPort, 'bulk_disconnect', path='disconnect', detail=False) class ConsoleServerPortBulkDisconnectView(BulkDisconnectView): queryset = ConsoleServerPort.objects.all() +@register_model_view(ConsoleServerPort, 'bulk_delete', path='delete', detail=False) class ConsoleServerPortBulkDeleteView(generic.BulkDeleteView): queryset = ConsoleServerPort.objects.all() filterset = filtersets.ConsoleServerPortFilterSet @@ -2387,6 +2542,7 @@ register_model_view(ConsoleServerPort, 'trace', kwargs={'model': ConsoleServerPo # Power ports # +@register_model_view(PowerPort, 'list', path='', detail=False) class PowerPortListView(generic.ObjectListView): queryset = PowerPort.objects.all() filterset = filtersets.PowerPortFilterSet @@ -2404,6 +2560,7 @@ class PowerPortView(generic.ObjectView): queryset = PowerPort.objects.all() +@register_model_view(PowerPort, 'add', detail=False) class PowerPortCreateView(generic.ComponentCreateView): queryset = PowerPort.objects.all() form = forms.PowerPortCreateForm @@ -2421,11 +2578,13 @@ class PowerPortDeleteView(generic.ObjectDeleteView): queryset = PowerPort.objects.all() +@register_model_view(PowerPort, 'bulk_import', detail=False) class PowerPortBulkImportView(generic.BulkImportView): queryset = PowerPort.objects.all() model_form = forms.PowerPortImportForm +@register_model_view(PowerPort, 'bulk_edit', path='edit', detail=False) class PowerPortBulkEditView(generic.BulkEditView): queryset = PowerPort.objects.all() filterset = filtersets.PowerPortFilterSet @@ -2433,14 +2592,17 @@ class PowerPortBulkEditView(generic.BulkEditView): form = forms.PowerPortBulkEditForm +@register_model_view(PowerPort, 'bulk_rename', path='rename', detail=False) class PowerPortBulkRenameView(generic.BulkRenameView): queryset = PowerPort.objects.all() +@register_model_view(PowerPort, 'bulk_disconnect', path='disconnect', detail=False) class PowerPortBulkDisconnectView(BulkDisconnectView): queryset = PowerPort.objects.all() +@register_model_view(PowerPort, 'bulk_delete', path='delete', detail=False) class PowerPortBulkDeleteView(generic.BulkDeleteView): queryset = PowerPort.objects.all() filterset = filtersets.PowerPortFilterSet @@ -2455,6 +2617,7 @@ register_model_view(PowerPort, 'trace', kwargs={'model': PowerPort})(PathTraceVi # Power outlets # +@register_model_view(PowerOutlet, 'list', path='', detail=False) class PowerOutletListView(generic.ObjectListView): queryset = PowerOutlet.objects.all() filterset = filtersets.PowerOutletFilterSet @@ -2472,6 +2635,7 @@ class PowerOutletView(generic.ObjectView): queryset = PowerOutlet.objects.all() +@register_model_view(PowerOutlet, 'add', detail=False) class PowerOutletCreateView(generic.ComponentCreateView): queryset = PowerOutlet.objects.all() form = forms.PowerOutletCreateForm @@ -2489,11 +2653,13 @@ class PowerOutletDeleteView(generic.ObjectDeleteView): queryset = PowerOutlet.objects.all() +@register_model_view(PowerOutlet, 'bulk_import', detail=False) class PowerOutletBulkImportView(generic.BulkImportView): queryset = PowerOutlet.objects.all() model_form = forms.PowerOutletImportForm +@register_model_view(PowerOutlet, 'bulk_edit', path='edit', detail=False) class PowerOutletBulkEditView(generic.BulkEditView): queryset = PowerOutlet.objects.all() filterset = filtersets.PowerOutletFilterSet @@ -2501,14 +2667,17 @@ class PowerOutletBulkEditView(generic.BulkEditView): form = forms.PowerOutletBulkEditForm +@register_model_view(PowerOutlet, 'bulk_rename', path='rename', detail=False) class PowerOutletBulkRenameView(generic.BulkRenameView): queryset = PowerOutlet.objects.all() +@register_model_view(PowerOutlet, 'bulk_disconnect', path='disconnect', detail=False) class PowerOutletBulkDisconnectView(BulkDisconnectView): queryset = PowerOutlet.objects.all() +@register_model_view(PowerOutlet, 'bulk_delete', path='delete', detail=False) class PowerOutletBulkDeleteView(generic.BulkDeleteView): queryset = PowerOutlet.objects.all() filterset = filtersets.PowerOutletFilterSet @@ -2523,6 +2692,7 @@ register_model_view(PowerOutlet, 'trace', kwargs={'model': PowerOutlet})(PathTra # Interfaces # +@register_model_view(Interface, 'list', path='', detail=False) class InterfaceListView(generic.ObjectListView): queryset = Interface.objects.all() filterset = filtersets.InterfaceFilterSet @@ -2552,7 +2722,7 @@ class InterfaceView(generic.ObjectView): # Get bridge interfaces bridge_interfaces = Interface.objects.restrict(request.user, 'view').filter(bridge=instance) - bridge_interfaces_tables = tables.InterfaceTable( + bridge_interfaces_table = tables.InterfaceTable( bridge_interfaces, exclude=('device', 'parent'), orderable=False @@ -2560,7 +2730,7 @@ class InterfaceView(generic.ObjectView): # Get child interfaces child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance) - child_interfaces_tables = tables.InterfaceTable( + child_interfaces_table = tables.InterfaceTable( child_interfaces, exclude=('device', 'parent'), orderable=False @@ -2580,14 +2750,24 @@ class InterfaceView(generic.ObjectView): orderable=False ) + # Get VLAN translation rules + vlan_translation_table = None + if instance.vlan_translation_policy: + vlan_translation_table = VLANTranslationRuleTable( + data=instance.vlan_translation_policy.rules.all(), + orderable=False + ) + return { 'vdc_table': vdc_table, - 'bridge_interfaces_table': bridge_interfaces_tables, - 'child_interfaces_table': child_interfaces_tables, + 'bridge_interfaces_table': bridge_interfaces_table, + 'child_interfaces_table': child_interfaces_table, 'vlan_table': vlan_table, + 'vlan_translation_table': vlan_translation_table, } +@register_model_view(Interface, 'add', detail=False) class InterfaceCreateView(generic.ComponentCreateView): queryset = Interface.objects.all() form = forms.InterfaceCreateForm @@ -2605,26 +2785,41 @@ class InterfaceDeleteView(generic.ObjectDeleteView): queryset = Interface.objects.all() +@register_model_view(Interface, 'bulk_import', detail=False) class InterfaceBulkImportView(generic.BulkImportView): queryset = Interface.objects.all() model_form = forms.InterfaceImportForm +@register_model_view(Interface, 'bulk_edit', path='edit', detail=False) class InterfaceBulkEditView(generic.BulkEditView): queryset = Interface.objects.all() filterset = filtersets.InterfaceFilterSet table = tables.InterfaceTable form = forms.InterfaceBulkEditForm + def post_save_operations(self, form, obj): + super().post_save_operations(form, obj) + # Add/remove tagged VLANs + if obj.mode == InterfaceModeChoices.MODE_TAGGED: + if form.cleaned_data.get('add_tagged_vlans', None): + obj.tagged_vlans.add(*form.cleaned_data['add_tagged_vlans']) + if form.cleaned_data.get('remove_tagged_vlans', None): + obj.tagged_vlans.remove(*form.cleaned_data['remove_tagged_vlans']) + + +@register_model_view(Interface, 'bulk_rename', path='rename', detail=False) class InterfaceBulkRenameView(generic.BulkRenameView): queryset = Interface.objects.all() +@register_model_view(Interface, 'bulk_disconnect', path='disconnect', detail=False) class InterfaceBulkDisconnectView(BulkDisconnectView): queryset = Interface.objects.all() +@register_model_view(Interface, 'bulk_delete', path='delete', detail=False) class InterfaceBulkDeleteView(generic.BulkDeleteView): # Ensure child interfaces are deleted prior to their parents queryset = Interface.objects.order_by('device', 'parent', CollateAsChar('_name')) @@ -2640,6 +2835,7 @@ register_model_view(Interface, 'trace', kwargs={'model': Interface})(PathTraceVi # Front ports # +@register_model_view(FrontPort, 'list', path='', detail=False) class FrontPortListView(generic.ObjectListView): queryset = FrontPort.objects.all() filterset = filtersets.FrontPortFilterSet @@ -2657,6 +2853,7 @@ class FrontPortView(generic.ObjectView): queryset = FrontPort.objects.all() +@register_model_view(FrontPort, 'add', detail=False) class FrontPortCreateView(generic.ComponentCreateView): queryset = FrontPort.objects.all() form = forms.FrontPortCreateForm @@ -2674,11 +2871,13 @@ class FrontPortDeleteView(generic.ObjectDeleteView): queryset = FrontPort.objects.all() +@register_model_view(FrontPort, 'bulk_import', detail=False) class FrontPortBulkImportView(generic.BulkImportView): queryset = FrontPort.objects.all() model_form = forms.FrontPortImportForm +@register_model_view(FrontPort, 'bulk_edit', path='edit', detail=False) class FrontPortBulkEditView(generic.BulkEditView): queryset = FrontPort.objects.all() filterset = filtersets.FrontPortFilterSet @@ -2686,14 +2885,17 @@ class FrontPortBulkEditView(generic.BulkEditView): form = forms.FrontPortBulkEditForm +@register_model_view(FrontPort, 'bulk_rename', path='rename', detail=False) class FrontPortBulkRenameView(generic.BulkRenameView): queryset = FrontPort.objects.all() +@register_model_view(FrontPort, 'bulk_disconnect', path='disconnect', detail=False) class FrontPortBulkDisconnectView(BulkDisconnectView): queryset = FrontPort.objects.all() +@register_model_view(FrontPort, 'bulk_delete', path='delete', detail=False) class FrontPortBulkDeleteView(generic.BulkDeleteView): queryset = FrontPort.objects.all() filterset = filtersets.FrontPortFilterSet @@ -2708,6 +2910,7 @@ register_model_view(FrontPort, 'trace', kwargs={'model': FrontPort})(PathTraceVi # Rear ports # +@register_model_view(RearPort, 'list', path='', detail=False) class RearPortListView(generic.ObjectListView): queryset = RearPort.objects.all() filterset = filtersets.RearPortFilterSet @@ -2725,6 +2928,7 @@ class RearPortView(generic.ObjectView): queryset = RearPort.objects.all() +@register_model_view(RearPort, 'add', detail=False) class RearPortCreateView(generic.ComponentCreateView): queryset = RearPort.objects.all() form = forms.RearPortCreateForm @@ -2742,11 +2946,13 @@ class RearPortDeleteView(generic.ObjectDeleteView): queryset = RearPort.objects.all() +@register_model_view(RearPort, 'bulk_import', detail=False) class RearPortBulkImportView(generic.BulkImportView): queryset = RearPort.objects.all() model_form = forms.RearPortImportForm +@register_model_view(RearPort, 'bulk_edit', path='edit', detail=False) class RearPortBulkEditView(generic.BulkEditView): queryset = RearPort.objects.all() filterset = filtersets.RearPortFilterSet @@ -2754,14 +2960,17 @@ class RearPortBulkEditView(generic.BulkEditView): form = forms.RearPortBulkEditForm +@register_model_view(RearPort, 'bulk_rename', path='rename', detail=False) class RearPortBulkRenameView(generic.BulkRenameView): queryset = RearPort.objects.all() +@register_model_view(RearPort, 'bulk_disconnect', path='disconnect', detail=False) class RearPortBulkDisconnectView(BulkDisconnectView): queryset = RearPort.objects.all() +@register_model_view(RearPort, 'bulk_delete', path='delete', detail=False) class RearPortBulkDeleteView(generic.BulkDeleteView): queryset = RearPort.objects.all() filterset = filtersets.RearPortFilterSet @@ -2776,6 +2985,7 @@ register_model_view(RearPort, 'trace', kwargs={'model': RearPort})(PathTraceView # Module bays # +@register_model_view(ModuleBay, 'list', path='', detail=False) class ModuleBayListView(generic.ObjectListView): queryset = ModuleBay.objects.select_related('installed_module__module_type') filterset = filtersets.ModuleBayFilterSet @@ -2793,6 +3003,7 @@ class ModuleBayView(generic.ObjectView): queryset = ModuleBay.objects.all() +@register_model_view(ModuleBay, 'add', detail=False) class ModuleBayCreateView(generic.ComponentCreateView): queryset = ModuleBay.objects.all() form = forms.ModuleBayCreateForm @@ -2810,11 +3021,13 @@ class ModuleBayDeleteView(generic.ObjectDeleteView): queryset = ModuleBay.objects.all() +@register_model_view(ModuleBay, 'bulk_import', detail=False) class ModuleBayBulkImportView(generic.BulkImportView): queryset = ModuleBay.objects.all() model_form = forms.ModuleBayImportForm +@register_model_view(ModuleBay, 'bulk_edit', path='edit', detail=False) class ModuleBayBulkEditView(generic.BulkEditView): queryset = ModuleBay.objects.all() filterset = filtersets.ModuleBayFilterSet @@ -2822,10 +3035,12 @@ class ModuleBayBulkEditView(generic.BulkEditView): form = forms.ModuleBayBulkEditForm +@register_model_view(ModuleBay, 'bulk_rename', path='rename', detail=False) class ModuleBayBulkRenameView(generic.BulkRenameView): queryset = ModuleBay.objects.all() +@register_model_view(ModuleBay, 'bulk_delete', path='delete', detail=False) class ModuleBayBulkDeleteView(generic.BulkDeleteView): queryset = ModuleBay.objects.all() filterset = filtersets.ModuleBayFilterSet @@ -2836,6 +3051,7 @@ class ModuleBayBulkDeleteView(generic.BulkDeleteView): # Device bays # +@register_model_view(DeviceBay, 'list', path='', detail=False) class DeviceBayListView(generic.ObjectListView): queryset = DeviceBay.objects.all() filterset = filtersets.DeviceBayFilterSet @@ -2853,6 +3069,7 @@ class DeviceBayView(generic.ObjectView): queryset = DeviceBay.objects.all() +@register_model_view(DeviceBay, 'add', detail=False) class DeviceBayCreateView(generic.ComponentCreateView): queryset = DeviceBay.objects.all() form = forms.DeviceBayCreateForm @@ -2951,11 +3168,13 @@ class DeviceBayDepopulateView(generic.ObjectEditView): }) +@register_model_view(DeviceBay, 'bulk_import', detail=False) class DeviceBayBulkImportView(generic.BulkImportView): queryset = DeviceBay.objects.all() model_form = forms.DeviceBayImportForm +@register_model_view(DeviceBay, 'bulk_edit', path='edit', detail=False) class DeviceBayBulkEditView(generic.BulkEditView): queryset = DeviceBay.objects.all() filterset = filtersets.DeviceBayFilterSet @@ -2963,10 +3182,12 @@ class DeviceBayBulkEditView(generic.BulkEditView): form = forms.DeviceBayBulkEditForm +@register_model_view(DeviceBay, 'bulk_rename', path='rename', detail=False) class DeviceBayBulkRenameView(generic.BulkRenameView): queryset = DeviceBay.objects.all() +@register_model_view(DeviceBay, 'bulk_delete', path='delete', detail=False) class DeviceBayBulkDeleteView(generic.BulkDeleteView): queryset = DeviceBay.objects.all() filterset = filtersets.DeviceBayFilterSet @@ -2977,6 +3198,7 @@ class DeviceBayBulkDeleteView(generic.BulkDeleteView): # Inventory items # +@register_model_view(InventoryItem, 'list', path='', detail=False) class InventoryItemListView(generic.ObjectListView): queryset = InventoryItem.objects.all() filterset = filtersets.InventoryItemFilterSet @@ -3000,6 +3222,7 @@ class InventoryItemEditView(generic.ObjectEditView): form = forms.InventoryItemForm +@register_model_view(InventoryItem, 'add', detail=False) class InventoryItemCreateView(generic.ComponentCreateView): queryset = InventoryItem.objects.all() form = forms.InventoryItemCreateForm @@ -3011,11 +3234,13 @@ class InventoryItemDeleteView(generic.ObjectDeleteView): queryset = InventoryItem.objects.all() +@register_model_view(InventoryItem, 'bulk_import', detail=False) class InventoryItemBulkImportView(generic.BulkImportView): queryset = InventoryItem.objects.all() model_form = forms.InventoryItemImportForm +@register_model_view(InventoryItem, 'bulk_edit', path='edit', detail=False) class InventoryItemBulkEditView(generic.BulkEditView): queryset = InventoryItem.objects.all() filterset = filtersets.InventoryItemFilterSet @@ -3023,10 +3248,12 @@ class InventoryItemBulkEditView(generic.BulkEditView): form = forms.InventoryItemBulkEditForm +@register_model_view(InventoryItem, 'bulk_rename', path='rename', detail=False) class InventoryItemBulkRenameView(generic.BulkRenameView): queryset = InventoryItem.objects.all() +@register_model_view(InventoryItem, 'bulk_delete', path='delete', detail=False) class InventoryItemBulkDeleteView(generic.BulkDeleteView): queryset = InventoryItem.objects.all() filterset = filtersets.InventoryItemFilterSet @@ -3056,6 +3283,7 @@ class InventoryItemChildrenView(generic.ObjectChildrenView): # Inventory item roles # +@register_model_view(InventoryItemRole, 'list', path='', detail=False) class InventoryItemRoleListView(generic.ObjectListView): queryset = InventoryItemRole.objects.annotate( inventoryitem_count=count_related(InventoryItem, 'role'), @@ -3075,6 +3303,7 @@ class InventoryItemRoleView(generic.ObjectView): } +@register_model_view(InventoryItemRole, 'add', detail=False) @register_model_view(InventoryItemRole, 'edit') class InventoryItemRoleEditView(generic.ObjectEditView): queryset = InventoryItemRole.objects.all() @@ -3086,11 +3315,13 @@ class InventoryItemRoleDeleteView(generic.ObjectDeleteView): queryset = InventoryItemRole.objects.all() +@register_model_view(InventoryItemRole, 'bulk_import', detail=False) class InventoryItemRoleBulkImportView(generic.BulkImportView): queryset = InventoryItemRole.objects.all() model_form = forms.InventoryItemRoleImportForm +@register_model_view(InventoryItemRole, 'bulk_edit', path='edit', detail=False) class InventoryItemRoleBulkEditView(generic.BulkEditView): queryset = InventoryItemRole.objects.annotate( inventoryitem_count=count_related(InventoryItem, 'role'), @@ -3100,6 +3331,7 @@ class InventoryItemRoleBulkEditView(generic.BulkEditView): form = forms.InventoryItemRoleBulkEditForm +@register_model_view(InventoryItemRole, 'bulk_delete', path='delete', detail=False) class InventoryItemRoleBulkDeleteView(generic.BulkDeleteView): queryset = InventoryItemRole.objects.annotate( inventoryitem_count=count_related(InventoryItem, 'role'), @@ -3167,17 +3399,6 @@ class DeviceBulkAddInterfaceView(generic.BulkComponentCreateView): default_return_url = 'dcim:device_list' -# class DeviceBulkAddFrontPortView(generic.BulkComponentCreateView): -# parent_model = Device -# parent_field = 'device' -# form = forms.FrontPortBulkCreateForm -# queryset = FrontPort.objects.all() -# model_form = forms.FrontPortForm -# filterset = filtersets.DeviceFilterSet -# table = tables.DeviceTable -# default_return_url = 'dcim:device_list' - - class DeviceBulkAddRearPortView(generic.BulkComponentCreateView): parent_model = Device parent_field = 'device' @@ -3226,6 +3447,7 @@ class DeviceBulkAddInventoryItemView(generic.BulkComponentCreateView): # Cables # +@register_model_view(Cable, 'list', path='', detail=False) class CableListView(generic.ObjectListView): queryset = Cable.objects.prefetch_related( 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location', @@ -3241,6 +3463,7 @@ class CableView(generic.ObjectView): queryset = Cable.objects.all() +@register_model_view(Cable, 'add', detail=False) @register_model_view(Cable, 'edit') class CableEditView(generic.ObjectEditView): queryset = Cable.objects.all() @@ -3288,11 +3511,13 @@ class CableDeleteView(generic.ObjectDeleteView): queryset = Cable.objects.all() +@register_model_view(Cable, 'bulk_import', detail=False) class CableBulkImportView(generic.BulkImportView): queryset = Cable.objects.all() model_form = forms.CableImportForm +@register_model_view(Cable, 'bulk_edit', path='edit', detail=False) class CableBulkEditView(generic.BulkEditView): queryset = Cable.objects.prefetch_related( 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location', @@ -3303,6 +3528,7 @@ class CableBulkEditView(generic.BulkEditView): form = forms.CableBulkEditForm +@register_model_view(Cable, 'bulk_delete', path='delete', detail=False) class CableBulkDeleteView(generic.BulkDeleteView): queryset = Cable.objects.prefetch_related( 'terminations__termination', 'terminations___device', 'terminations___rack', 'terminations___location', @@ -3368,6 +3594,7 @@ class InterfaceConnectionsListView(generic.ObjectListView): # Virtual chassis # +@register_model_view(VirtualChassis, 'list', path='', detail=False) class VirtualChassisListView(generic.ObjectListView): queryset = VirtualChassis.objects.all() table = tables.VirtualChassisTable @@ -3387,6 +3614,7 @@ class VirtualChassisView(generic.ObjectView): } +@register_model_view(VirtualChassis, 'add', detail=False) class VirtualChassisCreateView(generic.ObjectEditView): queryset = VirtualChassis.objects.all() form = forms.VirtualChassisCreateForm @@ -3507,7 +3735,9 @@ class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMix membership_form.save() messages.success(request, mark_safe( - _('Added member {device}').format(url=device.get_absolute_url(), device=escape(device)) + _('Added member {device}').format( + url=device.get_absolute_url(), device=escape(device) + ) )) if '_addanother' in request.POST: @@ -3582,11 +3812,13 @@ class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURL }) +@register_model_view(VirtualChassis, 'bulk_import', detail=False) class VirtualChassisBulkImportView(generic.BulkImportView): queryset = VirtualChassis.objects.all() model_form = forms.VirtualChassisImportForm +@register_model_view(VirtualChassis, 'bulk_edit', path='edit', detail=False) class VirtualChassisBulkEditView(generic.BulkEditView): queryset = VirtualChassis.objects.all() filterset = filtersets.VirtualChassisFilterSet @@ -3594,6 +3826,7 @@ class VirtualChassisBulkEditView(generic.BulkEditView): form = forms.VirtualChassisBulkEditForm +@register_model_view(VirtualChassis, 'bulk_delete', path='delete', detail=False) class VirtualChassisBulkDeleteView(generic.BulkDeleteView): queryset = VirtualChassis.objects.all() filterset = filtersets.VirtualChassisFilterSet @@ -3604,6 +3837,7 @@ class VirtualChassisBulkDeleteView(generic.BulkDeleteView): # Power panels # +@register_model_view(PowerPanel, 'list', path='', detail=False) class PowerPanelListView(generic.ObjectListView): queryset = PowerPanel.objects.annotate( powerfeed_count=count_related(PowerFeed, 'power_panel') @@ -3623,6 +3857,7 @@ class PowerPanelView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(PowerPanel, 'add', detail=False) @register_model_view(PowerPanel, 'edit') class PowerPanelEditView(generic.ObjectEditView): queryset = PowerPanel.objects.all() @@ -3634,11 +3869,13 @@ class PowerPanelDeleteView(generic.ObjectDeleteView): queryset = PowerPanel.objects.all() +@register_model_view(PowerPanel, 'bulk_import', detail=False) class PowerPanelBulkImportView(generic.BulkImportView): queryset = PowerPanel.objects.all() model_form = forms.PowerPanelImportForm +@register_model_view(PowerPanel, 'bulk_edit', path='edit', detail=False) class PowerPanelBulkEditView(generic.BulkEditView): queryset = PowerPanel.objects.all() filterset = filtersets.PowerPanelFilterSet @@ -3646,6 +3883,7 @@ class PowerPanelBulkEditView(generic.BulkEditView): form = forms.PowerPanelBulkEditForm +@register_model_view(PowerPanel, 'bulk_delete', path='delete', detail=False) class PowerPanelBulkDeleteView(generic.BulkDeleteView): queryset = PowerPanel.objects.annotate( powerfeed_count=count_related(PowerFeed, 'power_panel') @@ -3663,6 +3901,7 @@ class PowerPanelContactsView(ObjectContactsView): # Power feeds # +@register_model_view(PowerFeed, 'list', path='', detail=False) class PowerFeedListView(generic.ObjectListView): queryset = PowerFeed.objects.all() filterset = filtersets.PowerFeedFilterSet @@ -3675,6 +3914,7 @@ class PowerFeedView(generic.ObjectView): queryset = PowerFeed.objects.all() +@register_model_view(PowerFeed, 'add', detail=False) @register_model_view(PowerFeed, 'edit') class PowerFeedEditView(generic.ObjectEditView): queryset = PowerFeed.objects.all() @@ -3686,11 +3926,13 @@ class PowerFeedDeleteView(generic.ObjectDeleteView): queryset = PowerFeed.objects.all() +@register_model_view(PowerFeed, 'bulk_import', detail=False) class PowerFeedBulkImportView(generic.BulkImportView): queryset = PowerFeed.objects.all() model_form = forms.PowerFeedImportForm +@register_model_view(PowerFeed, 'bulk_edit', path='edit', detail=False) class PowerFeedBulkEditView(generic.BulkEditView): queryset = PowerFeed.objects.all() filterset = filtersets.PowerFeedFilterSet @@ -3698,10 +3940,12 @@ class PowerFeedBulkEditView(generic.BulkEditView): form = forms.PowerFeedBulkEditForm +@register_model_view(PowerFeed, 'bulk_disconnect', path='disconnect', detail=False) class PowerFeedBulkDisconnectView(BulkDisconnectView): queryset = PowerFeed.objects.all() +@register_model_view(PowerFeed, 'bulk_delete', path='delete', detail=False) class PowerFeedBulkDeleteView(generic.BulkDeleteView): queryset = PowerFeed.objects.all() filterset = filtersets.PowerFeedFilterSet @@ -3712,7 +3956,11 @@ class PowerFeedBulkDeleteView(generic.BulkDeleteView): register_model_view(PowerFeed, 'trace', kwargs={'model': PowerFeed})(PathTraceView) -# VDC +# +# Virtual device contexts +# + +@register_model_view(VirtualDeviceContext, 'list', path='', detail=False) class VirtualDeviceContextListView(generic.ObjectListView): queryset = VirtualDeviceContext.objects.annotate( interface_count=count_related(Interface, 'vdcs'), @@ -3738,6 +3986,7 @@ class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(VirtualDeviceContext, 'add', detail=False) @register_model_view(VirtualDeviceContext, 'edit') class VirtualDeviceContextEditView(generic.ObjectEditView): queryset = VirtualDeviceContext.objects.all() @@ -3749,11 +3998,13 @@ class VirtualDeviceContextDeleteView(generic.ObjectDeleteView): queryset = VirtualDeviceContext.objects.all() +@register_model_view(VirtualDeviceContext, 'bulk_import', detail=False) class VirtualDeviceContextBulkImportView(generic.BulkImportView): queryset = VirtualDeviceContext.objects.all() model_form = forms.VirtualDeviceContextImportForm +@register_model_view(VirtualDeviceContext, 'bulk_edit', path='edit', detail=False) class VirtualDeviceContextBulkEditView(generic.BulkEditView): queryset = VirtualDeviceContext.objects.all() filterset = filtersets.VirtualDeviceContextFilterSet @@ -3761,7 +4012,58 @@ class VirtualDeviceContextBulkEditView(generic.BulkEditView): form = forms.VirtualDeviceContextBulkEditForm +@register_model_view(VirtualDeviceContext, 'bulk_delete', path='delete', detail=False) class VirtualDeviceContextBulkDeleteView(generic.BulkDeleteView): queryset = VirtualDeviceContext.objects.all() filterset = filtersets.VirtualDeviceContextFilterSet table = tables.VirtualDeviceContextTable + + +# +# MAC addresses +# + +@register_model_view(MACAddress, 'list', path='', detail=False) +class MACAddressListView(generic.ObjectListView): + queryset = MACAddress.objects.all() + filterset = filtersets.MACAddressFilterSet + filterset_form = forms.MACAddressFilterForm + table = tables.MACAddressTable + + +@register_model_view(MACAddress) +class MACAddressView(generic.ObjectView): + queryset = MACAddress.objects.all() + + +@register_model_view(MACAddress, 'add', detail=False) +@register_model_view(MACAddress, 'edit') +class MACAddressEditView(generic.ObjectEditView): + queryset = MACAddress.objects.all() + form = forms.MACAddressForm + + +@register_model_view(MACAddress, 'delete') +class MACAddressDeleteView(generic.ObjectDeleteView): + queryset = MACAddress.objects.all() + + +@register_model_view(MACAddress, 'bulk_import', detail=False) +class MACAddressBulkImportView(generic.BulkImportView): + queryset = MACAddress.objects.all() + model_form = forms.MACAddressImportForm + + +@register_model_view(MACAddress, 'bulk_edit', path='edit', detail=False) +class MACAddressBulkEditView(generic.BulkEditView): + queryset = MACAddress.objects.all() + filterset = filtersets.MACAddressFilterSet + table = tables.MACAddressTable + form = forms.MACAddressBulkEditForm + + +@register_model_view(MACAddress, 'bulk_delete', path='delete', detail=False) +class MACAddressBulkDeleteView(generic.BulkDeleteView): + queryset = MACAddress.objects.all() + filterset = filtersets.MACAddressFilterSet + table = tables.MACAddressTable diff --git a/netbox/extras/api/nested_serializers.py b/netbox/extras/api/nested_serializers.py deleted file mode 100644 index 235cdd6d6..000000000 --- a/netbox/extras/api/nested_serializers.py +++ /dev/null @@ -1,135 +0,0 @@ -import warnings - -from rest_framework import serializers - -from extras import models -from netbox.api.serializers import NestedTagSerializer, WritableNestedSerializer - -__all__ = [ - 'NestedBookmarkSerializer', - 'NestedConfigContextSerializer', - 'NestedConfigTemplateSerializer', - 'NestedCustomFieldChoiceSetSerializer', - 'NestedCustomFieldSerializer', - 'NestedCustomLinkSerializer', - 'NestedEventRuleSerializer', - 'NestedExportTemplateSerializer', - 'NestedImageAttachmentSerializer', - 'NestedJournalEntrySerializer', - 'NestedSavedFilterSerializer', - 'NestedScriptSerializer', - 'NestedTagSerializer', # Defined in netbox.api.serializers - 'NestedWebhookSerializer', -] - -# TODO: Remove in v4.2 -warnings.warn( - "Dedicated nested serializers will be removed in NetBox v4.2. Use Serializer(nested=True) instead.", - DeprecationWarning -) - - -class NestedEventRuleSerializer(WritableNestedSerializer): - - class Meta: - model = models.EventRule - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedWebhookSerializer(WritableNestedSerializer): - - class Meta: - model = models.Webhook - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedCustomFieldSerializer(WritableNestedSerializer): - - class Meta: - model = models.CustomField - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedCustomFieldChoiceSetSerializer(WritableNestedSerializer): - - class Meta: - model = models.CustomFieldChoiceSet - fields = ['id', 'url', 'display_url', 'display', 'name', 'choices_count'] - - -class NestedCustomLinkSerializer(WritableNestedSerializer): - - class Meta: - model = models.CustomLink - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedConfigContextSerializer(WritableNestedSerializer): - - class Meta: - model = models.ConfigContext - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedConfigTemplateSerializer(WritableNestedSerializer): - - class Meta: - model = models.ConfigTemplate - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedExportTemplateSerializer(WritableNestedSerializer): - - class Meta: - model = models.ExportTemplate - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -class NestedSavedFilterSerializer(WritableNestedSerializer): - - class Meta: - model = models.SavedFilter - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug'] - - -class NestedBookmarkSerializer(WritableNestedSerializer): - - class Meta: - model = models.Bookmark - fields = ['id', 'url', 'display', 'object_id', 'object_type'] - - -class NestedImageAttachmentSerializer(WritableNestedSerializer): - - class Meta: - model = models.ImageAttachment - fields = ['id', 'url', 'display', 'name', 'image'] - - -class NestedJournalEntrySerializer(WritableNestedSerializer): - - class Meta: - model = models.JournalEntry - fields = ['id', 'url', 'display_url', 'display', 'created'] - - -class NestedScriptSerializer(WritableNestedSerializer): - url = serializers.HyperlinkedIdentityField( - view_name='extras-api:script-detail', - lookup_field='full_name', - lookup_url_kwarg='pk' - ) - display_url = serializers.HyperlinkedIdentityField( - view_name='extras:script', - lookup_field='full_name', - lookup_url_kwarg='pk' - ) - name = serializers.CharField(read_only=True) - display = serializers.SerializerMethodField(read_only=True) - - class Meta: - model = models.Script - fields = ['id', 'url', 'display_url', 'display', 'name'] - - def get_display(self, obj): - return f'{obj.name} ({obj.module})' diff --git a/netbox/extras/forms/bulk_import.py b/netbox/extras/forms/bulk_import.py index 258df8264..655a5d6ca 100644 --- a/netbox/extras/forms/bulk_import.py +++ b/netbox/extras/forms/bulk_import.py @@ -223,7 +223,7 @@ class EventRuleImportForm(NetBoxModelImportForm): from extras.scripts import get_module_and_script module_name, script_name = action_object.split('.', 1) try: - module, script = get_module_and_script(module_name, script_name) + script = get_module_and_script(module_name, script_name)[1] except ObjectDoesNotExist: raise forms.ValidationError(_("Script {name} not found").format(name=action_object)) self.instance.action_object = script diff --git a/netbox/extras/jobs.py b/netbox/extras/jobs.py index 64a7d6a69..190166b5b 100644 --- a/netbox/extras/jobs.py +++ b/netbox/extras/jobs.py @@ -22,9 +22,7 @@ class ScriptJob(JobRunner): """ class Meta: - # An explicit job name is not set because it doesn't make sense in this context. Currently, there's no scenario - # where jobs other than this one are used. Therefore, it is hidden, resulting in a cleaner job table overview. - name = '' + name = 'Run Script' def run_script(self, script, request, data, commit): """ @@ -49,7 +47,6 @@ class ScriptJob(JobRunner): script.log_info(message=_("Database changes have been reverted automatically.")) if script.failed: logger.warning("Script failed") - raise except Exception as e: if type(e) is AbortScript: diff --git a/netbox/extras/management/commands/reindex.py b/netbox/extras/management/commands/reindex.py index 21442be93..5495bbf5f 100644 --- a/netbox/extras/management/commands/reindex.py +++ b/netbox/extras/management/commands/reindex.py @@ -53,7 +53,8 @@ class Command(BaseCommand): else: raise CommandError( - f"Invalid model: {label}. Model names must be in the format or .." + f"Invalid model: {label}. Model names must be in the format or " + f".." ) return indexers diff --git a/netbox/extras/management/commands/runscript.py b/netbox/extras/management/commands/runscript.py index d5fb435ad..847d89396 100644 --- a/netbox/extras/management/commands/runscript.py +++ b/netbox/extras/management/commands/runscript.py @@ -38,7 +38,7 @@ class Command(BaseCommand): data = {} module_name, script_name = script.split('.', 1) - module, script_obj = get_module_and_script(module_name, script_name) + script_obj = get_module_and_script(module_name, script_name)[1] script = script_obj.python_class # Take user from command line if provided and exists, other diff --git a/netbox/extras/migrations/0001_squashed.py b/netbox/extras/migrations/0001_squashed.py index 6f1f77e53..a2514fa5e 100644 --- a/netbox/extras/migrations/0001_squashed.py +++ b/netbox/extras/migrations/0001_squashed.py @@ -9,7 +9,6 @@ import utilities.validators class Migration(migrations.Migration): - initial = True dependencies = [ @@ -99,8 +98,22 @@ class Migration(migrations.Migration): fields=[ ('object_id', models.IntegerField(db_index=True)), ('id', models.BigAutoField(primary_key=True, serialize=False)), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_tagged_items', to='contenttypes.contenttype')), - ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_items', to='extras.tag')), + ( + 'content_type', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='%(app_label)s_%(class)s_tagged_items', + to='contenttypes.contenttype', + ), + ), + ( + 'tag', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='%(app_label)s_%(class)s_items', + to='extras.tag', + ), + ), ], ), migrations.CreateModel( @@ -116,9 +129,32 @@ class Migration(migrations.Migration): ('object_repr', models.CharField(editable=False, max_length=200)), ('prechange_data', models.JSONField(blank=True, editable=False, null=True)), ('postchange_data', models.JSONField(blank=True, editable=False, null=True)), - ('changed_object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')), - ('related_object_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='changes', to=settings.AUTH_USER_MODEL)), + ( + 'changed_object_type', + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype' + ), + ), + ( + 'related_object_type', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), + ), + ( + 'user', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='changes', + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ 'ordering': ['-time'], @@ -133,8 +169,16 @@ class Migration(migrations.Migration): ('created', models.DateTimeField(auto_now_add=True)), ('kind', models.CharField(default='info', max_length=30)), ('comments', models.TextField()), - ('assigned_object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), - ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + 'assigned_object_type', + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype'), + ), + ( + 'created_by', + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL + ), + ), ], options={ 'verbose_name_plural': 'journal entries', @@ -151,8 +195,24 @@ class Migration(migrations.Migration): ('status', models.CharField(default='pending', max_length=30)), ('data', models.JSONField(blank=True, null=True)), ('job_id', models.UUIDField(unique=True)), - ('obj_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='job_results', to='contenttypes.contenttype')), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ( + 'obj_type', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='job_results', + to='contenttypes.contenttype', + ), + ), + ( + 'user', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ 'ordering': ['obj_type', 'name', '-created'], @@ -163,12 +223,20 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(primary_key=True, serialize=False)), ('object_id', models.PositiveIntegerField()), - ('image', models.ImageField(height_field='image_height', upload_to=extras.utils.image_upload, width_field='image_width')), + ( + 'image', + models.ImageField( + height_field='image_height', upload_to=extras.utils.image_upload, width_field='image_width' + ), + ), ('image_height', models.PositiveSmallIntegerField()), ('image_width', models.PositiveSmallIntegerField()), ('name', models.CharField(blank=True, max_length=50)), ('created', models.DateTimeField(auto_now_add=True)), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), + ( + 'content_type', + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype'), + ), ], options={ 'ordering': ('name', 'pk'), @@ -184,7 +252,10 @@ class Migration(migrations.Migration): ('mime_type', models.CharField(blank=True, max_length=50)), ('file_extension', models.CharField(blank=True, max_length=15)), ('as_attachment', models.BooleanField(default=True)), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), + ( + 'content_type', + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype'), + ), ], options={ 'ordering': ['content_type', 'name'], @@ -201,7 +272,10 @@ class Migration(migrations.Migration): ('group_name', models.CharField(blank=True, max_length=50)), ('button_class', models.CharField(default='default', max_length=30)), ('new_window', models.BooleanField(default=False)), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), + ( + 'content_type', + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype'), + ), ], options={ 'ordering': ['group_name', 'weight', 'name'], @@ -221,8 +295,16 @@ class Migration(migrations.Migration): ('weight', models.PositiveSmallIntegerField(default=100)), ('validation_minimum', models.PositiveIntegerField(blank=True, null=True)), ('validation_maximum', models.PositiveIntegerField(blank=True, null=True)), - ('validation_regex', models.CharField(blank=True, max_length=500, validators=[utilities.validators.validate_regex])), - ('choices', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=100), blank=True, null=True, size=None)), + ( + 'validation_regex', + models.CharField(blank=True, max_length=500, validators=[utilities.validators.validate_regex]), + ), + ( + 'choices', + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), blank=True, null=True, size=None + ), + ), ('content_types', models.ManyToManyField(related_name='custom_fields', to='contenttypes.ContentType')), ], options={ diff --git a/netbox/extras/migrations/0002_squashed_0059.py b/netbox/extras/migrations/0002_squashed_0059.py index a403a0e19..b664b286e 100644 --- a/netbox/extras/migrations/0002_squashed_0059.py +++ b/netbox/extras/migrations/0002_squashed_0059.py @@ -2,7 +2,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('dcim', '0002_auto_20160622_1821'), ('extras', '0001_initial'), diff --git a/netbox/extras/migrations/0060_squashed_0086.py b/netbox/extras/migrations/0060_squashed_0086.py index 0d5d03008..3bde7480f 100644 --- a/netbox/extras/migrations/0060_squashed_0086.py +++ b/netbox/extras/migrations/0060_squashed_0086.py @@ -12,7 +12,6 @@ import utilities.json class Migration(migrations.Migration): - replaces = [ ('extras', '0060_customlink_button_class'), ('extras', '0061_extras_change_logging'), @@ -40,7 +39,7 @@ class Migration(migrations.Migration): ('extras', '0083_search'), ('extras', '0084_staging'), ('extras', '0085_synced_data'), - ('extras', '0086_configtemplate') + ('extras', '0086_configtemplate'), ] dependencies = [ @@ -114,7 +113,23 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='customfield', name='name', - field=models.CharField(max_length=50, unique=True, validators=[django.core.validators.RegexValidator(flags=re.RegexFlag['IGNORECASE'], message='Only alphanumeric characters and underscores are allowed.', regex='^[a-z0-9_]+$'), django.core.validators.RegexValidator(flags=re.RegexFlag['IGNORECASE'], inverse_match=True, message='Double underscores are not permitted in custom field names.', regex='__')]), + field=models.CharField( + max_length=50, + unique=True, + validators=[ + django.core.validators.RegexValidator( + flags=re.RegexFlag['IGNORECASE'], + message='Only alphanumeric characters and underscores are allowed.', + regex='^[a-z0-9_]+$', + ), + django.core.validators.RegexValidator( + flags=re.RegexFlag['IGNORECASE'], + inverse_match=True, + message='Double underscores are not permitted in custom field names.', + regex='__', + ), + ], + ), ), migrations.AlterField( model_name='customfield', @@ -134,7 +149,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name='customfield', name='object_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype' + ), ), migrations.AddField( model_name='customlink', @@ -314,11 +331,16 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='exporttemplate', - constraint=models.UniqueConstraint(fields=('content_type', 'name'), name='extras_exporttemplate_unique_content_type_name'), + constraint=models.UniqueConstraint( + fields=('content_type', 'name'), name='extras_exporttemplate_unique_content_type_name' + ), ), migrations.AddConstraint( model_name='webhook', - constraint=models.UniqueConstraint(fields=('payload_url', 'type_create', 'type_update', 'type_delete'), name='extras_webhook_unique_payload_url_types'), + constraint=models.UniqueConstraint( + fields=('payload_url', 'type_create', 'type_update', 'type_delete'), + name='extras_webhook_unique_payload_url_types', + ), ), migrations.AddField( model_name='jobresult', @@ -328,7 +350,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name='jobresult', name='interval', - field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]), + field=models.PositiveIntegerField( + blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)] + ), ), migrations.AddField( model_name='jobresult', @@ -379,7 +403,12 @@ class Migration(migrations.Migration): ('shared', models.BooleanField(default=True)), ('parameters', models.JSONField()), ('content_types', models.ManyToManyField(related_name='saved_filters', to='contenttypes.contenttype')), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + 'user', + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL + ), + ), ], options={ 'ordering': ('weight', 'name'), @@ -400,7 +429,12 @@ class Migration(migrations.Migration): ('type', models.CharField(max_length=30)), ('value', extras.fields.CachedValueField()), ('weight', models.PositiveSmallIntegerField(default=1000)), - ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype')), + ( + 'object_type', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype' + ), + ), ], options={ 'ordering': ('weight', 'object_type', 'object_id'), @@ -414,7 +448,12 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('name', models.CharField(max_length=100, unique=True)), ('description', models.CharField(blank=True, max_length=200)), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + 'user', + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL + ), + ), ], options={ 'ordering': ('name',), @@ -429,8 +468,18 @@ class Migration(migrations.Migration): ('action', models.CharField(max_length=20)), ('object_id', models.PositiveBigIntegerField(blank=True, null=True)), ('data', models.JSONField(blank=True, null=True)), - ('branch', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='staged_changes', to='extras.branch')), - ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype')), + ( + 'branch', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='staged_changes', to='extras.branch' + ), + ), + ( + 'object_type', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype' + ), + ), ], options={ 'ordering': ('pk',), @@ -439,7 +488,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='configcontext', name='data_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='core.datafile'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='core.datafile', + ), ), migrations.AddField( model_name='configcontext', @@ -449,7 +504,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='configcontext', name='data_source', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='core.datasource'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='core.datasource', + ), ), migrations.AddField( model_name='configcontext', @@ -464,7 +525,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='exporttemplate', name='data_file', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='core.datafile'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='core.datafile', + ), ), migrations.AddField( model_name='exporttemplate', @@ -474,7 +541,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='exporttemplate', name='data_source', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='core.datasource'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='core.datasource', + ), ), migrations.AddField( model_name='exporttemplate', @@ -498,8 +571,26 @@ class Migration(migrations.Migration): ('description', models.CharField(blank=True, max_length=200)), ('template_code', models.TextField()), ('environment_params', models.JSONField(blank=True, default=dict, null=True)), - ('data_file', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='core.datafile')), - ('data_source', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='core.datasource')), + ( + 'data_file', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', + to='core.datafile', + ), + ), + ( + 'data_source', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='core.datasource', + ), + ), ('auto_sync_enabled', models.BooleanField(default=False)), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), ], diff --git a/netbox/extras/migrations/0087_squashed_0098.py b/netbox/extras/migrations/0087_squashed_0098.py index bbe7f79f5..839f4cbe4 100644 --- a/netbox/extras/migrations/0087_squashed_0098.py +++ b/netbox/extras/migrations/0087_squashed_0098.py @@ -9,7 +9,6 @@ import utilities.json class Migration(migrations.Migration): - replaces = [ ('extras', '0087_dashboard'), ('extras', '0088_jobresult_webhooks'), @@ -22,7 +21,7 @@ class Migration(migrations.Migration): ('extras', '0095_bookmarks'), ('extras', '0096_customfieldchoiceset'), ('extras', '0097_customfield_remove_choices'), - ('extras', '0098_webhook_custom_field_data_webhook_tags') + ('extras', '0098_webhook_custom_field_data_webhook_tags'), ] dependencies = [ @@ -39,7 +38,14 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('layout', models.JSONField(default=list)), ('config', models.JSONField(default=dict)), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dashboard', to=settings.AUTH_USER_MODEL)), + ( + 'user', + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name='dashboard', + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.AddField( @@ -64,8 +70,7 @@ class Migration(migrations.Migration): ), migrations.CreateModel( name='ReportModule', - fields=[ - ], + fields=[], options={ 'proxy': True, 'ordering': ('file_root', 'file_path'), @@ -76,8 +81,7 @@ class Migration(migrations.Migration): ), migrations.CreateModel( name='ScriptModule', - fields=[ - ], + fields=[], options={ 'proxy': True, 'ordering': ('file_root', 'file_path'), @@ -108,7 +112,10 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('created', models.DateTimeField(auto_now_add=True)), ('object_id', models.PositiveBigIntegerField()), - ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')), + ( + 'object_type', + models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype'), + ), ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), ], options={ @@ -117,7 +124,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='bookmark', - constraint=models.UniqueConstraint(fields=('object_type', 'object_id', 'user'), name='extras_bookmark_unique_per_object_and_user'), + constraint=models.UniqueConstraint( + fields=('object_type', 'object_id', 'user'), name='extras_bookmark_unique_per_object_and_user' + ), ), migrations.CreateModel( name='CustomFieldChoiceSet', @@ -128,7 +137,17 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=100, unique=True)), ('description', models.CharField(blank=True, max_length=200)), ('base_choices', models.CharField(blank=True, max_length=50)), - ('extra_choices', django.contrib.postgres.fields.ArrayField(base_field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=100), size=2), blank=True, null=True, size=None)), + ( + 'extra_choices', + django.contrib.postgres.fields.ArrayField( + base_field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), size=2 + ), + blank=True, + null=True, + size=None, + ), + ), ('order_alphabetically', models.BooleanField(default=False)), ], options={ @@ -138,7 +157,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='customfield', name='choice_set', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='choices_for', to='extras.customfieldchoiceset'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='choices_for', + to='extras.customfieldchoiceset', + ), ), migrations.RemoveField( model_name='customfield', diff --git a/netbox/extras/migrations/0099_cachedvalue_ordering.py b/netbox/extras/migrations/0099_cachedvalue_ordering.py index 242ffd983..36b91d59b 100644 --- a/netbox/extras/migrations/0099_cachedvalue_ordering.py +++ b/netbox/extras/migrations/0099_cachedvalue_ordering.py @@ -4,7 +4,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ ('extras', '0098_webhook_custom_field_data_webhook_tags'), ] diff --git a/netbox/extras/migrations/0100_customfield_ui_attrs.py b/netbox/extras/migrations/0100_customfield_ui_attrs.py index a4a713a86..b1a404d16 100644 --- a/netbox/extras/migrations/0100_customfield_ui_attrs.py +++ b/netbox/extras/migrations/0100_customfield_ui_attrs.py @@ -14,7 +14,6 @@ def update_ui_attrs(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('extras', '0099_cachedvalue_ordering'), ] @@ -30,10 +29,7 @@ class Migration(migrations.Migration): name='ui_visible', field=models.CharField(default='always', max_length=50), ), - migrations.RunPython( - code=update_ui_attrs, - reverse_code=migrations.RunPython.noop - ), + migrations.RunPython(code=update_ui_attrs, reverse_code=migrations.RunPython.noop), migrations.RemoveField( model_name='customfield', name='ui_visibility', diff --git a/netbox/extras/migrations/0101_eventrule.py b/netbox/extras/migrations/0101_eventrule.py index 3d236c847..605307c27 100644 --- a/netbox/extras/migrations/0101_eventrule.py +++ b/netbox/extras/migrations/0101_eventrule.py @@ -8,8 +8,8 @@ from extras.choices import * def move_webhooks(apps, schema_editor): - Webhook = apps.get_model("extras", "Webhook") - EventRule = apps.get_model("extras", "EventRule") + Webhook = apps.get_model('extras', 'Webhook') + EventRule = apps.get_model('extras', 'EventRule') webhook_ct = ContentType.objects.get_for_model(Webhook).pk for webhook in Webhook.objects.all(): @@ -39,7 +39,6 @@ class Migration(migrations.Migration): ] operations = [ - # Create the EventRule model migrations.CreateModel( name='EventRule', @@ -93,12 +92,12 @@ class Migration(migrations.Migration): ), migrations.AddIndex( model_name='eventrule', - index=models.Index(fields=['action_object_type', 'action_object_id'], name='extras_even_action__d9e2af_idx'), + index=models.Index( + fields=['action_object_type', 'action_object_id'], name='extras_even_action__d9e2af_idx' + ), ), - # Replicate Webhook data migrations.RunPython(move_webhooks), - # Remove obsolete fields from Webhook migrations.RemoveConstraint( model_name='webhook', @@ -136,7 +135,6 @@ class Migration(migrations.Migration): model_name='webhook', name='type_update', ), - # Add description field to Webhook migrations.AddField( model_name='webhook', diff --git a/netbox/extras/migrations/0102_move_configrevision.py b/netbox/extras/migrations/0102_move_configrevision.py index 36eef1205..64ff8c9ad 100644 --- a/netbox/extras/migrations/0102_move_configrevision.py +++ b/netbox/extras/migrations/0102_move_configrevision.py @@ -13,7 +13,6 @@ def update_content_type(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('extras', '0101_eventrule'), ] @@ -32,8 +31,5 @@ class Migration(migrations.Migration): ), ], ), - migrations.RunPython( - code=update_content_type, - reverse_code=migrations.RunPython.noop - ), + migrations.RunPython(code=update_content_type, reverse_code=migrations.RunPython.noop), ] diff --git a/netbox/extras/migrations/0103_gfk_indexes.py b/netbox/extras/migrations/0103_gfk_indexes.py index 2ccbdb2ff..f32b2e116 100644 --- a/netbox/extras/migrations/0103_gfk_indexes.py +++ b/netbox/extras/migrations/0103_gfk_indexes.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('extras', '0102_move_configrevision'), ] @@ -20,15 +19,21 @@ class Migration(migrations.Migration): ), migrations.AddIndex( model_name='journalentry', - index=models.Index(fields=['assigned_object_type', 'assigned_object_id'], name='extras_jour_assigne_76510f_idx'), + index=models.Index( + fields=['assigned_object_type', 'assigned_object_id'], name='extras_jour_assigne_76510f_idx' + ), ), migrations.AddIndex( model_name='objectchange', - index=models.Index(fields=['changed_object_type', 'changed_object_id'], name='extras_obje_changed_927fe5_idx'), + index=models.Index( + fields=['changed_object_type', 'changed_object_id'], name='extras_obje_changed_927fe5_idx' + ), ), migrations.AddIndex( model_name='objectchange', - index=models.Index(fields=['related_object_type', 'related_object_id'], name='extras_obje_related_bfcdef_idx'), + index=models.Index( + fields=['related_object_type', 'related_object_id'], name='extras_obje_related_bfcdef_idx' + ), ), migrations.AddIndex( model_name='stagedchange', diff --git a/netbox/extras/migrations/0105_customfield_min_max_values.py b/netbox/extras/migrations/0105_customfield_min_max_values.py index bcf3f97bd..71a0dcc68 100644 --- a/netbox/extras/migrations/0105_customfield_min_max_values.py +++ b/netbox/extras/migrations/0105_customfield_min_max_values.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('extras', '0104_stagedchange_remove_change_logging'), ] diff --git a/netbox/extras/migrations/0106_bookmark_user_cascade_deletion.py b/netbox/extras/migrations/0106_bookmark_user_cascade_deletion.py index d7bef2f0b..bc0e1bbd0 100644 --- a/netbox/extras/migrations/0106_bookmark_user_cascade_deletion.py +++ b/netbox/extras/migrations/0106_bookmark_user_cascade_deletion.py @@ -6,7 +6,6 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('extras', '0105_customfield_min_max_values'), diff --git a/netbox/extras/migrations/0107_cachedvalue_extras_cachedvalue_object.py b/netbox/extras/migrations/0107_cachedvalue_extras_cachedvalue_object.py index 15ce375a2..3f2907192 100644 --- a/netbox/extras/migrations/0107_cachedvalue_extras_cachedvalue_object.py +++ b/netbox/extras/migrations/0107_cachedvalue_extras_cachedvalue_object.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('extras', '0106_bookmark_user_cascade_deletion'), ] diff --git a/netbox/extras/migrations/0108_convert_reports_to_scripts.py b/netbox/extras/migrations/0108_convert_reports_to_scripts.py index b547c41c3..948bac754 100644 --- a/netbox/extras/migrations/0108_convert_reports_to_scripts.py +++ b/netbox/extras/migrations/0108_convert_reports_to_scripts.py @@ -12,16 +12,12 @@ def convert_reportmodule_jobs(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('extras', '0107_cachedvalue_extras_cachedvalue_object'), ] operations = [ - migrations.RunPython( - code=convert_reportmodule_jobs, - reverse_code=migrations.RunPython.noop - ), + migrations.RunPython(code=convert_reportmodule_jobs, reverse_code=migrations.RunPython.noop), migrations.DeleteModel( name='Report', ), diff --git a/netbox/extras/migrations/0109_script_model.py b/netbox/extras/migrations/0109_script_model.py index 6bfd2c14c..706a776af 100644 --- a/netbox/extras/migrations/0109_script_model.py +++ b/netbox/extras/migrations/0109_script_model.py @@ -30,7 +30,7 @@ def get_python_name(scriptmodule): """ Return the Python name of a ScriptModule's file on disk. """ - path, filename = os.path.split(scriptmodule.file_path) + filename = os.path.split(scriptmodule.file_path)[0] return os.path.splitext(filename)[0] @@ -55,9 +55,10 @@ def get_module_scripts(scriptmodule): """ Return a dictionary mapping of name and script class inside the passed ScriptModule. """ + def get_name(cls): # For child objects in submodules use the full import path w/o the root module as the name - return cls.full_name.split(".", maxsplit=1)[1] + return cls.full_name.split('.', maxsplit=1)[1] loader = SourceFileLoader(get_python_name(scriptmodule), get_full_path(scriptmodule)) try: @@ -100,17 +101,13 @@ def update_scripts(apps, schema_editor): ) # Update all Jobs associated with this ScriptModule & script name to point to the new Script object - Job.objects.filter( - object_type_id=scriptmodule_ct.id, - object_id=module.pk, - name=script_name - ).update(object_type_id=script_ct.id, object_id=script.pk) + Job.objects.filter(object_type_id=scriptmodule_ct.id, object_id=module.pk, name=script_name).update( + object_type_id=script_ct.id, object_id=script.pk + ) # Update all Jobs associated with this ScriptModule & script name to point to the new Script object - Job.objects.filter( - object_type_id=reportmodule_ct.id, - object_id=module.pk, - name=script_name - ).update(object_type_id=script_ct.id, object_id=script.pk) + Job.objects.filter(object_type_id=reportmodule_ct.id, object_id=module.pk, name=script_name).update( + object_type_id=script_ct.id, object_id=script.pk + ) def update_event_rules(apps, schema_editor): @@ -128,16 +125,13 @@ def update_event_rules(apps, schema_editor): for eventrule in EventRule.objects.filter(action_object_type=scriptmodule_ct): name = eventrule.action_parameters.get('script_name') - obj, created = Script.objects.get_or_create( - module_id=eventrule.action_object_id, - name=name, - defaults={'is_executable': False} + obj, __ = Script.objects.get_or_create( + module_id=eventrule.action_object_id, name=name, defaults={'is_executable': False} ) EventRule.objects.filter(pk=eventrule.pk).update(action_object_type=script_ct, action_object_id=obj.id) class Migration(migrations.Migration): - dependencies = [ ('extras', '0108_convert_reports_to_scripts'), ] @@ -148,8 +142,16 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('name', models.CharField(editable=False, max_length=79)), - ('module', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='scripts', to='extras.scriptmodule')), - ('is_executable', models.BooleanField(editable=False, default=True)) + ( + 'module', + models.ForeignKey( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name='scripts', + to='extras.scriptmodule', + ), + ), + ('is_executable', models.BooleanField(editable=False, default=True)), ], options={ 'ordering': ('module', 'name'), @@ -159,12 +161,6 @@ class Migration(migrations.Migration): model_name='script', constraint=models.UniqueConstraint(fields=('name', 'module'), name='extras_script_unique_name_module'), ), - migrations.RunPython( - code=update_scripts, - reverse_code=migrations.RunPython.noop - ), - migrations.RunPython( - code=update_event_rules, - reverse_code=migrations.RunPython.noop - ), + migrations.RunPython(code=update_scripts, reverse_code=migrations.RunPython.noop), + migrations.RunPython(code=update_event_rules, reverse_code=migrations.RunPython.noop), ] diff --git a/netbox/extras/migrations/0110_remove_eventrule_action_parameters.py b/netbox/extras/migrations/0110_remove_eventrule_action_parameters.py index b7373bdce..494107643 100644 --- a/netbox/extras/migrations/0110_remove_eventrule_action_parameters.py +++ b/netbox/extras/migrations/0110_remove_eventrule_action_parameters.py @@ -2,7 +2,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ ('extras', '0109_script_model'), ] diff --git a/netbox/extras/migrations/0111_rename_content_types.py b/netbox/extras/migrations/0111_rename_content_types.py index acd6aef0f..a9f80b146 100644 --- a/netbox/extras/migrations/0111_rename_content_types.py +++ b/netbox/extras/migrations/0111_rename_content_types.py @@ -3,7 +3,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('core', '0010_gfk_indexes'), ('extras', '0110_remove_eventrule_action_parameters'), @@ -24,16 +23,19 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='customfield', name='object_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='core.objecttype'), - ), - migrations.RunSQL( - "ALTER TABLE IF EXISTS extras_customfield_content_types_id_seq RENAME TO extras_customfield_object_types_id_seq" + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='core.objecttype' + ), ), + migrations.RunSQL(( + 'ALTER TABLE IF EXISTS extras_customfield_content_types_id_seq ' + 'RENAME TO extras_customfield_object_types_id_seq' + )), # Pre-v2.10 sequence name (see #15605) - migrations.RunSQL( - "ALTER TABLE IF EXISTS extras_customfield_obj_type_id_seq RENAME TO extras_customfield_object_types_id_seq" - ), - + migrations.RunSQL(( + 'ALTER TABLE IF EXISTS extras_customfield_obj_type_id_seq ' + 'RENAME TO extras_customfield_object_types_id_seq' + )), # Custom links migrations.RenameField( model_name='customlink', @@ -46,9 +48,8 @@ class Migration(migrations.Migration): field=models.ManyToManyField(related_name='custom_links', to='core.objecttype'), ), migrations.RunSQL( - "ALTER TABLE extras_customlink_content_types_id_seq RENAME TO extras_customlink_object_types_id_seq" + 'ALTER TABLE extras_customlink_content_types_id_seq RENAME TO extras_customlink_object_types_id_seq' ), - # Event rules migrations.RenameField( model_name='eventrule', @@ -61,9 +62,8 @@ class Migration(migrations.Migration): field=models.ManyToManyField(related_name='event_rules', to='core.objecttype'), ), migrations.RunSQL( - "ALTER TABLE extras_eventrule_content_types_id_seq RENAME TO extras_eventrule_object_types_id_seq" + 'ALTER TABLE extras_eventrule_content_types_id_seq RENAME TO extras_eventrule_object_types_id_seq' ), - # Export templates migrations.RenameField( model_name='exporttemplate', @@ -76,9 +76,8 @@ class Migration(migrations.Migration): field=models.ManyToManyField(related_name='export_templates', to='core.objecttype'), ), migrations.RunSQL( - "ALTER TABLE extras_exporttemplate_content_types_id_seq RENAME TO extras_exporttemplate_object_types_id_seq" + 'ALTER TABLE extras_exporttemplate_content_types_id_seq RENAME TO extras_exporttemplate_object_types_id_seq' ), - # Saved filters migrations.RenameField( model_name='savedfilter', @@ -91,9 +90,8 @@ class Migration(migrations.Migration): field=models.ManyToManyField(related_name='saved_filters', to='core.objecttype'), ), migrations.RunSQL( - "ALTER TABLE extras_savedfilter_content_types_id_seq RENAME TO extras_savedfilter_object_types_id_seq" + 'ALTER TABLE extras_savedfilter_content_types_id_seq RENAME TO extras_savedfilter_object_types_id_seq' ), - # Image attachments migrations.RemoveIndex( model_name='imageattachment', diff --git a/netbox/extras/migrations/0112_tag_update_object_types.py b/netbox/extras/migrations/0112_tag_update_object_types.py index 87ec117a4..e863ba8c3 100644 --- a/netbox/extras/migrations/0112_tag_update_object_types.py +++ b/netbox/extras/migrations/0112_tag_update_object_types.py @@ -2,7 +2,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('core', '0010_gfk_indexes'), ('extras', '0111_rename_content_types'), diff --git a/netbox/extras/migrations/0113_customfield_rename_object_type.py b/netbox/extras/migrations/0113_customfield_rename_object_type.py index 73c4a2a61..9ad9fbbc4 100644 --- a/netbox/extras/migrations/0113_customfield_rename_object_type.py +++ b/netbox/extras/migrations/0113_customfield_rename_object_type.py @@ -2,7 +2,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ ('extras', '0112_tag_update_object_types'), ] diff --git a/netbox/extras/migrations/0114_customfield_add_comments.py b/netbox/extras/migrations/0114_customfield_add_comments.py index cd85db1ba..ad9e3d46f 100644 --- a/netbox/extras/migrations/0114_customfield_add_comments.py +++ b/netbox/extras/migrations/0114_customfield_add_comments.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('extras', '0113_customfield_rename_object_type'), ] diff --git a/netbox/extras/migrations/0115_convert_dashboard_widgets.py b/netbox/extras/migrations/0115_convert_dashboard_widgets.py index c85c83ecf..28f6eade9 100644 --- a/netbox/extras/migrations/0115_convert_dashboard_widgets.py +++ b/netbox/extras/migrations/0115_convert_dashboard_widgets.py @@ -16,14 +16,10 @@ def update_dashboard_widgets(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('extras', '0114_customfield_add_comments'), ] operations = [ - migrations.RunPython( - code=update_dashboard_widgets, - reverse_code=migrations.RunPython.noop - ), + migrations.RunPython(code=update_dashboard_widgets, reverse_code=migrations.RunPython.noop), ] diff --git a/netbox/extras/migrations/0116_custom_link_button_color.py b/netbox/extras/migrations/0116_custom_link_button_color.py index 665d73017..ff47eab11 100644 --- a/netbox/extras/migrations/0116_custom_link_button_color.py +++ b/netbox/extras/migrations/0116_custom_link_button_color.py @@ -7,7 +7,6 @@ def update_link_buttons(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('extras', '0115_convert_dashboard_widgets'), ] @@ -18,8 +17,5 @@ class Migration(migrations.Migration): name='button_class', field=models.CharField(default='default', max_length=30), ), - migrations.RunPython( - code=update_link_buttons, - reverse_code=migrations.RunPython.noop - ), + migrations.RunPython(code=update_link_buttons, reverse_code=migrations.RunPython.noop), ] diff --git a/netbox/extras/migrations/0117_move_objectchange.py b/netbox/extras/migrations/0117_move_objectchange.py index a69b5a711..62c7255e7 100644 --- a/netbox/extras/migrations/0117_move_objectchange.py +++ b/netbox/extras/migrations/0117_move_objectchange.py @@ -26,7 +26,6 @@ def update_dashboard_widgets(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('extras', '0116_custom_link_button_color'), ('core', '0011_move_objectchange'), @@ -44,81 +43,64 @@ class Migration(migrations.Migration): name='ObjectChange', table='core_objectchange', ), - # Rename PK sequence - migrations.RunSQL( - "ALTER TABLE extras_objectchange_id_seq" - " RENAME TO core_objectchange_id_seq" - ), - + migrations.RunSQL('ALTER TABLE extras_objectchange_id_seq' ' RENAME TO core_objectchange_id_seq'), # Rename indexes. Hashes generated by schema_editor._create_index_name() + migrations.RunSQL('ALTER INDEX extras_objectchange_pkey' ' RENAME TO core_objectchange_pkey'), migrations.RunSQL( - "ALTER INDEX extras_objectchange_pkey" - " RENAME TO core_objectchange_pkey" + 'ALTER INDEX extras_obje_changed_927fe5_idx' + ' RENAME TO core_objectchange_changed_object_type_id_cha_79a9ed1e' ), migrations.RunSQL( - "ALTER INDEX extras_obje_changed_927fe5_idx" - " RENAME TO core_objectchange_changed_object_type_id_cha_79a9ed1e" + 'ALTER INDEX extras_obje_related_bfcdef_idx' + ' RENAME TO core_objectchange_related_object_type_id_rel_a71d604a' ), migrations.RunSQL( - "ALTER INDEX extras_obje_related_bfcdef_idx" - " RENAME TO core_objectchange_related_object_type_id_rel_a71d604a" + 'ALTER INDEX extras_objectchange_changed_object_type_id_b755bb60' + ' RENAME TO core_objectchange_changed_object_type_id_2070ade6' ), migrations.RunSQL( - "ALTER INDEX extras_objectchange_changed_object_type_id_b755bb60" - " RENAME TO core_objectchange_changed_object_type_id_2070ade6" + 'ALTER INDEX extras_objectchange_related_object_type_id_fe6e521f' + ' RENAME TO core_objectchange_related_object_type_id_b80958af' ), migrations.RunSQL( - "ALTER INDEX extras_objectchange_related_object_type_id_fe6e521f" - " RENAME TO core_objectchange_related_object_type_id_b80958af" + 'ALTER INDEX extras_objectchange_request_id_4ae21e90' + ' RENAME TO core_objectchange_request_id_d9d160ac' ), migrations.RunSQL( - "ALTER INDEX extras_objectchange_request_id_4ae21e90" - " RENAME TO core_objectchange_request_id_d9d160ac" + 'ALTER INDEX extras_objectchange_time_224380ea' ' RENAME TO core_objectchange_time_800f60a5' ), migrations.RunSQL( - "ALTER INDEX extras_objectchange_time_224380ea" - " RENAME TO core_objectchange_time_800f60a5" + 'ALTER INDEX extras_objectchange_user_id_7fdf8186' ' RENAME TO core_objectchange_user_id_2b2142be' ), - migrations.RunSQL( - "ALTER INDEX extras_objectchange_user_id_7fdf8186" - " RENAME TO core_objectchange_user_id_2b2142be" - ), - # Rename constraints migrations.RunSQL( - "ALTER TABLE core_objectchange RENAME CONSTRAINT " - "extras_objectchange_changed_object_id_check TO " - "core_objectchange_changed_object_id_check" + 'ALTER TABLE core_objectchange RENAME CONSTRAINT ' + 'extras_objectchange_changed_object_id_check TO ' + 'core_objectchange_changed_object_id_check' ), migrations.RunSQL( - "ALTER TABLE core_objectchange RENAME CONSTRAINT " - "extras_objectchange_related_object_id_check TO " - "core_objectchange_related_object_id_check" + 'ALTER TABLE core_objectchange RENAME CONSTRAINT ' + 'extras_objectchange_related_object_id_check TO ' + 'core_objectchange_related_object_id_check' ), migrations.RunSQL( - "ALTER TABLE core_objectchange RENAME CONSTRAINT " - "extras_objectchange_changed_object_type__b755bb60_fk_django_co TO " - "core_objectchange_changed_object_type_id_2070ade6" + 'ALTER TABLE core_objectchange RENAME CONSTRAINT ' + 'extras_objectchange_changed_object_type__b755bb60_fk_django_co TO ' + 'core_objectchange_changed_object_type_id_2070ade6' ), migrations.RunSQL( - "ALTER TABLE core_objectchange RENAME CONSTRAINT " - "extras_objectchange_related_object_type__fe6e521f_fk_django_co TO " - "core_objectchange_related_object_type_id_b80958af" + 'ALTER TABLE core_objectchange RENAME CONSTRAINT ' + 'extras_objectchange_related_object_type__fe6e521f_fk_django_co TO ' + 'core_objectchange_related_object_type_id_b80958af' ), migrations.RunSQL( - "ALTER TABLE core_objectchange RENAME CONSTRAINT " - "extras_objectchange_user_id_7fdf8186_fk_auth_user_id TO " - "core_objectchange_user_id_2b2142be" + 'ALTER TABLE core_objectchange RENAME CONSTRAINT ' + 'extras_objectchange_user_id_7fdf8186_fk_auth_user_id TO ' + 'core_objectchange_user_id_2b2142be' ), ], ), - migrations.RunPython( - code=update_content_types, - reverse_code=migrations.RunPython.noop - ), - migrations.RunPython( - code=update_dashboard_widgets, - reverse_code=migrations.RunPython.noop - ), + migrations.RunPython(code=update_content_types, reverse_code=migrations.RunPython.noop), + migrations.RunPython(code=update_dashboard_widgets, reverse_code=migrations.RunPython.noop), ] diff --git a/netbox/extras/migrations/0118_customfield_uniqueness.py b/netbox/extras/migrations/0118_customfield_uniqueness.py index b7693aa24..7571e975a 100644 --- a/netbox/extras/migrations/0118_customfield_uniqueness.py +++ b/netbox/extras/migrations/0118_customfield_uniqueness.py @@ -2,7 +2,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('extras', '0117_move_objectchange'), ] diff --git a/netbox/extras/migrations/0119_notifications.py b/netbox/extras/migrations/0119_notifications.py index c266f3b6c..2e6aefd20 100644 --- a/netbox/extras/migrations/0119_notifications.py +++ b/netbox/extras/migrations/0119_notifications.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('contenttypes', '0002_remove_content_type_name'), ('extras', '0118_customfield_uniqueness'), @@ -22,7 +21,10 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=100, unique=True)), ('description', models.CharField(blank=True, max_length=200)), ('groups', models.ManyToManyField(blank=True, related_name='notification_groups', to='users.group')), - ('users', models.ManyToManyField(blank=True, related_name='notification_groups', to=settings.AUTH_USER_MODEL)), + ( + 'users', + models.ManyToManyField(blank=True, related_name='notification_groups', to=settings.AUTH_USER_MODEL), + ), ], options={ 'verbose_name': 'notification group', @@ -36,8 +38,18 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('created', models.DateTimeField(auto_now_add=True)), ('object_id', models.PositiveBigIntegerField()), - ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subscriptions', to=settings.AUTH_USER_MODEL)), + ( + 'object_type', + models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype'), + ), + ( + 'user', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='subscriptions', + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ 'verbose_name': 'subscription', @@ -53,9 +65,19 @@ class Migration(migrations.Migration): ('read', models.DateTimeField(blank=True, null=True)), ('object_id', models.PositiveBigIntegerField()), ('event_type', models.CharField(max_length=50)), - ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')), + ( + 'object_type', + models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype'), + ), ('object_repr', models.CharField(editable=False, max_length=200)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL)), + ( + 'user', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='notifications', + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ 'verbose_name': 'notification', @@ -66,7 +88,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='notification', - constraint=models.UniqueConstraint(fields=('object_type', 'object_id', 'user'), name='extras_notification_unique_per_object_and_user'), + constraint=models.UniqueConstraint( + fields=('object_type', 'object_id', 'user'), name='extras_notification_unique_per_object_and_user' + ), ), migrations.AddIndex( model_name='subscription', @@ -74,6 +98,8 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='subscription', - constraint=models.UniqueConstraint(fields=('object_type', 'object_id', 'user'), name='extras_subscription_unique_per_object_and_user'), + constraint=models.UniqueConstraint( + fields=('object_type', 'object_id', 'user'), name='extras_subscription_unique_per_object_and_user' + ), ), ] diff --git a/netbox/extras/migrations/0120_eventrule_event_types.py b/netbox/extras/migrations/0120_eventrule_event_types.py index f62c83e4c..2bcc0a4e6 100644 --- a/netbox/extras/migrations/0120_eventrule_event_types.py +++ b/netbox/extras/migrations/0120_eventrule_event_types.py @@ -26,7 +26,6 @@ def set_event_types(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('extras', '0119_notifications'), ] @@ -36,16 +35,10 @@ class Migration(migrations.Migration): model_name='eventrule', name='event_types', field=django.contrib.postgres.fields.ArrayField( - base_field=models.CharField(max_length=50), - blank=True, - null=True, - size=None + base_field=models.CharField(max_length=50), blank=True, null=True, size=None ), ), - migrations.RunPython( - code=set_event_types, - reverse_code=migrations.RunPython.noop - ), + migrations.RunPython(code=set_event_types, reverse_code=migrations.RunPython.noop), migrations.AlterField( model_name='eventrule', name='event_types', diff --git a/netbox/extras/migrations/0121_customfield_related_object_filter.py b/netbox/extras/migrations/0121_customfield_related_object_filter.py index d6e41fd7d..10eecd6cc 100644 --- a/netbox/extras/migrations/0121_customfield_related_object_filter.py +++ b/netbox/extras/migrations/0121_customfield_related_object_filter.py @@ -2,7 +2,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('extras', '0120_eventrule_event_types'), ] diff --git a/netbox/extras/migrations/0122_charfield_null_choices.py b/netbox/extras/migrations/0122_charfield_null_choices.py new file mode 100644 index 000000000..a32051cb1 --- /dev/null +++ b/netbox/extras/migrations/0122_charfield_null_choices.py @@ -0,0 +1,25 @@ +from django.db import migrations, models + + +def set_null_values(apps, schema_editor): + """ + Replace empty strings with null values. + """ + CustomFieldChoiceSet = apps.get_model('extras', 'CustomFieldChoiceSet') + + CustomFieldChoiceSet.objects.filter(base_choices='').update(base_choices=None) + + +class Migration(migrations.Migration): + dependencies = [ + ('extras', '0121_customfield_related_object_filter'), + ] + + operations = [ + migrations.AlterField( + model_name='customfieldchoiceset', + name='base_choices', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.RunPython(code=set_null_values, reverse_code=migrations.RunPython.noop), + ] diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 8b7fc0cb6..e1ceaf7a6 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -760,6 +760,7 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel max_length=50, choices=CustomFieldChoiceSetBaseChoices, blank=True, + null=True, help_text=_('Base set of predefined choices (optional)') ) extra_choices = ArrayField( diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index d8a274c89..d3e443b14 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -704,7 +704,10 @@ class JournalEntry(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ExportTemplat def __str__(self): created = timezone.localtime(self.created) - return f"{created.date().isoformat()} {created.time().isoformat(timespec='minutes')} ({self.get_kind_display()})" + return ( + f"{created.date().isoformat()} {created.time().isoformat(timespec='minutes')} " + f"({self.get_kind_display()})" + ) def get_absolute_url(self): return reverse('extras:journalentry', args=[self.pk]) diff --git a/netbox/extras/tests/test_custom_validation.py b/netbox/extras/tests/test_custom_validation.py index 652bc241b..6eb90e5b0 100644 --- a/netbox/extras/tests/test_custom_validation.py +++ b/netbox/extras/tests/test_custom_validation.py @@ -191,7 +191,7 @@ class BulkImportCustomValidationTest(ModelViewTestCase): # Attempt to import providers without tags request = { - 'path': self._get_url('import'), + 'path': self._get_url('bulk_import'), 'data': post_data(data), } response = self.client.post(**request) @@ -207,7 +207,7 @@ class BulkImportCustomValidationTest(ModelViewTestCase): ) data['data'] = '\n'.join(csv_data) request = { - 'path': self._get_url('import'), + 'path': self._get_url('bulk_import'), 'data': post_data(data), } response = self.client.post(**request) diff --git a/netbox/extras/tests/test_customfields.py b/netbox/extras/tests/test_customfields.py index 2bc9b5acc..d36477da8 100644 --- a/netbox/extras/tests/test_customfields.py +++ b/netbox/extras/tests/test_customfields.py @@ -637,15 +637,51 @@ class CustomFieldAPITest(APITestCase): ) custom_fields = ( - CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='text_field', default='foo'), - CustomField(type=CustomFieldTypeChoices.TYPE_LONGTEXT, name='longtext_field', default='ABC'), - CustomField(type=CustomFieldTypeChoices.TYPE_INTEGER, name='integer_field', default=123), - CustomField(type=CustomFieldTypeChoices.TYPE_DECIMAL, name='decimal_field', default=123.45), - CustomField(type=CustomFieldTypeChoices.TYPE_BOOLEAN, name='boolean_field', default=False), - CustomField(type=CustomFieldTypeChoices.TYPE_DATE, name='date_field', default='2020-01-01'), - CustomField(type=CustomFieldTypeChoices.TYPE_DATETIME, name='datetime_field', default='2020-01-01T01:23:45'), - CustomField(type=CustomFieldTypeChoices.TYPE_URL, name='url_field', default='http://example.com/1'), - CustomField(type=CustomFieldTypeChoices.TYPE_JSON, name='json_field', default='{"x": "y"}'), + CustomField( + type=CustomFieldTypeChoices.TYPE_TEXT, + name='text_field', + default='foo' + ), + CustomField( + type=CustomFieldTypeChoices.TYPE_LONGTEXT, + name='longtext_field', + default='ABC' + ), + CustomField( + type=CustomFieldTypeChoices.TYPE_INTEGER, + name='integer_field', + default=123 + ), + CustomField( + type=CustomFieldTypeChoices.TYPE_DECIMAL, + name='decimal_field', + default=123.45 + ), + CustomField( + type=CustomFieldTypeChoices.TYPE_BOOLEAN, + name='boolean_field', + default=False) + , + CustomField( + type=CustomFieldTypeChoices.TYPE_DATE, + name='date_field', + default='2020-01-01' + ), + CustomField( + type=CustomFieldTypeChoices.TYPE_DATETIME, + name='datetime_field', + default='2020-01-01T01:23:45' + ), + CustomField( + type=CustomFieldTypeChoices.TYPE_URL, + name='url_field', + default='http://example.com/1' + ), + CustomField( + type=CustomFieldTypeChoices.TYPE_JSON, + name='json_field', + default='{"x": "y"}' + ), CustomField( type=CustomFieldTypeChoices.TYPE_SELECT, name='select_field', @@ -656,7 +692,7 @@ class CustomFieldAPITest(APITestCase): type=CustomFieldTypeChoices.TYPE_MULTISELECT, name='multiselect_field', default=['foo'], - choice_set=choice_set + choice_set=choice_set, ), CustomField( type=CustomFieldTypeChoices.TYPE_OBJECT, @@ -1273,14 +1309,23 @@ class CustomFieldImportTest(TestCase): Import a Site in CSV format, including a value for each CustomField. """ data = ( - ('name', 'slug', 'status', 'cf_text', 'cf_longtext', 'cf_integer', 'cf_decimal', 'cf_boolean', 'cf_date', 'cf_datetime', 'cf_url', 'cf_json', 'cf_select', 'cf_multiselect'), - ('Site 1', 'site-1', 'active', 'ABC', 'Foo', '123', '123.45', 'True', '2020-01-01', '2020-01-01 12:00:00', 'http://example.com/1', '{"foo": 123}', 'a', '"a,b"'), - ('Site 2', 'site-2', 'active', 'DEF', 'Bar', '456', '456.78', 'False', '2020-01-02', '2020-01-02 12:00:00', 'http://example.com/2', '{"bar": 456}', 'b', '"b,c"'), + ( + 'name', 'slug', 'status', 'cf_text', 'cf_longtext', 'cf_integer', 'cf_decimal', 'cf_boolean', 'cf_date', + 'cf_datetime', 'cf_url', 'cf_json', 'cf_select', 'cf_multiselect', + ), + ( + 'Site 1', 'site-1', 'active', 'ABC', 'Foo', '123', '123.45', 'True', '2020-01-01', + '2020-01-01 12:00:00', 'http://example.com/1', '{"foo": 123}', 'a', '"a,b"', + ), + ( + 'Site 2', 'site-2', 'active', 'DEF', 'Bar', '456', '456.78', 'False', '2020-01-02', + '2020-01-02 12:00:00', 'http://example.com/2', '{"bar": 456}', 'b', '"b,c"', + ), ('Site 3', 'site-3', 'active', '', '', '', '', '', '', '', '', '', '', ''), ) csv_data = '\n'.join(','.join(row) for row in data) - response = self.client.post(reverse('dcim:site_import'), { + response = self.client.post(reverse('dcim:site_bulk_import'), { 'data': csv_data, 'format': ImportFormatChoices.CSV, 'csv_delimiter': CSVDelimiterChoices.AUTO, @@ -1616,7 +1661,10 @@ class CustomFieldModelFilterTest(TestCase): self.assertEqual(self.filterset({'cf_cf6__lte': ['2016-06-27']}, self.queryset).qs.count(), 2) def test_filter_url_strict(self): - self.assertEqual(self.filterset({'cf_cf7': ['http://a.example.com', 'http://b.example.com']}, self.queryset).qs.count(), 2) + self.assertEqual( + self.filterset({'cf_cf7': ['http://a.example.com', 'http://b.example.com']}, self.queryset).qs.count(), + 2 + ) self.assertEqual(self.filterset({'cf_cf7__n': ['http://b.example.com']}, self.queryset).qs.count(), 2) self.assertEqual(self.filterset({'cf_cf7__ic': ['b']}, self.queryset).qs.count(), 1) self.assertEqual(self.filterset({'cf_cf7__nic': ['b']}, self.queryset).qs.count(), 2) @@ -1640,9 +1688,18 @@ class CustomFieldModelFilterTest(TestCase): def test_filter_object(self): manufacturer_ids = Manufacturer.objects.values_list('id', flat=True) - self.assertEqual(self.filterset({'cf_cf11': [manufacturer_ids[0], manufacturer_ids[1]]}, self.queryset).qs.count(), 2) + self.assertEqual( + self.filterset({'cf_cf11': [manufacturer_ids[0], manufacturer_ids[1]]}, self.queryset).qs.count(), + 2 + ) def test_filter_multiobject(self): manufacturer_ids = Manufacturer.objects.values_list('id', flat=True) - self.assertEqual(self.filterset({'cf_cf12': [manufacturer_ids[0], manufacturer_ids[1]]}, self.queryset).qs.count(), 2) - self.assertEqual(self.filterset({'cf_cf12': [manufacturer_ids[3]]}, self.queryset).qs.count(), 3) + self.assertEqual( + self.filterset({'cf_cf12': [manufacturer_ids[0], manufacturer_ids[1]]}, self.queryset).qs.count(), + 2 + ) + self.assertEqual( + self.filterset({'cf_cf12': [manufacturer_ids[3]]}, self.queryset).qs.count(), + 3 + ) diff --git a/netbox/extras/tests/test_filtersets.py b/netbox/extras/tests/test_filtersets.py index 9048d5fd9..c94e36e4b 100644 --- a/netbox/extras/tests/test_filtersets.py +++ b/netbox/extras/tests/test_filtersets.py @@ -1135,6 +1135,7 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests): 'l2vpn', 'l2vpntermination', 'location', + 'macaddress', 'manufacturer', 'module', 'modulebay', @@ -1167,11 +1168,15 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests): 'tunnelgroup', 'tunneltermination', 'virtualchassis', + 'virtualcircuit', + 'virtualcircuittermination', 'virtualdevicecontext', 'virtualdisk', 'virtualmachine', 'vlan', 'vlangroup', + 'vlantranslationpolicy', + 'vlantranslationrule', 'vminterface', 'vrf', 'webhook', diff --git a/netbox/extras/tests/test_models.py b/netbox/extras/tests/test_models.py index 188a06a3f..c90390dd1 100644 --- a/netbox/extras/tests/test_models.py +++ b/netbox/extras/tests/test_models.py @@ -274,7 +274,7 @@ class ConfigContextTest(TestCase): name="Cluster", group=cluster_group, type=cluster_type, - site=site, + scope=site, ) region_context = ConfigContext.objects.create( @@ -366,7 +366,7 @@ class ConfigContextTest(TestCase): """ site = Site.objects.first() cluster_type = ClusterType.objects.create(name="Cluster Type") - cluster = Cluster.objects.create(name="Cluster", type=cluster_type, site=site) + cluster = Cluster.objects.create(name="Cluster", type=cluster_type, scope=site) vm_role = DeviceRole.objects.first() # Create a ConfigContext associated with the site diff --git a/netbox/extras/urls.py b/netbox/extras/urls.py index b13af1db9..32633493f 100644 --- a/netbox/extras/urls.py +++ b/netbox/extras/urls.py @@ -7,128 +7,68 @@ from utilities.urls import get_model_urls app_name = 'extras' urlpatterns = [ - # Custom fields - path('custom-fields/', views.CustomFieldListView.as_view(), name='customfield_list'), - path('custom-fields/add/', views.CustomFieldEditView.as_view(), name='customfield_add'), - path('custom-fields/import/', views.CustomFieldBulkImportView.as_view(), name='customfield_import'), - path('custom-fields/edit/', views.CustomFieldBulkEditView.as_view(), name='customfield_bulk_edit'), - path('custom-fields/delete/', views.CustomFieldBulkDeleteView.as_view(), name='customfield_bulk_delete'), + path('custom-fields/', include(get_model_urls('extras', 'customfield', detail=False))), path('custom-fields//', include(get_model_urls('extras', 'customfield'))), - # Custom field choices - path('custom-field-choices/', views.CustomFieldChoiceSetListView.as_view(), name='customfieldchoiceset_list'), - path('custom-field-choices/add/', views.CustomFieldChoiceSetEditView.as_view(), name='customfieldchoiceset_add'), - path('custom-field-choices/import/', views.CustomFieldChoiceSetBulkImportView.as_view(), name='customfieldchoiceset_import'), - path('custom-field-choices/edit/', views.CustomFieldChoiceSetBulkEditView.as_view(), name='customfieldchoiceset_bulk_edit'), - path('custom-field-choices/delete/', views.CustomFieldChoiceSetBulkDeleteView.as_view(), name='customfieldchoiceset_bulk_delete'), + path('custom-field-choices/', include(get_model_urls('extras', 'customfieldchoiceset', detail=False))), path('custom-field-choices//', include(get_model_urls('extras', 'customfieldchoiceset'))), - # Custom links - path('custom-links/', views.CustomLinkListView.as_view(), name='customlink_list'), - path('custom-links/add/', views.CustomLinkEditView.as_view(), name='customlink_add'), - path('custom-links/import/', views.CustomLinkBulkImportView.as_view(), name='customlink_import'), - path('custom-links/edit/', views.CustomLinkBulkEditView.as_view(), name='customlink_bulk_edit'), - path('custom-links/delete/', views.CustomLinkBulkDeleteView.as_view(), name='customlink_bulk_delete'), + path('custom-links/', include(get_model_urls('extras', 'customlink', detail=False))), path('custom-links//', include(get_model_urls('extras', 'customlink'))), - # Export templates - path('export-templates/', views.ExportTemplateListView.as_view(), name='exporttemplate_list'), - path('export-templates/add/', views.ExportTemplateEditView.as_view(), name='exporttemplate_add'), - path('export-templates/import/', views.ExportTemplateBulkImportView.as_view(), name='exporttemplate_import'), - path('export-templates/edit/', views.ExportTemplateBulkEditView.as_view(), name='exporttemplate_bulk_edit'), - path('export-templates/delete/', views.ExportTemplateBulkDeleteView.as_view(), name='exporttemplate_bulk_delete'), - path('export-templates/sync/', views.ExportTemplateBulkSyncDataView.as_view(), name='exporttemplate_bulk_sync'), + path('export-templates/', include(get_model_urls('extras', 'exporttemplate', detail=False))), path('export-templates//', include(get_model_urls('extras', 'exporttemplate'))), - # Saved filters - path('saved-filters/', views.SavedFilterListView.as_view(), name='savedfilter_list'), - path('saved-filters/add/', views.SavedFilterEditView.as_view(), name='savedfilter_add'), - path('saved-filters/import/', views.SavedFilterBulkImportView.as_view(), name='savedfilter_import'), - path('saved-filters/edit/', views.SavedFilterBulkEditView.as_view(), name='savedfilter_bulk_edit'), - path('saved-filters/delete/', views.SavedFilterBulkDeleteView.as_view(), name='savedfilter_bulk_delete'), + path('saved-filters/', include(get_model_urls('extras', 'savedfilter', detail=False))), path('saved-filters//', include(get_model_urls('extras', 'savedfilter'))), - # Bookmarks - path('bookmarks/add/', views.BookmarkCreateView.as_view(), name='bookmark_add'), - path('bookmarks/delete/', views.BookmarkBulkDeleteView.as_view(), name='bookmark_bulk_delete'), + path('bookmarks/', include(get_model_urls('extras', 'bookmark', detail=False))), path('bookmarks//', include(get_model_urls('extras', 'bookmark'))), - # Notification groups - path('notification-groups/', views.NotificationGroupListView.as_view(), name='notificationgroup_list'), - path('notification-groups/add/', views.NotificationGroupEditView.as_view(), name='notificationgroup_add'), - path('notification-groups/import/', views.NotificationGroupBulkImportView.as_view(), name='notificationgroup_import'), - path('notification-groups/edit/', views.NotificationGroupBulkEditView.as_view(), name='notificationgroup_bulk_edit'), - path('notification-groups/delete/', views.NotificationGroupBulkDeleteView.as_view(), name='notificationgroup_bulk_delete'), + path('notification-groups/', include(get_model_urls('extras', 'notificationgroup', detail=False))), path('notification-groups//', include(get_model_urls('extras', 'notificationgroup'))), - # Notifications path('notifications/', views.NotificationsView.as_view(), name='notifications'), - path('notifications/delete/', views.NotificationBulkDeleteView.as_view(), name='notification_bulk_delete'), + path('notifications/', include(get_model_urls('extras', 'notification', detail=False))), path('notifications//', include(get_model_urls('extras', 'notification'))), - # Subscriptions - path('subscriptions/add/', views.SubscriptionCreateView.as_view(), name='subscription_add'), - path('subscriptions/delete/', views.SubscriptionBulkDeleteView.as_view(), name='subscription_bulk_delete'), + path('subscriptions/', include(get_model_urls('extras', 'subscription', detail=False))), path('subscriptions//', include(get_model_urls('extras', 'subscription'))), - # Webhooks - path('webhooks/', views.WebhookListView.as_view(), name='webhook_list'), - path('webhooks/add/', views.WebhookEditView.as_view(), name='webhook_add'), - path('webhooks/import/', views.WebhookBulkImportView.as_view(), name='webhook_import'), - path('webhooks/edit/', views.WebhookBulkEditView.as_view(), name='webhook_bulk_edit'), - path('webhooks/delete/', views.WebhookBulkDeleteView.as_view(), name='webhook_bulk_delete'), + path('webhooks/', include(get_model_urls('extras', 'webhook', detail=False))), path('webhooks//', include(get_model_urls('extras', 'webhook'))), - # Event rules - path('event-rules/', views.EventRuleListView.as_view(), name='eventrule_list'), - path('event-rules/add/', views.EventRuleEditView.as_view(), name='eventrule_add'), - path('event-rules/import/', views.EventRuleBulkImportView.as_view(), name='eventrule_import'), - path('event-rules/edit/', views.EventRuleBulkEditView.as_view(), name='eventrule_bulk_edit'), - path('event-rules/delete/', views.EventRuleBulkDeleteView.as_view(), name='eventrule_bulk_delete'), + path('event-rules/', include(get_model_urls('extras', 'eventrule', detail=False))), path('event-rules//', include(get_model_urls('extras', 'eventrule'))), - # Tags - path('tags/', views.TagListView.as_view(), name='tag_list'), - path('tags/add/', views.TagEditView.as_view(), name='tag_add'), - path('tags/import/', views.TagBulkImportView.as_view(), name='tag_import'), - path('tags/edit/', views.TagBulkEditView.as_view(), name='tag_bulk_edit'), - path('tags/delete/', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'), + path('tags/', include(get_model_urls('extras', 'tag', detail=False))), path('tags//', include(get_model_urls('extras', 'tag'))), - # Config contexts - path('config-contexts/', views.ConfigContextListView.as_view(), name='configcontext_list'), - path('config-contexts/add/', views.ConfigContextEditView.as_view(), name='configcontext_add'), - path('config-contexts/edit/', views.ConfigContextBulkEditView.as_view(), name='configcontext_bulk_edit'), - path('config-contexts/delete/', views.ConfigContextBulkDeleteView.as_view(), name='configcontext_bulk_delete'), - path('config-contexts/sync/', views.ConfigContextBulkSyncDataView.as_view(), name='configcontext_bulk_sync'), + path('config-contexts/', include(get_model_urls('extras', 'configcontext', detail=False))), path('config-contexts//', include(get_model_urls('extras', 'configcontext'))), - # Config templates - path('config-templates/', views.ConfigTemplateListView.as_view(), name='configtemplate_list'), - path('config-templates/add/', views.ConfigTemplateEditView.as_view(), name='configtemplate_add'), - path('config-templates/edit/', views.ConfigTemplateBulkEditView.as_view(), name='configtemplate_bulk_edit'), - path('config-templates/delete/', views.ConfigTemplateBulkDeleteView.as_view(), name='configtemplate_bulk_delete'), - path('config-templates/sync/', views.ConfigTemplateBulkSyncDataView.as_view(), name='configtemplate_bulk_sync'), + path('config-templates/', include(get_model_urls('extras', 'configtemplate', detail=False))), path('config-templates//', include(get_model_urls('extras', 'configtemplate'))), - # Image attachments - path('image-attachments/', views.ImageAttachmentListView.as_view(), name='imageattachment_list'), - path('image-attachments/add/', views.ImageAttachmentEditView.as_view(), name='imageattachment_add'), + path('image-attachments/', include(get_model_urls('extras', 'imageattachment', detail=False))), path('image-attachments//', include(get_model_urls('extras', 'imageattachment'))), - # Journal entries - path('journal-entries/', views.JournalEntryListView.as_view(), name='journalentry_list'), - path('journal-entries/add/', views.JournalEntryEditView.as_view(), name='journalentry_add'), - path('journal-entries/edit/', views.JournalEntryBulkEditView.as_view(), name='journalentry_bulk_edit'), - path('journal-entries/delete/', views.JournalEntryBulkDeleteView.as_view(), name='journalentry_bulk_delete'), - path('journal-entries/import/', views.JournalEntryBulkImportView.as_view(), name='journalentry_import'), + path('journal-entries/', include(get_model_urls('extras', 'journalentry', detail=False))), path('journal-entries//', include(get_model_urls('extras', 'journalentry'))), # User dashboard path('dashboard/reset/', views.DashboardResetView.as_view(), name='dashboard_reset'), path('dashboard/widgets/add/', views.DashboardWidgetAddView.as_view(), name='dashboardwidget_add'), - path('dashboard/widgets//configure/', views.DashboardWidgetConfigView.as_view(), name='dashboardwidget_config'), - path('dashboard/widgets//delete/', views.DashboardWidgetDeleteView.as_view(), name='dashboardwidget_delete'), + path( + 'dashboard/widgets//configure/', + views.DashboardWidgetConfigView.as_view(), + name='dashboardwidget_config' + ), + path( + 'dashboard/widgets//delete/', + views.DashboardWidgetDeleteView.as_view(), + name='dashboardwidget_delete' + ), # Scripts path('scripts/', views.ScriptListView.as_view(), name='script_list'), diff --git a/netbox/extras/utils.py b/netbox/extras/utils.py index 28d2e13f0..efe7ada5b 100644 --- a/netbox/extras/utils.py +++ b/netbox/extras/utils.py @@ -33,7 +33,7 @@ def image_upload(instance, filename): # Rename the file to the provided name, if any. Attempt to preserve the file extension. extension = filename.rsplit('.')[-1].lower() - if instance.name and extension in ['bmp', 'gif', 'jpeg', 'jpg', 'png']: + if instance.name and extension in ['bmp', 'gif', 'jpeg', 'jpg', 'png', 'webp']: filename = '.'.join([instance.name, extension]) elif instance.name: filename = instance.name diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 321842260..2c390a78c 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -42,6 +42,7 @@ from .tables import ReportResultsTable, ScriptResultsTable # Custom fields # +@register_model_view(CustomField, 'list', path='', detail=False) class CustomFieldListView(generic.ObjectListView): queryset = CustomField.objects.select_related('choice_set') filterset = filtersets.CustomFieldFilterSet @@ -69,6 +70,7 @@ class CustomFieldView(generic.ObjectView): } +@register_model_view(CustomField, 'add', detail=False) @register_model_view(CustomField, 'edit') class CustomFieldEditView(generic.ObjectEditView): queryset = CustomField.objects.select_related('choice_set') @@ -80,11 +82,13 @@ class CustomFieldDeleteView(generic.ObjectDeleteView): queryset = CustomField.objects.select_related('choice_set') +@register_model_view(CustomField, 'bulk_import', detail=False) class CustomFieldBulkImportView(generic.BulkImportView): queryset = CustomField.objects.select_related('choice_set') model_form = forms.CustomFieldImportForm +@register_model_view(CustomField, 'bulk_edit', path='edit', detail=False) class CustomFieldBulkEditView(generic.BulkEditView): queryset = CustomField.objects.select_related('choice_set') filterset = filtersets.CustomFieldFilterSet @@ -92,6 +96,7 @@ class CustomFieldBulkEditView(generic.BulkEditView): form = forms.CustomFieldBulkEditForm +@register_model_view(CustomField, 'bulk_delete', path='delete', detail=False) class CustomFieldBulkDeleteView(generic.BulkDeleteView): queryset = CustomField.objects.select_related('choice_set') filterset = filtersets.CustomFieldFilterSet @@ -102,6 +107,7 @@ class CustomFieldBulkDeleteView(generic.BulkDeleteView): # Custom field choices # +@register_model_view(CustomFieldChoiceSet, 'list', path='', detail=False) class CustomFieldChoiceSetListView(generic.ObjectListView): queryset = CustomFieldChoiceSet.objects.all() filterset = filtersets.CustomFieldChoiceSetFilterSet @@ -133,6 +139,7 @@ class CustomFieldChoiceSetView(generic.ObjectView): } +@register_model_view(CustomFieldChoiceSet, 'add', detail=False) @register_model_view(CustomFieldChoiceSet, 'edit') class CustomFieldChoiceSetEditView(generic.ObjectEditView): queryset = CustomFieldChoiceSet.objects.all() @@ -144,11 +151,13 @@ class CustomFieldChoiceSetDeleteView(generic.ObjectDeleteView): queryset = CustomFieldChoiceSet.objects.all() +@register_model_view(CustomFieldChoiceSet, 'bulk_import', detail=False) class CustomFieldChoiceSetBulkImportView(generic.BulkImportView): queryset = CustomFieldChoiceSet.objects.all() model_form = forms.CustomFieldChoiceSetImportForm +@register_model_view(CustomFieldChoiceSet, 'bulk_edit', path='edit', detail=False) class CustomFieldChoiceSetBulkEditView(generic.BulkEditView): queryset = CustomFieldChoiceSet.objects.all() filterset = filtersets.CustomFieldChoiceSetFilterSet @@ -156,6 +165,7 @@ class CustomFieldChoiceSetBulkEditView(generic.BulkEditView): form = forms.CustomFieldChoiceSetBulkEditForm +@register_model_view(CustomFieldChoiceSet, 'bulk_delete', path='delete', detail=False) class CustomFieldChoiceSetBulkDeleteView(generic.BulkDeleteView): queryset = CustomFieldChoiceSet.objects.all() filterset = filtersets.CustomFieldChoiceSetFilterSet @@ -166,6 +176,7 @@ class CustomFieldChoiceSetBulkDeleteView(generic.BulkDeleteView): # Custom links # +@register_model_view(CustomLink, 'list', path='', detail=False) class CustomLinkListView(generic.ObjectListView): queryset = CustomLink.objects.all() filterset = filtersets.CustomLinkFilterSet @@ -178,6 +189,7 @@ class CustomLinkView(generic.ObjectView): queryset = CustomLink.objects.all() +@register_model_view(CustomLink, 'add', detail=False) @register_model_view(CustomLink, 'edit') class CustomLinkEditView(generic.ObjectEditView): queryset = CustomLink.objects.all() @@ -189,11 +201,13 @@ class CustomLinkDeleteView(generic.ObjectDeleteView): queryset = CustomLink.objects.all() +@register_model_view(CustomLink, 'bulk_import', detail=False) class CustomLinkBulkImportView(generic.BulkImportView): queryset = CustomLink.objects.all() model_form = forms.CustomLinkImportForm +@register_model_view(CustomLink, 'bulk_edit', path='edit', detail=False) class CustomLinkBulkEditView(generic.BulkEditView): queryset = CustomLink.objects.all() filterset = filtersets.CustomLinkFilterSet @@ -201,6 +215,7 @@ class CustomLinkBulkEditView(generic.BulkEditView): form = forms.CustomLinkBulkEditForm +@register_model_view(CustomLink, 'bulk_delete', path='delete', detail=False) class CustomLinkBulkDeleteView(generic.BulkDeleteView): queryset = CustomLink.objects.all() filterset = filtersets.CustomLinkFilterSet @@ -211,6 +226,7 @@ class CustomLinkBulkDeleteView(generic.BulkDeleteView): # Export templates # +@register_model_view(ExportTemplate, 'list', path='', detail=False) class ExportTemplateListView(generic.ObjectListView): queryset = ExportTemplate.objects.all() filterset = filtersets.ExportTemplateFilterSet @@ -228,6 +244,7 @@ class ExportTemplateView(generic.ObjectView): queryset = ExportTemplate.objects.all() +@register_model_view(ExportTemplate, 'add', detail=False) @register_model_view(ExportTemplate, 'edit') class ExportTemplateEditView(generic.ObjectEditView): queryset = ExportTemplate.objects.all() @@ -239,11 +256,13 @@ class ExportTemplateDeleteView(generic.ObjectDeleteView): queryset = ExportTemplate.objects.all() +@register_model_view(ExportTemplate, 'bulk_import', detail=False) class ExportTemplateBulkImportView(generic.BulkImportView): queryset = ExportTemplate.objects.all() model_form = forms.ExportTemplateImportForm +@register_model_view(ExportTemplate, 'bulk_edit', path='edit', detail=False) class ExportTemplateBulkEditView(generic.BulkEditView): queryset = ExportTemplate.objects.all() filterset = filtersets.ExportTemplateFilterSet @@ -251,12 +270,14 @@ class ExportTemplateBulkEditView(generic.BulkEditView): form = forms.ExportTemplateBulkEditForm +@register_model_view(ExportTemplate, 'bulk_delete', path='delete', detail=False) class ExportTemplateBulkDeleteView(generic.BulkDeleteView): queryset = ExportTemplate.objects.all() filterset = filtersets.ExportTemplateFilterSet table = tables.ExportTemplateTable +@register_model_view(ExportTemplate, 'bulk_sync', path='sync', detail=False) class ExportTemplateBulkSyncDataView(generic.BulkSyncDataView): queryset = ExportTemplate.objects.all() @@ -283,6 +304,7 @@ class SavedFilterMixin: ) +@register_model_view(SavedFilter, 'list', path='', detail=False) class SavedFilterListView(SavedFilterMixin, generic.ObjectListView): filterset = filtersets.SavedFilterFilterSet filterset_form = forms.SavedFilterFilterForm @@ -294,6 +316,7 @@ class SavedFilterView(SavedFilterMixin, generic.ObjectView): queryset = SavedFilter.objects.all() +@register_model_view(SavedFilter, 'add', detail=False) @register_model_view(SavedFilter, 'edit') class SavedFilterEditView(SavedFilterMixin, generic.ObjectEditView): queryset = SavedFilter.objects.all() @@ -310,11 +333,13 @@ class SavedFilterDeleteView(SavedFilterMixin, generic.ObjectDeleteView): queryset = SavedFilter.objects.all() +@register_model_view(SavedFilter, 'bulk_import', detail=False) class SavedFilterBulkImportView(SavedFilterMixin, generic.BulkImportView): queryset = SavedFilter.objects.all() model_form = forms.SavedFilterImportForm +@register_model_view(SavedFilter, 'bulk_edit', path='edit', detail=False) class SavedFilterBulkEditView(SavedFilterMixin, generic.BulkEditView): queryset = SavedFilter.objects.all() filterset = filtersets.SavedFilterFilterSet @@ -322,6 +347,7 @@ class SavedFilterBulkEditView(SavedFilterMixin, generic.BulkEditView): form = forms.SavedFilterBulkEditForm +@register_model_view(SavedFilter, 'bulk_delete', path='delete', detail=False) class SavedFilterBulkDeleteView(SavedFilterMixin, generic.BulkDeleteView): queryset = SavedFilter.objects.all() filterset = filtersets.SavedFilterFilterSet @@ -332,6 +358,7 @@ class SavedFilterBulkDeleteView(SavedFilterMixin, generic.BulkDeleteView): # Bookmarks # +@register_model_view(Bookmark, 'add', detail=False) class BookmarkCreateView(generic.ObjectEditView): form = forms.BookmarkForm @@ -350,6 +377,7 @@ class BookmarkDeleteView(generic.ObjectDeleteView): return Bookmark.objects.filter(user=request.user) +@register_model_view(Bookmark, 'bulk_delete', path='delete', detail=False) class BookmarkBulkDeleteView(generic.BulkDeleteView): table = tables.BookmarkTable @@ -361,6 +389,7 @@ class BookmarkBulkDeleteView(generic.BulkDeleteView): # Notification groups # +@register_model_view(NotificationGroup, 'list', path='', detail=False) class NotificationGroupListView(generic.ObjectListView): queryset = NotificationGroup.objects.all() filterset = filtersets.NotificationGroupFilterSet @@ -373,6 +402,7 @@ class NotificationGroupView(generic.ObjectView): queryset = NotificationGroup.objects.all() +@register_model_view(NotificationGroup, 'add', detail=False) @register_model_view(NotificationGroup, 'edit') class NotificationGroupEditView(generic.ObjectEditView): queryset = NotificationGroup.objects.all() @@ -384,11 +414,13 @@ class NotificationGroupDeleteView(generic.ObjectDeleteView): queryset = NotificationGroup.objects.all() +@register_model_view(NotificationGroup, 'bulk_import', detail=False) class NotificationGroupBulkImportView(generic.BulkImportView): queryset = NotificationGroup.objects.all() model_form = forms.NotificationGroupImportForm +@register_model_view(NotificationGroup, 'bulk_edit', path='edit', detail=False) class NotificationGroupBulkEditView(generic.BulkEditView): queryset = NotificationGroup.objects.all() filterset = filtersets.NotificationGroupFilterSet @@ -396,6 +428,7 @@ class NotificationGroupBulkEditView(generic.BulkEditView): form = forms.NotificationGroupBulkEditForm +@register_model_view(NotificationGroup, 'bulk_delete', path='delete', detail=False) class NotificationGroupBulkDeleteView(generic.BulkDeleteView): queryset = NotificationGroup.objects.all() filterset = filtersets.NotificationGroupFilterSet @@ -459,6 +492,7 @@ class NotificationDeleteView(generic.ObjectDeleteView): return Notification.objects.filter(user=request.user) +@register_model_view(Notification, 'bulk_delete', path='delete', detail=False) class NotificationBulkDeleteView(generic.BulkDeleteView): table = tables.NotificationTable @@ -470,6 +504,7 @@ class NotificationBulkDeleteView(generic.BulkDeleteView): # Subscriptions # +@register_model_view(Subscription, 'add', detail=False) class SubscriptionCreateView(generic.ObjectEditView): form = forms.SubscriptionForm @@ -488,6 +523,7 @@ class SubscriptionDeleteView(generic.ObjectDeleteView): return Subscription.objects.filter(user=request.user) +@register_model_view(Subscription, 'bulk_delete', path='delete', detail=False) class SubscriptionBulkDeleteView(generic.BulkDeleteView): table = tables.SubscriptionTable @@ -499,6 +535,7 @@ class SubscriptionBulkDeleteView(generic.BulkDeleteView): # Webhooks # +@register_model_view(Webhook, 'list', path='', detail=False) class WebhookListView(generic.ObjectListView): queryset = Webhook.objects.all() filterset = filtersets.WebhookFilterSet @@ -511,6 +548,7 @@ class WebhookView(generic.ObjectView): queryset = Webhook.objects.all() +@register_model_view(Webhook, 'add', detail=False) @register_model_view(Webhook, 'edit') class WebhookEditView(generic.ObjectEditView): queryset = Webhook.objects.all() @@ -522,11 +560,13 @@ class WebhookDeleteView(generic.ObjectDeleteView): queryset = Webhook.objects.all() +@register_model_view(Webhook, 'bulk_import', detail=False) class WebhookBulkImportView(generic.BulkImportView): queryset = Webhook.objects.all() model_form = forms.WebhookImportForm +@register_model_view(Webhook, 'bulk_edit', path='edit', detail=False) class WebhookBulkEditView(generic.BulkEditView): queryset = Webhook.objects.all() filterset = filtersets.WebhookFilterSet @@ -534,6 +574,7 @@ class WebhookBulkEditView(generic.BulkEditView): form = forms.WebhookBulkEditForm +@register_model_view(Webhook, 'bulk_delete', path='delete', detail=False) class WebhookBulkDeleteView(generic.BulkDeleteView): queryset = Webhook.objects.all() filterset = filtersets.WebhookFilterSet @@ -544,6 +585,7 @@ class WebhookBulkDeleteView(generic.BulkDeleteView): # Event Rules # +@register_model_view(EventRule, 'list', path='', detail=False) class EventRuleListView(generic.ObjectListView): queryset = EventRule.objects.all() filterset = filtersets.EventRuleFilterSet @@ -556,6 +598,7 @@ class EventRuleView(generic.ObjectView): queryset = EventRule.objects.all() +@register_model_view(EventRule, 'add', detail=False) @register_model_view(EventRule, 'edit') class EventRuleEditView(generic.ObjectEditView): queryset = EventRule.objects.all() @@ -567,11 +610,13 @@ class EventRuleDeleteView(generic.ObjectDeleteView): queryset = EventRule.objects.all() +@register_model_view(EventRule, 'bulk_import', detail=False) class EventRuleBulkImportView(generic.BulkImportView): queryset = EventRule.objects.all() model_form = forms.EventRuleImportForm +@register_model_view(EventRule, 'bulk_edit', path='edit', detail=False) class EventRuleBulkEditView(generic.BulkEditView): queryset = EventRule.objects.all() filterset = filtersets.EventRuleFilterSet @@ -579,6 +624,7 @@ class EventRuleBulkEditView(generic.BulkEditView): form = forms.EventRuleBulkEditForm +@register_model_view(EventRule, 'bulk_delete', path='delete', detail=False) class EventRuleBulkDeleteView(generic.BulkDeleteView): queryset = EventRule.objects.all() filterset = filtersets.EventRuleFilterSet @@ -589,6 +635,7 @@ class EventRuleBulkDeleteView(generic.BulkDeleteView): # Tags # +@register_model_view(Tag, 'list', path='', detail=False) class TagListView(generic.ObjectListView): queryset = Tag.objects.annotate( items=count_related(TaggedItem, 'tag') @@ -624,6 +671,7 @@ class TagView(generic.ObjectView): } +@register_model_view(Tag, 'add', detail=False) @register_model_view(Tag, 'edit') class TagEditView(generic.ObjectEditView): queryset = Tag.objects.all() @@ -635,11 +683,13 @@ class TagDeleteView(generic.ObjectDeleteView): queryset = Tag.objects.all() +@register_model_view(Tag, 'bulk_import', detail=False) class TagBulkImportView(generic.BulkImportView): queryset = Tag.objects.all() model_form = forms.TagImportForm +@register_model_view(Tag, 'bulk_edit', path='edit', detail=False) class TagBulkEditView(generic.BulkEditView): queryset = Tag.objects.annotate( items=count_related(TaggedItem, 'tag') @@ -648,6 +698,7 @@ class TagBulkEditView(generic.BulkEditView): form = forms.TagBulkEditForm +@register_model_view(Tag, 'bulk_delete', path='delete', detail=False) class TagBulkDeleteView(generic.BulkDeleteView): queryset = Tag.objects.annotate( items=count_related(TaggedItem, 'tag') @@ -659,6 +710,7 @@ class TagBulkDeleteView(generic.BulkDeleteView): # Config contexts # +@register_model_view(ConfigContext, 'list', path='', detail=False) class ConfigContextListView(generic.ObjectListView): queryset = ConfigContext.objects.all() filterset = filtersets.ConfigContextFilterSet @@ -711,30 +763,34 @@ class ConfigContextView(generic.ObjectView): } +@register_model_view(ConfigContext, 'add', detail=False) @register_model_view(ConfigContext, 'edit') class ConfigContextEditView(generic.ObjectEditView): queryset = ConfigContext.objects.all() form = forms.ConfigContextForm -class ConfigContextBulkEditView(generic.BulkEditView): - queryset = ConfigContext.objects.all() - filterset = filtersets.ConfigContextFilterSet - table = tables.ConfigContextTable - form = forms.ConfigContextBulkEditForm - - @register_model_view(ConfigContext, 'delete') class ConfigContextDeleteView(generic.ObjectDeleteView): queryset = ConfigContext.objects.all() +@register_model_view(ConfigContext, 'bulk_edit', path='edit', detail=False) +class ConfigContextBulkEditView(generic.BulkEditView): + queryset = ConfigContext.objects.all() + filterset = filtersets.ConfigContextFilterSet + table = tables.ConfigContextTable + form = forms.ConfigContextBulkEditForm + + +@register_model_view(ConfigContext, 'bulk_delete', path='delete', detail=False) class ConfigContextBulkDeleteView(generic.BulkDeleteView): queryset = ConfigContext.objects.all() filterset = filtersets.ConfigContextFilterSet table = tables.ConfigContextTable +@register_model_view(ConfigContext, 'bulk_sync', path='sync', detail=False) class ConfigContextBulkSyncDataView(generic.BulkSyncDataView): queryset = ConfigContext.objects.all() @@ -768,6 +824,7 @@ class ObjectConfigContextView(generic.ObjectView): # Config templates # +@register_model_view(ConfigTemplate, 'list', path='', detail=False) class ConfigTemplateListView(generic.ObjectListView): queryset = ConfigTemplate.objects.annotate( device_count=count_related(Device, 'config_template'), @@ -790,6 +847,7 @@ class ConfigTemplateView(generic.ObjectView): queryset = ConfigTemplate.objects.all() +@register_model_view(ConfigTemplate, 'add', detail=False) @register_model_view(ConfigTemplate, 'edit') class ConfigTemplateEditView(generic.ObjectEditView): queryset = ConfigTemplate.objects.all() @@ -801,11 +859,13 @@ class ConfigTemplateDeleteView(generic.ObjectDeleteView): queryset = ConfigTemplate.objects.all() +@register_model_view(ConfigTemplate, 'bulk_import', detail=False) class ConfigTemplateBulkImportView(generic.BulkImportView): queryset = ConfigTemplate.objects.all() model_form = forms.ConfigTemplateImportForm +@register_model_view(ConfigTemplate, 'bulk_edit', path='edit', detail=False) class ConfigTemplateBulkEditView(generic.BulkEditView): queryset = ConfigTemplate.objects.all() filterset = filtersets.ConfigTemplateFilterSet @@ -813,12 +873,14 @@ class ConfigTemplateBulkEditView(generic.BulkEditView): form = forms.ConfigTemplateBulkEditForm +@register_model_view(ConfigTemplate, 'bulk_delete', path='delete', detail=False) class ConfigTemplateBulkDeleteView(generic.BulkDeleteView): queryset = ConfigTemplate.objects.all() filterset = filtersets.ConfigTemplateFilterSet table = tables.ConfigTemplateTable +@register_model_view(ConfigTemplate, 'bulk_sync', path='sync', detail=False) class ConfigTemplateBulkSyncDataView(generic.BulkSyncDataView): queryset = ConfigTemplate.objects.all() @@ -827,6 +889,7 @@ class ConfigTemplateBulkSyncDataView(generic.BulkSyncDataView): # Image attachments # +@register_model_view(ImageAttachment, 'list', path='', detail=False) class ImageAttachmentListView(generic.ObjectListView): queryset = ImageAttachment.objects.all() filterset = filtersets.ImageAttachmentFilterSet @@ -837,6 +900,7 @@ class ImageAttachmentListView(generic.ObjectListView): } +@register_model_view(ImageAttachment, 'add', detail=False) @register_model_view(ImageAttachment, 'edit') class ImageAttachmentEditView(generic.ObjectEditView): queryset = ImageAttachment.objects.all() @@ -871,14 +935,15 @@ class ImageAttachmentDeleteView(generic.ObjectDeleteView): # Journal entries # +@register_model_view(JournalEntry, 'list', path='', detail=False) class JournalEntryListView(generic.ObjectListView): queryset = JournalEntry.objects.all() filterset = filtersets.JournalEntryFilterSet filterset_form = forms.JournalEntryFilterForm table = tables.JournalEntryTable actions = { - 'import': {'add'}, 'export': {'view'}, + 'bulk_import': {'add'}, 'bulk_edit': {'change'}, 'bulk_delete': {'delete'}, } @@ -889,6 +954,7 @@ class JournalEntryView(generic.ObjectView): queryset = JournalEntry.objects.all() +@register_model_view(JournalEntry, 'add', detail=False) @register_model_view(JournalEntry, 'edit') class JournalEntryEditView(generic.ObjectEditView): queryset = JournalEntry.objects.all() @@ -917,6 +983,13 @@ class JournalEntryDeleteView(generic.ObjectDeleteView): return reverse(viewname, kwargs={'pk': obj.pk}) +@register_model_view(JournalEntry, 'bulk_import', detail=False) +class JournalEntryBulkImportView(generic.BulkImportView): + queryset = JournalEntry.objects.all() + model_form = forms.JournalEntryImportForm + + +@register_model_view(JournalEntry, 'bulk_edit', path='edit', detail=False) class JournalEntryBulkEditView(generic.BulkEditView): queryset = JournalEntry.objects.all() filterset = filtersets.JournalEntryFilterSet @@ -924,17 +997,13 @@ class JournalEntryBulkEditView(generic.BulkEditView): form = forms.JournalEntryBulkEditForm +@register_model_view(JournalEntry, 'bulk_delete', path='delete', detail=False) class JournalEntryBulkDeleteView(generic.BulkDeleteView): queryset = JournalEntry.objects.all() filterset = filtersets.JournalEntryFilterSet table = tables.JournalEntryTable -class JournalEntryBulkImportView(generic.BulkImportView): - queryset = JournalEntry.objects.all() - model_form = forms.JournalEntryImportForm - - # # Dashboard & widgets # @@ -1180,7 +1249,7 @@ class ScriptView(BaseScriptView): data=form.cleaned_data, request=copy_safe_request(request), job_timeout=script.python_class.job_timeout, - commit=form.cleaned_data.pop('_commit') + commit=form.cleaned_data.pop('_commit'), ) return redirect('extras:script_result', job_pk=job.pk) diff --git a/netbox/ipam/api/nested_serializers.py b/netbox/ipam/api/nested_serializers.py deleted file mode 100644 index 8b10f29df..000000000 --- a/netbox/ipam/api/nested_serializers.py +++ /dev/null @@ -1,204 +0,0 @@ -import warnings - -from drf_spectacular.utils import extend_schema_serializer -from rest_framework import serializers - -from ipam import models -from netbox.api.fields import RelatedObjectCountField -from netbox.api.serializers import WritableNestedSerializer -from .field_serializers import IPAddressField -from .serializers_.nested import NestedIPAddressSerializer - -__all__ = [ - 'NestedAggregateSerializer', - 'NestedASNSerializer', - 'NestedASNRangeSerializer', - 'NestedFHRPGroupSerializer', - 'NestedFHRPGroupAssignmentSerializer', - 'NestedIPAddressSerializer', - 'NestedIPRangeSerializer', - 'NestedPrefixSerializer', - 'NestedRIRSerializer', - 'NestedRoleSerializer', - 'NestedRouteTargetSerializer', - 'NestedServiceSerializer', - 'NestedServiceTemplateSerializer', - 'NestedVLANGroupSerializer', - 'NestedVLANSerializer', - 'NestedVRFSerializer', -] - -# TODO: Remove in v4.2 -warnings.warn( - "Dedicated nested serializers will be removed in NetBox v4.2. Use Serializer(nested=True) instead.", - DeprecationWarning -) - - -# -# ASN ranges -# - -class NestedASNRangeSerializer(WritableNestedSerializer): - - class Meta: - model = models.ASNRange - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -# -# ASNs -# - -class NestedASNSerializer(WritableNestedSerializer): - - class Meta: - model = models.ASN - fields = ['id', 'url', 'display_url', 'display', 'asn'] - - -# -# VRFs -# - -@extend_schema_serializer( - exclude_fields=('prefix_count',), -) -class NestedVRFSerializer(WritableNestedSerializer): - prefix_count = RelatedObjectCountField('prefixes') - - class Meta: - model = models.VRF - fields = ['id', 'url', 'display_url', 'display', 'name', 'rd', 'prefix_count'] - - -# -# Route targets -# - -class NestedRouteTargetSerializer(WritableNestedSerializer): - - class Meta: - model = models.RouteTarget - fields = ['id', 'url', 'display_url', 'display', 'name'] - - -# -# RIRs/aggregates -# - -@extend_schema_serializer( - exclude_fields=('aggregate_count',), -) -class NestedRIRSerializer(WritableNestedSerializer): - aggregate_count = RelatedObjectCountField('aggregates') - - class Meta: - model = models.RIR - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'aggregate_count'] - - -class NestedAggregateSerializer(WritableNestedSerializer): - family = serializers.IntegerField(read_only=True) - - class Meta: - model = models.Aggregate - fields = ['id', 'url', 'display_url', 'display', 'family', 'prefix'] - - -# -# FHRP groups -# - -class NestedFHRPGroupSerializer(WritableNestedSerializer): - - class Meta: - model = models.FHRPGroup - fields = ['id', 'url', 'display_url', 'display', 'protocol', 'group_id'] - - -class NestedFHRPGroupAssignmentSerializer(WritableNestedSerializer): - group = NestedFHRPGroupSerializer() - - class Meta: - model = models.FHRPGroupAssignment - fields = ['id', 'url', 'display_url', 'display', 'group', 'interface_type', 'interface_id', 'priority'] - - -# -# VLANs -# - -@extend_schema_serializer( - exclude_fields=('prefix_count', 'vlan_count'), -) -class NestedRoleSerializer(WritableNestedSerializer): - prefix_count = RelatedObjectCountField('prefixes') - vlan_count = RelatedObjectCountField('vlans') - - class Meta: - model = models.Role - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'prefix_count', 'vlan_count'] - - -@extend_schema_serializer( - exclude_fields=('vlan_count',), -) -class NestedVLANGroupSerializer(WritableNestedSerializer): - vlan_count = RelatedObjectCountField('vlans') - - class Meta: - model = models.VLANGroup - fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'vlan_count'] - - -class NestedVLANSerializer(WritableNestedSerializer): - - class Meta: - model = models.VLAN - fields = ['id', 'url', 'display_url', 'display', 'vid', 'name'] - - -# -# Prefixes -# - -class NestedPrefixSerializer(WritableNestedSerializer): - family = serializers.IntegerField(read_only=True) - _depth = serializers.IntegerField(read_only=True) - - class Meta: - model = models.Prefix - fields = ['id', 'url', 'display_url', 'display', 'family', 'prefix', '_depth'] - - -# -# IP ranges -# - -class NestedIPRangeSerializer(WritableNestedSerializer): - family = serializers.IntegerField(read_only=True) - start_address = IPAddressField() - end_address = IPAddressField() - - class Meta: - model = models.IPRange - fields = ['id', 'url', 'display_url', 'display', 'family', 'start_address', 'end_address'] - - -# -# Services -# - -class NestedServiceTemplateSerializer(WritableNestedSerializer): - - class Meta: - model = models.ServiceTemplate - fields = ['id', 'url', 'display_url', 'display', 'name', 'protocol', 'ports'] - - -class NestedServiceSerializer(WritableNestedSerializer): - - class Meta: - model = models.Service - fields = ['id', 'url', 'display_url', 'display', 'name', 'protocol', 'ports'] diff --git a/netbox/ipam/api/serializers_/ip.py b/netbox/ipam/api/serializers_/ip.py index 535ffcec1..bfc7ac546 100644 --- a/netbox/ipam/api/serializers_/ip.py +++ b/netbox/ipam/api/serializers_/ip.py @@ -2,7 +2,7 @@ from django.contrib.contenttypes.models import ContentType from drf_spectacular.utils import extend_schema_field from rest_framework import serializers -from dcim.api.serializers_.sites import SiteSerializer +from dcim.constants import LOCATION_SCOPE_TYPES from ipam.choices import * from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS from ipam.models import Aggregate, IPAddress, IPRange, Prefix @@ -45,8 +45,17 @@ class AggregateSerializer(NetBoxModelSerializer): class PrefixSerializer(NetBoxModelSerializer): family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True) - site = SiteSerializer(nested=True, required=False, allow_null=True) vrf = VRFSerializer(nested=True, required=False, allow_null=True) + scope_type = ContentTypeField( + queryset=ContentType.objects.filter( + model__in=LOCATION_SCOPE_TYPES + ), + allow_null=True, + required=False, + default=None + ) + scope_id = serializers.IntegerField(allow_null=True, required=False, default=None) + scope = serializers.SerializerMethodField(read_only=True) tenant = TenantSerializer(nested=True, required=False, allow_null=True) vlan = VLANSerializer(nested=True, required=False, allow_null=True) status = ChoiceField(choices=PrefixStatusChoices, required=False) @@ -58,12 +67,20 @@ class PrefixSerializer(NetBoxModelSerializer): class Meta: model = Prefix fields = [ - 'id', 'url', 'display_url', 'display', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', - 'role', 'is_pool', 'mark_utilized', 'description', 'comments', 'tags', 'custom_fields', - 'created', 'last_updated', 'children', '_depth', + 'id', 'url', 'display_url', 'display', 'family', 'prefix', 'vrf', 'scope_type', 'scope_id', 'scope', + 'tenant', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description', 'comments', 'tags', + 'custom_fields', 'created', 'last_updated', 'children', '_depth', ] brief_fields = ('id', 'url', 'display', 'family', 'prefix', 'description', '_depth') + @extend_schema_field(serializers.JSONField(allow_null=True)) + def get_scope(self, obj): + if obj.scope_id is None: + return None + serializer = get_serializer_for_model(obj.scope) + context = {'request': self.context['request']} + return serializer(obj.scope, nested=True, context=context).data + class PrefixLengthSerializer(serializers.Serializer): diff --git a/netbox/ipam/api/serializers_/nested.py b/netbox/ipam/api/serializers_/nested.py index 5297565bb..b56b15984 100644 --- a/netbox/ipam/api/serializers_/nested.py +++ b/netbox/ipam/api/serializers_/nested.py @@ -6,6 +6,7 @@ from ..field_serializers import IPAddressField __all__ = ( 'NestedIPAddressSerializer', + 'NestedVLANSerializer', ) @@ -16,3 +17,10 @@ class NestedIPAddressSerializer(WritableNestedSerializer): class Meta: model = models.IPAddress fields = ['id', 'url', 'display_url', 'display', 'family', 'address'] + + +class NestedVLANSerializer(WritableNestedSerializer): + + class Meta: + model = models.VLAN + fields = ['id', 'url', 'display', 'vid', 'name', 'description'] diff --git a/netbox/ipam/api/serializers_/vlans.py b/netbox/ipam/api/serializers_/vlans.py index 608fcf0b4..9b5501dc5 100644 --- a/netbox/ipam/api/serializers_/vlans.py +++ b/netbox/ipam/api/serializers_/vlans.py @@ -5,12 +5,13 @@ from rest_framework import serializers from dcim.api.serializers_.sites import SiteSerializer from ipam.choices import * from ipam.constants import VLANGROUP_SCOPE_TYPES -from ipam.models import VLAN, VLANGroup +from ipam.models import VLAN, VLANGroup, VLANTranslationPolicy, VLANTranslationRule from netbox.api.fields import ChoiceField, ContentTypeField, IntegerRangeSerializer, RelatedObjectCountField from netbox.api.serializers import NetBoxModelSerializer from tenancy.api.serializers_.tenants import TenantSerializer from utilities.api import get_serializer_for_model from vpn.api.serializers_.l2vpn import L2VPNTerminationSerializer +from .nested import NestedVLANSerializer from .roles import RoleSerializer __all__ = ( @@ -18,6 +19,8 @@ __all__ = ( 'CreateAvailableVLANSerializer', 'VLANGroupSerializer', 'VLANSerializer', + 'VLANTranslationPolicySerializer', + 'VLANTranslationRuleSerializer', ) @@ -62,6 +65,8 @@ class VLANSerializer(NetBoxModelSerializer): tenant = TenantSerializer(nested=True, required=False, allow_null=True) status = ChoiceField(choices=VLANStatusChoices, required=False) role = RoleSerializer(nested=True, required=False, allow_null=True) + qinq_role = ChoiceField(choices=VLANQinQRoleChoices, required=False) + qinq_svlan = NestedVLANSerializer(required=False, allow_null=True, default=None) l2vpn_termination = L2VPNTerminationSerializer(nested=True, read_only=True, allow_null=True) # Related object counts @@ -71,8 +76,8 @@ class VLANSerializer(NetBoxModelSerializer): model = VLAN fields = [ 'id', 'url', 'display_url', 'display', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', - 'description', 'comments', 'l2vpn_termination', 'tags', 'custom_fields', 'created', 'last_updated', - 'prefix_count', + 'description', 'qinq_role', 'qinq_svlan', 'comments', 'l2vpn_termination', 'tags', 'custom_fields', + 'created', 'last_updated', 'prefix_count', ] brief_fields = ('id', 'url', 'display', 'vid', 'name', 'description') @@ -110,3 +115,19 @@ class CreateAvailableVLANSerializer(NetBoxModelSerializer): def validate(self, data): # Bypass model validation since we don't have a VID yet return data + + +class VLANTranslationRuleSerializer(NetBoxModelSerializer): + + class Meta: + model = VLANTranslationRule + fields = ['id', 'url', 'display', 'policy', 'local_vid', 'remote_vid', 'description'] + + +class VLANTranslationPolicySerializer(NetBoxModelSerializer): + rules = VLANTranslationRuleSerializer(many=True, read_only=True) + + class Meta: + model = VLANTranslationPolicy + fields = ['id', 'url', 'display', 'name', 'description', 'display', 'rules'] + brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/ipam/api/urls.py b/netbox/ipam/api/urls.py index bae9d8048..ea76025ec 100644 --- a/netbox/ipam/api/urls.py +++ b/netbox/ipam/api/urls.py @@ -21,6 +21,8 @@ router.register('fhrp-groups', views.FHRPGroupViewSet) router.register('fhrp-group-assignments', views.FHRPGroupAssignmentViewSet) router.register('vlan-groups', views.VLANGroupViewSet) router.register('vlans', views.VLANViewSet) +router.register('vlan-translation-policies', views.VLANTranslationPolicyViewSet) +router.register('vlan-translation-rules', views.VLANTranslationRuleViewSet) router.register('service-templates', views.ServiceTemplateViewSet) router.register('services', views.ServiceViewSet) diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py index ffd4d5b7d..783d13523 100644 --- a/netbox/ipam/api/views.py +++ b/netbox/ipam/api/views.py @@ -143,6 +143,18 @@ class VLANViewSet(NetBoxModelViewSet): filterset_class = filtersets.VLANFilterSet +class VLANTranslationPolicyViewSet(NetBoxModelViewSet): + queryset = VLANTranslationPolicy.objects.all() + serializer_class = serializers.VLANTranslationPolicySerializer + filterset_class = filtersets.VLANTranslationPolicyFilterSet + + +class VLANTranslationRuleViewSet(NetBoxModelViewSet): + queryset = VLANTranslationRule.objects.all() + serializer_class = serializers.VLANTranslationRuleSerializer + filterset_class = filtersets.VLANTranslationRuleFilterSet + + class ServiceTemplateViewSet(NetBoxModelViewSet): queryset = ServiceTemplate.objects.all() serializer_class = serializers.ServiceTemplateSerializer diff --git a/netbox/ipam/apps.py b/netbox/ipam/apps.py index c118d5464..ae88d69a9 100644 --- a/netbox/ipam/apps.py +++ b/netbox/ipam/apps.py @@ -1,5 +1,7 @@ from django.apps import AppConfig +from netbox import denormalized + class IPAMConfig(AppConfig): name = "ipam" @@ -8,6 +10,16 @@ class IPAMConfig(AppConfig): def ready(self): from netbox.models.features import register_models from . import signals, search # noqa: F401 + from .models import Prefix # Register models register_models(*self.get_models()) + + # Register denormalized fields + denormalized.register(Prefix, '_site', { + '_region': 'region', + '_site_group': 'group', + }) + denormalized.register(Prefix, '_location', { + '_site': 'site', + }) diff --git a/netbox/ipam/choices.py b/netbox/ipam/choices.py index 017fd0430..51b65a6da 100644 --- a/netbox/ipam/choices.py +++ b/netbox/ipam/choices.py @@ -157,6 +157,17 @@ class VLANStatusChoices(ChoiceSet): ] +class VLANQinQRoleChoices(ChoiceSet): + + ROLE_SERVICE = 'svlan' + ROLE_CUSTOMER = 'cvlan' + + CHOICES = [ + (ROLE_SERVICE, _('Service'), 'blue'), + (ROLE_CUSTOMER, _('Customer'), 'orange'), + ] + + # # Services # diff --git a/netbox/ipam/fields.py b/netbox/ipam/fields.py index 20341005d..a82976326 100644 --- a/netbox/ipam/fields.py +++ b/netbox/ipam/fields.py @@ -105,6 +105,8 @@ IPAddressField.register_lookup(lookups.NetIn) IPAddressField.register_lookup(lookups.NetHostContained) IPAddressField.register_lookup(lookups.NetFamily) IPAddressField.register_lookup(lookups.NetMaskLength) +IPAddressField.register_lookup(lookups.Host) +IPAddressField.register_lookup(lookups.Inet) class ASNField(models.BigIntegerField): diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index 894219c64..c762c15fe 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -1,5 +1,6 @@ import django_filters import netaddr +from dcim.base_filtersets import ScopedFilterSet from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.db.models import Q @@ -37,6 +38,8 @@ __all__ = ( 'ServiceTemplateFilterSet', 'VLANFilterSet', 'VLANGroupFilterSet', + 'VLANTranslationPolicyFilterSet', + 'VLANTranslationRuleFilterSet', 'VRFFilterSet', ) @@ -271,7 +274,7 @@ class RoleFilterSet(OrganizationalModelFilterSet): fields = ('id', 'name', 'slug', 'description', 'weight') -class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet): +class PrefixFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet): family = django_filters.NumberFilter( field_name='prefix', lookup_expr='family' @@ -332,42 +335,6 @@ class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet): to_field_name='rd', label=_('VRF (RD)'), ) - region_id = TreeNodeMultipleChoiceFilter( - queryset=Region.objects.all(), - field_name='site__region', - lookup_expr='in', - label=_('Region (ID)'), - ) - region = TreeNodeMultipleChoiceFilter( - queryset=Region.objects.all(), - field_name='site__region', - lookup_expr='in', - to_field_name='slug', - label=_('Region (slug)'), - ) - site_group_id = TreeNodeMultipleChoiceFilter( - queryset=SiteGroup.objects.all(), - field_name='site__group', - lookup_expr='in', - label=_('Site group (ID)'), - ) - site_group = TreeNodeMultipleChoiceFilter( - queryset=SiteGroup.objects.all(), - field_name='site__group', - lookup_expr='in', - to_field_name='slug', - label=_('Site group (slug)'), - ) - site_id = django_filters.ModelMultipleChoiceFilter( - queryset=Site.objects.all(), - label=_('Site (ID)'), - ) - site = django_filters.ModelMultipleChoiceFilter( - field_name='site__slug', - queryset=Site.objects.all(), - to_field_name='slug', - label=_('Site (slug)'), - ) vlan_id = django_filters.ModelMultipleChoiceFilter( queryset=VLAN.objects.all(), label=_('VLAN (ID)'), @@ -393,7 +360,7 @@ class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet): class Meta: model = Prefix - fields = ('id', 'is_pool', 'mark_utilized', 'description') + fields = ('id', 'scope_id', 'is_pool', 'mark_utilized', 'description') def search(self, queryset, name, value): if not value.strip(): @@ -1024,6 +991,17 @@ class VLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet): queryset=VirtualMachine.objects.all(), method='get_for_virtualmachine' ) + qinq_role = django_filters.MultipleChoiceFilter( + choices=VLANQinQRoleChoices + ) + qinq_svlan_id = django_filters.ModelMultipleChoiceFilter( + queryset=VLAN.objects.all(), + label=_('Q-in-Q SVLAN (ID)'), + ) + qinq_svlan_vid = MultiValueNumberFilter( + field_name='qinq_svlan__vid', + label=_('Q-in-Q SVLAN number (1-4094)'), + ) l2vpn_id = django_filters.ModelMultipleChoiceFilter( field_name='l2vpn_terminations__l2vpn', queryset=L2VPN.objects.all(), @@ -1089,6 +1067,53 @@ class VLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet): ) +class VLANTranslationPolicyFilterSet(NetBoxModelFilterSet): + + class Meta: + model = VLANTranslationPolicy + fields = ('id', 'name', 'description') + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + qs_filter = ( + Q(name__icontains=value) | + Q(description__icontains=value) + ) + return queryset.filter(qs_filter) + + +class VLANTranslationRuleFilterSet(NetBoxModelFilterSet): + policy_id = django_filters.ModelMultipleChoiceFilter( + queryset=VLANTranslationPolicy.objects.all(), + label=_('VLAN Translation Policy (ID)'), + ) + policy = django_filters.ModelMultipleChoiceFilter( + field_name='policy__name', + queryset=VLANTranslationPolicy.objects.all(), + to_field_name='name', + label=_('VLAN Translation Policy (name)'), + ) + + class Meta: + model = VLANTranslationRule + fields = ('id', 'policy_id', 'policy', 'local_vid', 'remote_vid', 'description') + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + qs_filter = ( + Q(policy__name__icontains=value) + ) + try: + int_value = int(value.strip()) + qs_filter |= Q(local_vid=int_value) + qs_filter |= Q(remote_vid=int_value) + except ValueError: + pass + return queryset.filter(qs_filter) + + class ServiceTemplateFilterSet(NetBoxModelFilterSet): port = NumericArrayFilter( field_name='ports', diff --git a/netbox/ipam/forms/bulk_edit.py b/netbox/ipam/forms/bulk_edit.py index f4a7eabb7..7f3216cfd 100644 --- a/netbox/ipam/forms/bulk_edit.py +++ b/netbox/ipam/forms/bulk_edit.py @@ -1,22 +1,24 @@ from django import forms from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import gettext_lazy as _ -from dcim.models import Location, Rack, Region, Site, SiteGroup +from dcim.forms.mixins import ScopedBulkEditForm +from dcim.models import Region, Site, SiteGroup from ipam.choices import * from ipam.constants import * from ipam.models import * from ipam.models import ASN from netbox.forms import NetBoxModelBulkEditForm from tenancy.models import Tenant -from utilities.forms import add_blank_choice +from utilities.forms import add_blank_choice, get_field_value from utilities.forms.fields import ( CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField, NumericRangeArrayField, ) from utilities.forms.rendering import FieldSet -from utilities.forms.widgets import BulkEditNullBooleanSelect -from virtualization.models import Cluster, ClusterGroup +from utilities.forms.widgets import BulkEditNullBooleanSelect, HTMXSelect +from utilities.templatetags.builtins.filters import bettertitle __all__ = ( 'AggregateBulkEditForm', @@ -33,6 +35,8 @@ __all__ = ( 'ServiceTemplateBulkEditForm', 'VLANBulkEditForm', 'VLANGroupBulkEditForm', + 'VLANTranslationPolicyBulkEditForm', + 'VLANTranslationRuleBulkEditForm', 'VRFBulkEditForm', ) @@ -202,26 +206,7 @@ class RoleBulkEditForm(NetBoxModelBulkEditForm): nullable_fields = ('description',) -class PrefixBulkEditForm(NetBoxModelBulkEditForm): - region = DynamicModelChoiceField( - label=_('Region'), - queryset=Region.objects.all(), - required=False - ) - site_group = DynamicModelChoiceField( - label=_('Site group'), - queryset=SiteGroup.objects.all(), - required=False - ) - site = DynamicModelChoiceField( - label=_('Site'), - queryset=Site.objects.all(), - required=False, - query_params={ - 'region_id': '$region', - 'group_id': '$site_group', - } - ) +class PrefixBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm): vlan_group = DynamicModelChoiceField( queryset=VLANGroup.objects.all(), required=False, @@ -281,12 +266,12 @@ class PrefixBulkEditForm(NetBoxModelBulkEditForm): model = Prefix fieldsets = ( FieldSet('tenant', 'status', 'role', 'description'), - FieldSet('region', 'site_group', 'site', name=_('Site')), FieldSet('vrf', 'prefix_length', 'is_pool', 'mark_utilized', name=_('Addressing')), + FieldSet('scope_type', 'scope', name=_('Scope')), FieldSet('vlan_group', 'vlan', name=_('VLAN Assignment')), ) nullable_fields = ( - 'site', 'vlan', 'vrf', 'tenant', 'role', 'description', 'comments', + 'vlan', 'vrf', 'tenant', 'role', 'scope', 'description', 'comments', ) @@ -429,62 +414,17 @@ class VLANGroupBulkEditForm(NetBoxModelBulkEditForm): required=False ) scope_type = ContentTypeChoiceField( - label=_('Scope type'), queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES), - required=False - ) - scope_id = forms.IntegerField( + widget=HTMXSelect(method='post', attrs={'hx-select': '#form_fields'}), required=False, - widget=forms.HiddenInput() + label=_('Scope type') ) - region = DynamicModelChoiceField( - label=_('Region'), - queryset=Region.objects.all(), - required=False - ) - sitegroup = DynamicModelChoiceField( - queryset=SiteGroup.objects.all(), + scope = DynamicModelChoiceField( + label=_('Scope'), + queryset=Site.objects.none(), # Initial queryset required=False, - label=_('Site group') - ) - site = DynamicModelChoiceField( - label=_('Site'), - queryset=Site.objects.all(), - required=False, - query_params={ - 'region_id': '$region', - 'group_id': '$sitegroup', - } - ) - location = DynamicModelChoiceField( - label=_('Location'), - queryset=Location.objects.all(), - required=False, - query_params={ - 'site_id': '$site', - } - ) - rack = DynamicModelChoiceField( - label=_('Rack'), - queryset=Rack.objects.all(), - required=False, - query_params={ - 'site_id': '$site', - 'location_id': '$location', - } - ) - clustergroup = DynamicModelChoiceField( - queryset=ClusterGroup.objects.all(), - required=False, - label=_('Cluster group') - ) - cluster = DynamicModelChoiceField( - label=_('Cluster'), - queryset=Cluster.objects.all(), - required=False, - query_params={ - 'group_id': '$clustergroup', - } + disabled=True, + selector=True ) vid_ranges = NumericRangeArrayField( label=_('VLAN ID ranges'), @@ -494,24 +434,23 @@ class VLANGroupBulkEditForm(NetBoxModelBulkEditForm): model = VLANGroup fieldsets = ( FieldSet('site', 'vid_ranges', 'description'), - FieldSet( - 'scope_type', 'region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster', name=_('Scope') - ), + FieldSet('scope_type', 'scope', name=_('Scope')), ) - nullable_fields = ('description',) + nullable_fields = ('description', 'scope') - def clean(self): - super().clean() + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) - # Assign scope based on scope_type - if self.cleaned_data.get('scope_type'): - scope_field = self.cleaned_data['scope_type'].model - if scope_obj := self.cleaned_data.get(scope_field): - self.cleaned_data['scope_id'] = scope_obj.pk - self.changed_data.append('scope_id') - else: - self.cleaned_data.pop('scope_type') - self.changed_data.remove('scope_type') + if scope_type_id := get_field_value(self, 'scope_type'): + try: + scope_type = ContentType.objects.get(pk=scope_type_id) + model = scope_type.model_class() + self.fields['scope'].queryset = model.objects.all() + self.fields['scope'].widget.attrs['selector'] = model._meta.label_lower + self.fields['scope'].disabled = False + self.fields['scope'].label = _(bettertitle(model._meta.verbose_name)) + except ObjectDoesNotExist: + pass class VLANBulkEditForm(NetBoxModelBulkEditForm): @@ -562,18 +501,62 @@ class VLANBulkEditForm(NetBoxModelBulkEditForm): max_length=200, required=False ) + qinq_role = forms.ChoiceField( + label=_('Q-in-Q role'), + choices=add_blank_choice(VLANQinQRoleChoices), + required=False + ) + qinq_svlan = DynamicModelChoiceField( + label=_('Q-in-Q SVLAN'), + queryset=VLAN.objects.all(), + required=False, + query_params={ + 'qinq_role': VLANQinQRoleChoices.ROLE_SERVICE, + } + ) comments = CommentField() model = VLAN fieldsets = ( FieldSet('status', 'role', 'tenant', 'description'), + FieldSet('qinq_role', 'qinq_svlan', name=_('Q-in-Q')), FieldSet('region', 'site_group', 'site', 'group', name=_('Site & Group')), ) nullable_fields = ( - 'site', 'group', 'tenant', 'role', 'description', 'comments', + 'site', 'group', 'tenant', 'role', 'description', 'qinq_role', 'qinq_svlan', 'comments', ) +class VLANTranslationPolicyBulkEditForm(NetBoxModelBulkEditForm): + description = forms.CharField( + label=_('Description'), + max_length=200, + required=False + ) + + model = VLANTranslationPolicy + fieldsets = ( + FieldSet('description'), + ) + nullable_fields = ('description',) + + +class VLANTranslationRuleBulkEditForm(NetBoxModelBulkEditForm): + policy = DynamicModelChoiceField( + label=_('Policy'), + queryset=VLANTranslationPolicy.objects.all(), + selector=True + ) + local_vid = forms.IntegerField(required=False) + remote_vid = forms.IntegerField(required=False) + + model = VLANTranslationRule + fieldsets = ( + FieldSet('policy', 'local_vid', 'remote_vid'), + ) + fields = ('policy', 'local_vid', 'remote_vid') + + class ServiceTemplateBulkEditForm(NetBoxModelBulkEditForm): protocol = forms.ChoiceField( label=_('Protocol'), diff --git a/netbox/ipam/forms/bulk_import.py b/netbox/ipam/forms/bulk_import.py index dea250c79..0b37665d5 100644 --- a/netbox/ipam/forms/bulk_import.py +++ b/netbox/ipam/forms/bulk_import.py @@ -3,6 +3,7 @@ from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _ from dcim.models import Device, Interface, Site +from dcim.forms.mixins import ScopedImportForm from ipam.choices import * from ipam.constants import * from ipam.models import * @@ -29,6 +30,8 @@ __all__ = ( 'ServiceTemplateImportForm', 'VLANImportForm', 'VLANGroupImportForm', + 'VLANTranslationPolicyImportForm', + 'VLANTranslationRuleImportForm', 'VRFImportForm', ) @@ -152,7 +155,7 @@ class RoleImportForm(NetBoxModelImportForm): fields = ('name', 'slug', 'weight', 'description', 'tags') -class PrefixImportForm(NetBoxModelImportForm): +class PrefixImportForm(ScopedImportForm, NetBoxModelImportForm): vrf = CSVModelChoiceField( label=_('VRF'), queryset=VRF.objects.all(), @@ -167,13 +170,6 @@ class PrefixImportForm(NetBoxModelImportForm): to_field_name='name', help_text=_('Assigned tenant') ) - site = CSVModelChoiceField( - label=_('Site'), - queryset=Site.objects.all(), - required=False, - to_field_name='name', - help_text=_('Assigned site') - ) vlan_group = CSVModelChoiceField( label=_('VLAN group'), queryset=VLANGroup.objects.all(), @@ -204,9 +200,12 @@ class PrefixImportForm(NetBoxModelImportForm): class Meta: model = Prefix fields = ( - 'prefix', 'vrf', 'tenant', 'site', 'vlan_group', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', - 'description', 'comments', 'tags', + 'prefix', 'vrf', 'tenant', 'vlan_group', 'vlan', 'status', 'role', 'scope_type', 'scope_id', 'is_pool', + 'mark_utilized', 'description', 'comments', 'tags', ) + labels = { + 'scope_id': _('Scope ID'), + } def __init__(self, data=None, *args, **kwargs): super().__init__(data, *args, **kwargs) @@ -458,10 +457,46 @@ class VLANImportForm(NetBoxModelImportForm): to_field_name='name', help_text=_('Functional role') ) + qinq_role = CSVChoiceField( + label=_('Q-in-Q role'), + choices=VLANQinQRoleChoices, + required=False, + help_text=_('Operational status') + ) + qinq_svlan = CSVModelChoiceField( + label=_('Q-in-Q SVLAN'), + queryset=VLAN.objects.all(), + required=False, + to_field_name='vid', + help_text=_("Service VLAN (for Q-in-Q/802.1ad customer VLANs)") + ) class Meta: model = VLAN - fields = ('site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'comments', 'tags') + fields = ( + 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'qinq_role', 'qinq_svlan', + 'comments', 'tags', + ) + + +class VLANTranslationPolicyImportForm(NetBoxModelImportForm): + + class Meta: + model = VLANTranslationPolicy + fields = ('name', 'description', 'tags') + + +class VLANTranslationRuleImportForm(NetBoxModelImportForm): + policy = CSVModelChoiceField( + label=_('Policy'), + queryset=VLANTranslationPolicy.objects.all(), + to_field_name='name', + help_text=_('VLAN translation policy') + ) + + class Meta: + model = VLANTranslationRule + fields = ('policy', 'local_vid', 'remote_vid') class ServiceTemplateImportForm(NetBoxModelImportForm): diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py index a32694321..3f951512b 100644 --- a/netbox/ipam/forms/filtersets.py +++ b/netbox/ipam/forms/filtersets.py @@ -28,6 +28,8 @@ __all__ = ( 'ServiceTemplateFilterForm', 'VLANFilterForm', 'VLANGroupFilterForm', + 'VLANTranslationPolicyFilterForm', + 'VLANTranslationRuleFilterForm', 'VRFFilterForm', ) @@ -170,7 +172,7 @@ class PrefixFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): ), FieldSet('vlan_id', name=_('VLAN Assignment')), FieldSet('vrf_id', 'present_in_vrf_id', name=_('VRF')), - FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')), + FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Scope')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), ) mask_length__lte = forms.IntegerField( @@ -224,12 +226,13 @@ class PrefixFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), required=False, - null_option='None', - query_params={ - 'region_id': '$region_id' - }, label=_('Site') ) + location_id = DynamicModelMultipleChoiceField( + queryset=Location.objects.all(), + required=False, + label=_('Location') + ) role_id = DynamicModelMultipleChoiceField( queryset=Role.objects.all(), required=False, @@ -460,12 +463,50 @@ class VLANGroupFilterForm(NetBoxModelFilterSetForm): tag = TagFilterField(model) +class VLANTranslationPolicyFilterForm(NetBoxModelFilterSetForm): + model = VLANTranslationPolicy + fieldsets = ( + FieldSet('q', 'filter_id', 'tag'), + FieldSet('name', name=_('Attributes')), + ) + name = forms.CharField( + required=False, + label=_('Name') + ) + tag = TagFilterField(model) + + +class VLANTranslationRuleFilterForm(NetBoxModelFilterSetForm): + model = VLANTranslationRule + fieldsets = ( + FieldSet('q', 'filter_id', 'tag'), + FieldSet('policy_id', 'local_vid', 'remote_vid', name=_('Attributes')), + ) + tag = TagFilterField(model) + policy_id = DynamicModelMultipleChoiceField( + queryset=VLANTranslationPolicy.objects.all(), + required=False, + label=_('VLAN Translation Policy') + ) + local_vid = forms.IntegerField( + min_value=1, + required=False, + label=_('Local VLAN ID') + ) + remote_vid = forms.IntegerField( + min_value=1, + required=False, + label=_('Remote VLAN ID') + ) + + class VLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): model = VLAN fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')), FieldSet('group_id', 'status', 'role_id', 'vid', 'l2vpn_id', name=_('Attributes')), + FieldSet('qinq_role', 'qinq_svlan_id', name=_('Q-in-Q/802.1ad')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), ) selector_fields = ('filter_id', 'q', 'site_id') @@ -512,6 +553,17 @@ class VLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): required=False, label=_('VLAN ID') ) + qinq_role = forms.MultipleChoiceField( + label=_('Q-in-Q role'), + choices=VLANQinQRoleChoices, + required=False + ) + qinq_svlan_id = DynamicModelMultipleChoiceField( + queryset=VLAN.objects.all(), + required=False, + null_option='None', + label=_('Q-in-Q SVLAN') + ) l2vpn_id = DynamicModelMultipleChoiceField( queryset=L2VPN.objects.all(), required=False, diff --git a/netbox/ipam/forms/model_forms.py b/netbox/ipam/forms/model_forms.py index 156e7c435..094da3007 100644 --- a/netbox/ipam/forms/model_forms.py +++ b/netbox/ipam/forms/model_forms.py @@ -4,6 +4,7 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.utils.translation import gettext_lazy as _ from dcim.models import Device, Interface, Site +from dcim.forms.mixins import ScopedForm from ipam.choices import * from ipam.constants import * from ipam.formfields import IPNetworkFormField @@ -41,6 +42,8 @@ __all__ = ( 'ServiceTemplateForm', 'VLANForm', 'VLANGroupForm', + 'VLANTranslationPolicyForm', + 'VLANTranslationRuleForm', 'VRFForm', ) @@ -106,7 +109,8 @@ class RIRForm(NetBoxModelForm): class AggregateForm(TenancyForm, NetBoxModelForm): rir = DynamicModelChoiceField( queryset=RIR.objects.all(), - label=_('RIR') + label=_('RIR'), + quick_add=True ) comments = CommentField() @@ -129,6 +133,7 @@ class ASNRangeForm(TenancyForm, NetBoxModelForm): rir = DynamicModelChoiceField( queryset=RIR.objects.all(), label=_('RIR'), + quick_add=True ) slug = SlugField() fieldsets = ( @@ -147,6 +152,7 @@ class ASNForm(TenancyForm, NetBoxModelForm): rir = DynamicModelChoiceField( queryset=RIR.objects.all(), label=_('RIR'), + quick_add=True ) sites = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -195,19 +201,12 @@ class RoleForm(NetBoxModelForm): ] -class PrefixForm(TenancyForm, NetBoxModelForm): +class PrefixForm(TenancyForm, ScopedForm, NetBoxModelForm): vrf = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, label=_('VRF') ) - site = DynamicModelChoiceField( - label=_('Site'), - queryset=Site.objects.all(), - required=False, - selector=True, - null_option='None' - ) vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, @@ -220,7 +219,8 @@ class PrefixForm(TenancyForm, NetBoxModelForm): role = DynamicModelChoiceField( label=_('Role'), queryset=Role.objects.all(), - required=False + required=False, + quick_add=True ) comments = CommentField() @@ -228,15 +228,16 @@ class PrefixForm(TenancyForm, NetBoxModelForm): FieldSet( 'prefix', 'status', 'vrf', 'role', 'is_pool', 'mark_utilized', 'description', 'tags', name=_('Prefix') ), - FieldSet('site', 'vlan', name=_('Site/VLAN Assignment')), + FieldSet('scope_type', 'scope', name=_('Scope')), + FieldSet('vlan', name=_('VLAN Assignment')), FieldSet('tenant_group', 'tenant', name=_('Tenancy')), ) class Meta: model = Prefix fields = [ - 'prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'tenant_group', 'tenant', - 'description', 'comments', 'tags', + 'prefix', 'vrf', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'scope_type', 'tenant_group', + 'tenant', 'description', 'comments', 'tags', ] @@ -249,7 +250,8 @@ class IPRangeForm(TenancyForm, NetBoxModelForm): role = DynamicModelChoiceField( label=_('Role'), queryset=Role.objects.all(), - required=False + required=False, + quick_add=True ) comments = CommentField() @@ -385,7 +387,12 @@ class IPAddressForm(TenancyForm, NetBoxModelForm): }) elif selected_objects: assigned_object = self.cleaned_data[selected_objects[0]] - if self.instance.pk and self.instance.assigned_object and self.cleaned_data['primary_for_parent'] and assigned_object != self.instance.assigned_object: + if ( + self.instance.pk and + self.instance.assigned_object and + self.cleaned_data['primary_for_parent'] and + assigned_object != self.instance.assigned_object + ): raise ValidationError( _("Cannot reassign IP address while it is designated as the primary IP for the parent object") ) @@ -642,15 +649,55 @@ class VLANForm(TenancyForm, NetBoxModelForm): role = DynamicModelChoiceField( label=_('Role'), queryset=Role.objects.all(), - required=False + required=False, + quick_add=True + ) + qinq_svlan = DynamicModelChoiceField( + label=_('Q-in-Q SVLAN'), + queryset=VLAN.objects.all(), + required=False, + query_params={ + 'qinq_role': VLANQinQRoleChoices.ROLE_SERVICE, + } ) comments = CommentField() class Meta: model = VLAN fields = [ - 'site', 'group', 'vid', 'name', 'status', 'role', 'tenant_group', 'tenant', 'description', 'comments', - 'tags', + 'site', 'group', 'vid', 'name', 'status', 'role', 'tenant_group', 'tenant', 'qinq_role', 'qinq_svlan', + 'description', 'comments', 'tags', + ] + + +class VLANTranslationPolicyForm(NetBoxModelForm): + + fieldsets = ( + FieldSet('name', 'description', 'tags', name=_('VLAN Translation Policy')), + ) + + class Meta: + model = VLANTranslationPolicy + fields = [ + 'name', 'description', 'tags', + ] + + +class VLANTranslationRuleForm(NetBoxModelForm): + policy = DynamicModelChoiceField( + label=_('Policy'), + queryset=VLANTranslationPolicy.objects.all(), + selector=True + ) + + fieldsets = ( + FieldSet('policy', 'local_vid', 'remote_vid', 'description', 'tags', name=_('VLAN Translation Rule')), + ) + + class Meta: + model = VLANTranslationRule + fields = [ + 'policy', 'local_vid', 'remote_vid', 'description', 'tags', ] diff --git a/netbox/ipam/graphql/filters.py b/netbox/ipam/graphql/filters.py index 5f6602416..1b0e0133b 100644 --- a/netbox/ipam/graphql/filters.py +++ b/netbox/ipam/graphql/filters.py @@ -19,6 +19,8 @@ __all__ = ( 'ServiceTemplateFilter', 'VLANFilter', 'VLANGroupFilter', + 'VLANTranslationPolicyFilter', + 'VLANTranslationRuleFilter', 'VRFFilter', ) @@ -113,6 +115,18 @@ class VLANGroupFilter(BaseFilterMixin): pass +@strawberry_django.filter(models.VLANTranslationPolicy, lookups=True) +@autotype_decorator(filtersets.VLANTranslationPolicyFilterSet) +class VLANTranslationPolicyFilter(BaseFilterMixin): + pass + + +@strawberry_django.filter(models.VLANTranslationRule, lookups=True) +@autotype_decorator(filtersets.VLANTranslationRuleFilterSet) +class VLANTranslationRuleFilter(BaseFilterMixin): + pass + + @strawberry_django.filter(models.VRF, lookups=True) @autotype_decorator(filtersets.VRFFilterSet) class VRFFilter(BaseFilterMixin): diff --git a/netbox/ipam/graphql/schema.py b/netbox/ipam/graphql/schema.py index 072f8cbcd..5fcf78ea9 100644 --- a/netbox/ipam/graphql/schema.py +++ b/netbox/ipam/graphql/schema.py @@ -53,5 +53,11 @@ class IPAMQuery: vlan_group: VLANGroupType = strawberry_django.field() vlan_group_list: List[VLANGroupType] = strawberry_django.field() + vlan_translation_policy: VLANTranslationPolicyType = strawberry_django.field() + vlan_translation_policy_list: List[VLANTranslationPolicyType] = strawberry_django.field() + + vlan_translation_rule: VLANTranslationRuleType = strawberry_django.field() + vlan_translation_rule_list: List[VLANTranslationRuleType] = strawberry_django.field() + vrf: VRFType = strawberry_django.field() vrf_list: List[VRFType] = strawberry_django.field() diff --git a/netbox/ipam/graphql/types.py b/netbox/ipam/graphql/types.py index 46d45816e..e6ecca984 100644 --- a/netbox/ipam/graphql/types.py +++ b/netbox/ipam/graphql/types.py @@ -27,6 +27,8 @@ __all__ = ( 'ServiceTemplateType', 'VLANType', 'VLANGroupType', + 'VLANTranslationPolicyType', + 'VLANTranslationRuleType', 'VRFType', ) @@ -152,17 +154,25 @@ class IPRangeType(NetBoxObjectType): @strawberry_django.type( models.Prefix, - fields='__all__', + exclude=('scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'), filters=PrefixFilter ) class PrefixType(NetBoxObjectType, BaseIPAddressFamilyType): prefix: str - site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] | None vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None vlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None role: Annotated["RoleType", strawberry.lazy('ipam.graphql.types')] | None + @strawberry_django.field + def scope(self) -> Annotated[Union[ + Annotated["LocationType", strawberry.lazy('dcim.graphql.types')], + Annotated["RegionType", strawberry.lazy('dcim.graphql.types')], + Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')], + Annotated["SiteType", strawberry.lazy('dcim.graphql.types')], + ], strawberry.union("PrefixScopeType")] | None: + return self.scope + @strawberry_django.type( models.RIR, @@ -226,7 +236,7 @@ class ServiceTemplateType(NetBoxObjectType): @strawberry_django.type( models.VLAN, - fields='__all__', + exclude=('qinq_svlan',), filters=VLANFilter ) class VLANType(NetBoxObjectType): @@ -242,6 +252,10 @@ class VLANType(NetBoxObjectType): interfaces_as_tagged: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]] vminterfaces_as_tagged: List[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]] + @strawberry_django.field + def qinq_svlan(self) -> Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None: + return self.qinq_svlan + @strawberry_django.type( models.VLANGroup, @@ -266,6 +280,27 @@ class VLANGroupType(OrganizationalObjectType): return self.scope +@strawberry_django.type( + models.VLANTranslationPolicy, + fields='__all__', + filters=VLANTranslationPolicyFilter +) +class VLANTranslationPolicyType(NetBoxObjectType): + rules: List[Annotated["VLANTranslationRuleType", strawberry.lazy('ipam.graphql.types')]] + + +@strawberry_django.type( + models.VLANTranslationRule, + fields='__all__', + filters=VLANTranslationRuleFilter +) +class VLANTranslationRuleType(NetBoxObjectType): + policy: Annotated[ + "VLANTranslationPolicyType", + strawberry.lazy('ipam.graphql.types') + ] = strawberry_django.field(select_related=["policy"]) + + @strawberry_django.type( models.VRF, fields='__all__', diff --git a/netbox/ipam/lookups.py b/netbox/ipam/lookups.py index c6abb5a26..c493b7876 100644 --- a/netbox/ipam/lookups.py +++ b/netbox/ipam/lookups.py @@ -108,8 +108,8 @@ class NetIn(Lookup): return self.rhs def as_sql(self, qn, connection): - lhs, lhs_params = self.process_lhs(qn, connection) - rhs, rhs_params = self.process_rhs(qn, connection) + lhs = self.process_lhs(qn, connection)[0] + rhs_params = self.process_rhs(qn, connection)[1] with_mask, without_mask = [], [] for address in rhs_params[0]: if '/' in address: diff --git a/netbox/ipam/migrations/0001_squashed.py b/netbox/ipam/migrations/0001_squashed.py index bef36e698..896d7c4c9 100644 --- a/netbox/ipam/migrations/0001_squashed.py +++ b/netbox/ipam/migrations/0001_squashed.py @@ -9,7 +9,6 @@ import taggit.managers class Migration(migrations.Migration): - initial = True dependencies = [ @@ -50,7 +49,23 @@ class Migration(migrations.Migration): ('status', models.CharField(default='active', max_length=50)), ('role', models.CharField(blank=True, max_length=50)), ('assigned_object_id', models.PositiveIntegerField(blank=True, null=True)), - ('dns_name', models.CharField(blank=True, max_length=255, validators=[django.core.validators.RegexValidator(code='invalid', message='Only alphanumeric characters, asterisks, hyphens, periods, and underscores are allowed in DNS names', regex='^([0-9A-Za-z_-]+|\\*)(\\.[0-9A-Za-z_-]+)*\\.?$')])), + ( + 'dns_name', + models.CharField( + blank=True, + max_length=255, + validators=[ + django.core.validators.RegexValidator( + code='invalid', + message=( + 'Only alphanumeric characters, asterisks, hyphens, periods, and underscores are ' + 'allowed in DNS names' + ), + regex='^([0-9A-Za-z_-]+|\\*)(\\.[0-9A-Za-z_-]+)*\\.?$', + ) + ], + ), + ), ('description', models.CharField(blank=True, max_length=200)), ], options={ @@ -73,7 +88,11 @@ class Migration(migrations.Migration): ], options={ 'verbose_name_plural': 'prefixes', - 'ordering': (django.db.models.expressions.OrderBy(django.db.models.expressions.F('vrf'), nulls_first=True), 'prefix', 'pk'), + 'ordering': ( + django.db.models.expressions.OrderBy(django.db.models.expressions.F('vrf'), nulls_first=True), + 'prefix', + 'pk', + ), }, ), migrations.CreateModel( @@ -135,10 +154,25 @@ class Migration(migrations.Migration): ('rd', models.CharField(blank=True, max_length=21, null=True, unique=True)), ('enforce_unique', models.BooleanField(default=True)), ('description', models.CharField(blank=True, max_length=200)), - ('export_targets', models.ManyToManyField(blank=True, related_name='exporting_vrfs', to='ipam.RouteTarget')), - ('import_targets', models.ManyToManyField(blank=True, related_name='importing_vrfs', to='ipam.RouteTarget')), + ( + 'export_targets', + models.ManyToManyField(blank=True, related_name='exporting_vrfs', to='ipam.RouteTarget'), + ), + ( + 'import_targets', + models.ManyToManyField(blank=True, related_name='importing_vrfs', to='ipam.RouteTarget'), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), - ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vrfs', to='tenancy.tenant')), + ( + 'tenant', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='vrfs', + to='tenancy.tenant', + ), + ), ], options={ 'verbose_name': 'VRF', @@ -157,7 +191,21 @@ class Migration(migrations.Migration): ('slug', models.SlugField(max_length=100)), ('scope_id', models.PositiveBigIntegerField(blank=True, null=True)), ('description', models.CharField(blank=True, max_length=200)), - ('scope_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(('model__in', ('region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster'))), null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), + ( + 'scope_type', + models.ForeignKey( + blank=True, + limit_choices_to=models.Q( + ( + 'model__in', + ('region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster'), + ) + ), + null=True, + on_delete=django.db.models.deletion.CASCADE, + to='contenttypes.contenttype', + ), + ), ], options={ 'verbose_name': 'VLAN group', @@ -172,15 +220,59 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)), ('id', models.BigAutoField(primary_key=True, serialize=False)), - ('vid', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(4094)])), + ( + 'vid', + models.PositiveSmallIntegerField( + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(4094), + ] + ), + ), ('name', models.CharField(max_length=64)), ('status', models.CharField(default='active', max_length=50)), ('description', models.CharField(blank=True, max_length=200)), - ('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlans', to='ipam.vlangroup')), - ('role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vlans', to='ipam.role')), - ('site', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlans', to='dcim.site')), + ( + 'group', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='vlans', + to='ipam.vlangroup', + ), + ), + ( + 'role', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='vlans', + to='ipam.role', + ), + ), + ( + 'site', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='vlans', + to='dcim.site', + ), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), - ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlans', to='tenancy.tenant')), + ( + 'tenant', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='vlans', + to='tenancy.tenant', + ), + ), ], options={ 'verbose_name': 'VLAN', @@ -197,9 +289,29 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=100)), ('protocol', models.CharField(max_length=50)), - ('ports', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(65535)]), size=None)), + ( + 'ports', + django.contrib.postgres.fields.ArrayField( + base_field=models.PositiveIntegerField( + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(65535), + ] + ), + size=None, + ), + ), ('description', models.CharField(blank=True, max_length=200)), - ('device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='services', to='dcim.device')), + ( + 'device', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='services', + to='dcim.device', + ), + ), ('ipaddresses', models.ManyToManyField(blank=True, related_name='services', to='ipam.IPAddress')), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), ], diff --git a/netbox/ipam/migrations/0002_squashed_0046.py b/netbox/ipam/migrations/0002_squashed_0046.py index 06bcd8741..6c03753d8 100644 --- a/netbox/ipam/migrations/0002_squashed_0046.py +++ b/netbox/ipam/migrations/0002_squashed_0046.py @@ -4,7 +4,6 @@ import taggit.managers class Migration(migrations.Migration): - dependencies = [ ('dcim', '0003_auto_20160628_1721'), ('virtualization', '0001_virtualization'), @@ -66,7 +65,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='service', name='virtual_machine', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='services', to='virtualization.virtualmachine'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='services', + to='virtualization.virtualmachine', + ), ), migrations.AddField( model_name='routetarget', @@ -76,17 +81,35 @@ class Migration(migrations.Migration): migrations.AddField( model_name='routetarget', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='route_targets', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='route_targets', + to='tenancy.tenant', + ), ), migrations.AddField( model_name='prefix', name='role', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='prefixes', to='ipam.role'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='prefixes', + to='ipam.role', + ), ), migrations.AddField( model_name='prefix', name='site', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='prefixes', to='dcim.site'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='prefixes', + to='dcim.site', + ), ), migrations.AddField( model_name='prefix', @@ -96,27 +119,64 @@ class Migration(migrations.Migration): migrations.AddField( model_name='prefix', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='prefixes', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='prefixes', + to='tenancy.tenant', + ), ), migrations.AddField( model_name='prefix', name='vlan', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='prefixes', to='ipam.vlan'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='prefixes', + to='ipam.vlan', + ), ), migrations.AddField( model_name='prefix', name='vrf', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='prefixes', to='ipam.vrf'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='prefixes', + to='ipam.vrf', + ), ), migrations.AddField( model_name='ipaddress', name='assigned_object_type', - field=models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'dcim'), ('model', 'interface')), models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), _connector='OR')), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + limit_choices_to=models.Q( + models.Q( + models.Q(('app_label', 'dcim'), ('model', 'interface')), + models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), + _connector='OR', + ) + ), + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.AddField( model_name='ipaddress', name='nat_inside', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='nat_outside', to='ipam.ipaddress'), + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='nat_outside', + to='ipam.ipaddress', + ), ), migrations.AddField( model_name='ipaddress', @@ -126,17 +186,31 @@ class Migration(migrations.Migration): migrations.AddField( model_name='ipaddress', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='ip_addresses', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='ip_addresses', + to='tenancy.tenant', + ), ), migrations.AddField( model_name='ipaddress', name='vrf', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='ip_addresses', to='ipam.vrf'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='ip_addresses', + to='ipam.vrf', + ), ), migrations.AddField( model_name='aggregate', name='rir', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='aggregates', to='ipam.rir'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='aggregates', to='ipam.rir' + ), ), migrations.AddField( model_name='aggregate', @@ -146,7 +220,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='aggregate', name='tenant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='aggregates', to='tenancy.tenant'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='aggregates', + to='tenancy.tenant', + ), ), migrations.AlterUniqueTogether( name='vlangroup', diff --git a/netbox/ipam/migrations/0047_squashed_0053.py b/netbox/ipam/migrations/0047_squashed_0053.py index 470261316..a05d0cb81 100644 --- a/netbox/ipam/migrations/0047_squashed_0053.py +++ b/netbox/ipam/migrations/0047_squashed_0053.py @@ -8,7 +8,6 @@ import utilities.json class Migration(migrations.Migration): - replaces = [ ('ipam', '0047_prefix_depth_children'), ('ipam', '0048_prefix_populate_depth_children'), @@ -16,7 +15,7 @@ class Migration(migrations.Migration): ('ipam', '0050_iprange'), ('ipam', '0051_extend_tag_support'), ('ipam', '0052_fhrpgroup'), - ('ipam', '0053_asn_model') + ('ipam', '0053_asn_model'), ] dependencies = [ @@ -47,17 +46,47 @@ class Migration(migrations.Migration): fields=[ ('created', models.DateField(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)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('start_address', ipam.fields.IPAddressField()), ('end_address', ipam.fields.IPAddressField()), ('size', models.PositiveIntegerField(editable=False)), ('status', models.CharField(default='active', max_length=50)), ('description', models.CharField(blank=True, max_length=200)), - ('role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ip_ranges', to='ipam.role')), + ( + 'role', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='ip_ranges', + to='ipam.role', + ), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), - ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='ip_ranges', to='tenancy.tenant')), - ('vrf', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='ip_ranges', to='ipam.vrf')), + ( + 'tenant', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='ip_ranges', + to='tenancy.tenant', + ), + ), + ( + 'vrf', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='ip_ranges', + to='ipam.vrf', + ), + ), ], options={ 'verbose_name': 'IP range', @@ -85,7 +114,10 @@ class Migration(migrations.Migration): fields=[ ('created', models.DateField(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)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('group_id', models.PositiveSmallIntegerField()), ('protocol', models.CharField(max_length=50)), @@ -102,7 +134,21 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='ipaddress', name='assigned_object_type', - field=models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'dcim'), ('model', 'interface')), models.Q(('app_label', 'ipam'), ('model', 'fhrpgroup')), models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), _connector='OR')), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype'), + field=models.ForeignKey( + blank=True, + limit_choices_to=models.Q( + models.Q( + models.Q(('app_label', 'dcim'), ('model', 'interface')), + models.Q(('app_label', 'ipam'), ('model', 'fhrpgroup')), + models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), + _connector='OR', + ) + ), + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), ), migrations.CreateModel( name='FHRPGroupAssignment', @@ -111,9 +157,20 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('interface_id', models.PositiveIntegerField()), - ('priority', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(255)])), + ( + 'priority', + models.PositiveSmallIntegerField( + validators=[ + django.core.validators.MinValueValidator(0), + django.core.validators.MaxValueValidator(255), + ] + ), + ), ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ipam.fhrpgroup')), - ('interface_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), + ( + 'interface_type', + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype'), + ), ], options={ 'verbose_name': 'FHRP group assignment', @@ -126,13 +183,28 @@ class Migration(migrations.Migration): fields=[ ('created', models.DateField(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)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), ('id', models.BigAutoField(primary_key=True, serialize=False)), ('asn', ipam.fields.ASNField(unique=True)), ('description', models.CharField(blank=True, max_length=200)), - ('rir', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='asns', to='ipam.rir')), + ( + 'rir', + models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='asns', to='ipam.rir'), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), - ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='asns', to='tenancy.tenant')), + ( + 'tenant', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='asns', + to='tenancy.tenant', + ), + ), ], options={ 'verbose_name': 'ASN', diff --git a/netbox/ipam/migrations/0054_squashed_0067.py b/netbox/ipam/migrations/0054_squashed_0067.py index 40073ca29..929a27fda 100644 --- a/netbox/ipam/migrations/0054_squashed_0067.py +++ b/netbox/ipam/migrations/0054_squashed_0067.py @@ -10,7 +10,6 @@ import utilities.json class Migration(migrations.Migration): - replaces = [ ('ipam', '0054_vlangroup_min_max_vids'), ('ipam', '0055_servicetemplate'), @@ -25,7 +24,7 @@ class Migration(migrations.Migration): ('ipam', '0064_clear_search_cache'), ('ipam', '0065_asnrange'), ('ipam', '0066_iprange_mark_utilized'), - ('ipam', '0067_ipaddress_index_host') + ('ipam', '0067_ipaddress_index_host'), ] dependencies = [ @@ -40,12 +39,24 @@ class Migration(migrations.Migration): migrations.AddField( model_name='vlangroup', name='max_vid', - field=models.PositiveSmallIntegerField(default=4094, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(4094)]), + field=models.PositiveSmallIntegerField( + default=4094, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(4094), + ], + ), ), migrations.AddField( model_name='vlangroup', name='min_vid', - field=models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(4094)]), + field=models.PositiveSmallIntegerField( + default=1, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(4094), + ], + ), ), migrations.AlterField( model_name='aggregate', @@ -187,10 +198,24 @@ class Migration(migrations.Migration): fields=[ ('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)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('protocol', models.CharField(max_length=50)), - ('ports', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(65535)]), size=None)), + ( + 'ports', + django.contrib.postgres.fields.ArrayField( + base_field=models.PositiveIntegerField( + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(65535), + ] + ), + size=None, + ), + ), ('description', models.CharField(blank=True, max_length=200)), ('name', models.CharField(max_length=100, unique=True)), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), @@ -217,7 +242,13 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='ipaddress', name='nat_inside', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='nat_outside', to='ipam.ipaddress'), + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='nat_outside', + to='ipam.ipaddress', + ), ), migrations.CreateModel( name='L2VPN', @@ -225,16 +256,34 @@ class Migration(migrations.Migration): ('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)), + ( + '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)), ('type', models.CharField(max_length=50)), ('identifier', models.BigIntegerField(blank=True, null=True)), ('description', models.CharField(blank=True, max_length=200)), - ('export_targets', models.ManyToManyField(blank=True, related_name='exporting_l2vpns', to='ipam.routetarget')), - ('import_targets', models.ManyToManyField(blank=True, related_name='importing_l2vpns', to='ipam.routetarget')), + ( + 'export_targets', + models.ManyToManyField(blank=True, related_name='exporting_l2vpns', to='ipam.routetarget'), + ), + ( + 'import_targets', + models.ManyToManyField(blank=True, related_name='importing_l2vpns', to='ipam.routetarget'), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), - ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='l2vpns', to='tenancy.tenant')), + ( + 'tenant', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='l2vpns', + to='tenancy.tenant', + ), + ), ], options={ 'verbose_name': 'L2VPN', @@ -247,10 +296,33 @@ class Migration(migrations.Migration): ('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)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), ('assigned_object_id', models.PositiveBigIntegerField()), - ('assigned_object_type', models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'dcim'), ('model', 'interface')), models.Q(('app_label', 'ipam'), ('model', 'vlan')), models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')), - ('l2vpn', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='ipam.l2vpn')), + ( + 'assigned_object_type', + models.ForeignKey( + limit_choices_to=models.Q( + models.Q( + models.Q(('app_label', 'dcim'), ('model', 'interface')), + models.Q(('app_label', 'ipam'), ('model', 'vlan')), + models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), + _connector='OR', + ) + ), + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), + ), + ( + 'l2vpn', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='ipam.l2vpn' + ), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), ], options={ @@ -260,7 +332,9 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='l2vpntermination', - constraint=models.UniqueConstraint(fields=('assigned_object_type', 'assigned_object_id'), name='ipam_l2vpntermination_assigned_object'), + constraint=models.UniqueConstraint( + fields=('assigned_object_type', 'assigned_object_id'), name='ipam_l2vpntermination_assigned_object' + ), ), migrations.AddField( model_name='fhrpgroup', @@ -281,7 +355,10 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='fhrpgroupassignment', - constraint=models.UniqueConstraint(fields=('interface_type', 'interface_id', 'group'), name='ipam_fhrpgroupassignment_unique_interface_group'), + constraint=models.UniqueConstraint( + fields=('interface_type', 'interface_id', 'group'), + name='ipam_fhrpgroupassignment_unique_interface_group', + ), ), migrations.AddConstraint( model_name='vlan', @@ -293,11 +370,15 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='vlangroup', - constraint=models.UniqueConstraint(fields=('scope_type', 'scope_id', 'name'), name='ipam_vlangroup_unique_scope_name'), + constraint=models.UniqueConstraint( + fields=('scope_type', 'scope_id', 'name'), name='ipam_vlangroup_unique_scope_name' + ), ), migrations.AddConstraint( model_name='vlangroup', - constraint=models.UniqueConstraint(fields=('scope_type', 'scope_id', 'slug'), name='ipam_vlangroup_unique_scope_slug'), + constraint=models.UniqueConstraint( + fields=('scope_type', 'scope_id', 'slug'), name='ipam_vlangroup_unique_scope_slug' + ), ), migrations.AddField( model_name='aggregate', @@ -365,15 +446,32 @@ class Migration(migrations.Migration): ('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)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), ('description', models.CharField(blank=True, max_length=200)), ('name', models.CharField(max_length=100, unique=True)), ('slug', models.SlugField(max_length=100, unique=True)), ('start', ipam.fields.ASNField()), ('end', ipam.fields.ASNField()), - ('rir', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='asn_ranges', to='ipam.rir')), + ( + 'rir', + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name='asn_ranges', to='ipam.rir' + ), + ), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), - ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='asn_ranges', to='tenancy.tenant')), + ( + 'tenant', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='asn_ranges', + to='tenancy.tenant', + ), + ), ], options={ 'verbose_name': 'ASN range', @@ -388,6 +486,11 @@ class Migration(migrations.Migration): ), migrations.AddIndex( model_name='ipaddress', - index=models.Index(django.db.models.functions.comparison.Cast(ipam.lookups.Host('address'), output_field=ipam.fields.IPAddressField()), name='ipam_ipaddress_host'), + index=models.Index( + django.db.models.functions.comparison.Cast( + ipam.lookups.Host('address'), output_field=ipam.fields.IPAddressField() + ), + name='ipam_ipaddress_host', + ), ), ] diff --git a/netbox/ipam/migrations/0068_move_l2vpn.py b/netbox/ipam/migrations/0068_move_l2vpn.py index b1a059de1..9240240bc 100644 --- a/netbox/ipam/migrations/0068_move_l2vpn.py +++ b/netbox/ipam/migrations/0068_move_l2vpn.py @@ -15,7 +15,6 @@ def update_content_types(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('ipam', '0067_ipaddress_index_host'), ] @@ -57,8 +56,5 @@ class Migration(migrations.Migration): ), ], ), - migrations.RunPython( - code=update_content_types, - reverse_code=migrations.RunPython.noop - ), + migrations.RunPython(code=update_content_types, reverse_code=migrations.RunPython.noop), ] diff --git a/netbox/ipam/migrations/0069_gfk_indexes.py b/netbox/ipam/migrations/0069_gfk_indexes.py index 75c016102..d7ce48e35 100644 --- a/netbox/ipam/migrations/0069_gfk_indexes.py +++ b/netbox/ipam/migrations/0069_gfk_indexes.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('ipam', '0068_move_l2vpn'), ] @@ -16,7 +15,9 @@ class Migration(migrations.Migration): ), migrations.AddIndex( model_name='ipaddress', - index=models.Index(fields=['assigned_object_type', 'assigned_object_id'], name='ipam_ipaddr_assigne_890ab8_idx'), + index=models.Index( + fields=['assigned_object_type', 'assigned_object_id'], name='ipam_ipaddr_assigne_890ab8_idx' + ), ), migrations.AddIndex( model_name='vlangroup', diff --git a/netbox/ipam/migrations/0070_vlangroup_vlan_id_ranges.py b/netbox/ipam/migrations/0070_vlangroup_vlan_id_ranges.py index b01941401..133173234 100644 --- a/netbox/ipam/migrations/0070_vlangroup_vlan_id_ranges.py +++ b/netbox/ipam/migrations/0070_vlangroup_vlan_id_ranges.py @@ -12,15 +12,12 @@ def set_vid_ranges(apps, schema_editor): """ VLANGroup = apps.get_model('ipam', 'VLANGroup') for group in VLANGroup.objects.all(): - group.vid_ranges = [ - NumericRange(group.min_vid, group.max_vid, bounds='[]') - ] + group.vid_ranges = [NumericRange(group.min_vid, group.max_vid, bounds='[]')] group._total_vlan_ids = group.max_vid - group.min_vid + 1 group.save() class Migration(migrations.Migration): - dependencies = [ ('ipam', '0069_gfk_indexes'), ] @@ -32,7 +29,7 @@ class Migration(migrations.Migration): field=django.contrib.postgres.fields.ArrayField( base_field=django.contrib.postgres.fields.ranges.IntegerRangeField(), default=ipam.models.vlans.default_vid_ranges, - size=None + size=None, ), ), migrations.AddField( @@ -40,10 +37,7 @@ class Migration(migrations.Migration): name='_total_vlan_ids', field=models.PositiveBigIntegerField(default=4094), ), - migrations.RunPython( - code=set_vid_ranges, - reverse_code=migrations.RunPython.noop - ), + migrations.RunPython(code=set_vid_ranges, reverse_code=migrations.RunPython.noop), migrations.RemoveField( model_name='vlangroup', name='max_vid', diff --git a/netbox/ipam/migrations/0071_prefix_scope.py b/netbox/ipam/migrations/0071_prefix_scope.py new file mode 100644 index 000000000..2ab54d023 --- /dev/null +++ b/netbox/ipam/migrations/0071_prefix_scope.py @@ -0,0 +1,45 @@ +import django.db.models.deletion +from django.db import migrations, models + + +def copy_site_assignments(apps, schema_editor): + """ + Copy site ForeignKey values to the scope GFK. + """ + ContentType = apps.get_model('contenttypes', 'ContentType') + Prefix = apps.get_model('ipam', 'Prefix') + Site = apps.get_model('dcim', 'Site') + + Prefix.objects.filter(site__isnull=False).update( + scope_type=ContentType.objects.get_for_model(Site), scope_id=models.F('site_id') + ) + + +class Migration(migrations.Migration): + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('ipam', '0070_vlangroup_vlan_id_ranges'), + ] + + operations = [ + # Add the `scope` GenericForeignKey + migrations.AddField( + model_name='prefix', + name='scope_id', + field=models.PositiveBigIntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='prefix', + name='scope_type', + field=models.ForeignKey( + blank=True, + limit_choices_to=models.Q(('model__in', ('region', 'sitegroup', 'site', 'location'))), + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype', + ), + ), + # Copy over existing site assignments + migrations.RunPython(code=copy_site_assignments, reverse_code=migrations.RunPython.noop), + ] diff --git a/netbox/ipam/migrations/0072_prefix_cached_relations.py b/netbox/ipam/migrations/0072_prefix_cached_relations.py new file mode 100644 index 000000000..e4a789704 --- /dev/null +++ b/netbox/ipam/migrations/0072_prefix_cached_relations.py @@ -0,0 +1,61 @@ +import django.db.models.deletion +from django.db import migrations, models + + +def populate_denormalized_fields(apps, schema_editor): + """ + Copy site ForeignKey values to the scope GFK. + """ + Prefix = apps.get_model('ipam', 'Prefix') + + prefixes = Prefix.objects.filter(site__isnull=False).prefetch_related('site') + for prefix in prefixes: + prefix._region_id = prefix.site.region_id + prefix._site_group_id = prefix.site.group_id + prefix._site_id = prefix.site_id + # Note: Location cannot be set prior to migration + + Prefix.objects.bulk_update(prefixes, ['_region', '_site_group', '_site']) + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0193_poweroutlet_color'), + ('ipam', '0071_prefix_scope'), + ] + + operations = [ + migrations.AddField( + model_name='prefix', + name='_location', + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.location' + ), + ), + migrations.AddField( + model_name='prefix', + name='_region', + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.region' + ), + ), + migrations.AddField( + model_name='prefix', + name='_site', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.site'), + ), + migrations.AddField( + model_name='prefix', + name='_site_group', + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.sitegroup' + ), + ), + # Populate denormalized FK values + migrations.RunPython(code=populate_denormalized_fields, reverse_code=migrations.RunPython.noop), + # Delete the site ForeignKey + migrations.RemoveField( + model_name='prefix', + name='site', + ), + ] diff --git a/netbox/ipam/migrations/0073_charfield_null_choices.py b/netbox/ipam/migrations/0073_charfield_null_choices.py new file mode 100644 index 000000000..cfb764b46 --- /dev/null +++ b/netbox/ipam/migrations/0073_charfield_null_choices.py @@ -0,0 +1,32 @@ +from django.db import migrations, models + + +def set_null_values(apps, schema_editor): + """ + Replace empty strings with null values. + """ + FHRPGroup = apps.get_model('ipam', 'FHRPGroup') + IPAddress = apps.get_model('ipam', 'IPAddress') + + FHRPGroup.objects.filter(auth_type='').update(auth_type=None) + IPAddress.objects.filter(role='').update(role=None) + + +class Migration(migrations.Migration): + dependencies = [ + ('ipam', '0072_prefix_cached_relations'), + ] + + operations = [ + migrations.AlterField( + model_name='fhrpgroup', + name='auth_type', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AlterField( + model_name='ipaddress', + name='role', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.RunPython(code=set_null_values, reverse_code=migrations.RunPython.noop), + ] diff --git a/netbox/ipam/migrations/0074_vlantranslationpolicy_vlantranslationrule.py b/netbox/ipam/migrations/0074_vlantranslationpolicy_vlantranslationrule.py new file mode 100644 index 000000000..5a13f18e6 --- /dev/null +++ b/netbox/ipam/migrations/0074_vlantranslationpolicy_vlantranslationrule.py @@ -0,0 +1,97 @@ +# Generated by Django 5.0.9 on 2024-10-11 19:45 + +import django.core.validators +import django.db.models.deletion +import taggit.managers +import utilities.json +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('extras', '0121_customfield_related_object_filter'), + ('ipam', '0073_charfield_null_choices'), + ] + + operations = [ + migrations.CreateModel( + name='VLANTranslationPolicy', + 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), + ), + ('comments', models.TextField(blank=True)), + ('name', models.CharField(max_length=100, unique=True)), + ('description', models.CharField(blank=True, max_length=200)), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'verbose_name': 'VLAN translation policy', + 'verbose_name_plural': 'VLAN translation policies', + 'ordering': ('name',), + }, + ), + migrations.CreateModel( + name='VLANTranslationRule', + 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), + ), + ( + 'local_vid', + models.PositiveSmallIntegerField( + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(4094), + ] + ), + ), + ( + 'remote_vid', + models.PositiveSmallIntegerField( + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(4094), + ] + ), + ), + ( + 'policy', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='rules', + to='ipam.vlantranslationpolicy', + ), + ), + ('description', models.CharField(blank=True, max_length=200)), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'verbose_name': 'VLAN translation rule', + 'ordering': ( + 'policy', + 'local_vid', + ), + }, + ), + migrations.AddConstraint( + model_name='vlantranslationrule', + constraint=models.UniqueConstraint( + fields=('policy', 'local_vid'), name='ipam_vlantranslationrule_unique_policy_local_vid' + ), + ), + migrations.AddConstraint( + model_name='vlantranslationrule', + constraint=models.UniqueConstraint( + fields=('policy', 'remote_vid'), name='ipam_vlantranslationrule_unique_policy_remote_vid' + ), + ), + ] diff --git a/netbox/ipam/migrations/0075_vlan_qinq.py b/netbox/ipam/migrations/0075_vlan_qinq.py new file mode 100644 index 000000000..1e8f86c36 --- /dev/null +++ b/netbox/ipam/migrations/0075_vlan_qinq.py @@ -0,0 +1,35 @@ +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('ipam', '0074_vlantranslationpolicy_vlantranslationrule'), + ] + + operations = [ + migrations.AddField( + model_name='vlan', + name='qinq_role', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AddField( + model_name='vlan', + name='qinq_svlan', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='qinq_cvlans', + to='ipam.vlan', + ), + ), + migrations.AddConstraint( + model_name='vlan', + constraint=models.UniqueConstraint(fields=('qinq_svlan', 'vid'), name='ipam_vlan_unique_qinq_svlan_vid'), + ), + migrations.AddConstraint( + model_name='vlan', + constraint=models.UniqueConstraint(fields=('qinq_svlan', 'name'), name='ipam_vlan_unique_qinq_svlan_name'), + ), + ] diff --git a/netbox/ipam/migrations/0076_natural_ordering.py b/netbox/ipam/migrations/0076_natural_ordering.py new file mode 100644 index 000000000..f6c9e5ccb --- /dev/null +++ b/netbox/ipam/migrations/0076_natural_ordering.py @@ -0,0 +1,31 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('ipam', '0075_vlan_qinq'), + ('dcim', '0197_natural_sort_collation'), + ] + + operations = [ + migrations.AlterField( + model_name='asnrange', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=100, unique=True), + ), + migrations.AlterField( + model_name='routetarget', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=21, unique=True), + ), + migrations.AlterField( + model_name='vlangroup', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=100), + ), + migrations.AlterField( + model_name='vrf', + name='name', + field=models.CharField(db_collation='natural_sort', max_length=100), + ), + ] diff --git a/netbox/ipam/models/asns.py b/netbox/ipam/models/asns.py index eb47426b2..c1d251301 100644 --- a/netbox/ipam/models/asns.py +++ b/netbox/ipam/models/asns.py @@ -16,7 +16,8 @@ class ASNRange(OrganizationalModel): name = models.CharField( verbose_name=_('name'), max_length=100, - unique=True + unique=True, + db_collation="natural_sort" ) slug = models.SlugField( verbose_name=_('slug'), diff --git a/netbox/ipam/models/fhrp.py b/netbox/ipam/models/fhrp.py index 28bb37ef3..f5982853e 100644 --- a/netbox/ipam/models/fhrp.py +++ b/netbox/ipam/models/fhrp.py @@ -34,6 +34,7 @@ class FHRPGroup(PrimaryModel): max_length=50, choices=FHRPGroupAuthTypeChoices, blank=True, + null=True, verbose_name=_('authentication type') ) auth_key = models.CharField( diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index a540b5810..e1a8d91e3 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -8,6 +8,7 @@ from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ from core.models import ObjectType +from dcim.models.mixins import CachedScopeMixin from ipam.choices import * from ipam.constants import * from ipam.fields import IPNetworkField, IPAddressField @@ -197,23 +198,16 @@ class Role(OrganizationalModel): return self.name -class Prefix(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel): +class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, PrimaryModel): """ - A Prefix represents an IPv4 or IPv6 network, including mask length. Prefixes can optionally be assigned to Sites and - VRFs. A Prefix must be assigned a status and may optionally be assigned a used-define Role. A Prefix can also be - assigned to a VLAN where appropriate. + A Prefix represents an IPv4 or IPv6 network, including mask length. Prefixes can optionally be scoped to certain + areas and/or assigned to VRFs. A Prefix must be assigned a status and may optionally be assigned a used-define Role. + A Prefix can also be assigned to a VLAN where appropriate. """ prefix = IPNetworkField( verbose_name=_('prefix'), help_text=_('IPv4 or IPv6 network with mask') ) - site = models.ForeignKey( - to='dcim.Site', - on_delete=models.PROTECT, - related_name='prefixes', - blank=True, - null=True - ) vrf = models.ForeignKey( to='ipam.VRF', on_delete=models.PROTECT, @@ -275,7 +269,7 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel): objects = PrefixQuerySet.as_manager() clone_fields = ( - 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description', + 'scope_type', 'scope_id', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description', ) class Meta: @@ -323,6 +317,9 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel): # Clear host bits from prefix self.prefix = self.prefix.cidr + # Cache objects associated with the terminating object (for filtering) + self.cache_related_objects() + super().save(*args, **kwargs) @property @@ -421,7 +418,9 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel): available_ips = prefix - child_ips - netaddr.IPSet(child_ranges) # IPv6 /127's, pool, or IPv4 /31-/32 sets are fully usable - if (self.family == 6 and self.prefix.prefixlen >= 127) or self.is_pool or (self.family == 4 and self.prefix.prefixlen >= 31): + if (self.family == 6 and self.prefix.prefixlen >= 127) or self.is_pool or ( + self.family == 4 and self.prefix.prefixlen >= 31 + ): return available_ips if self.family == 4: @@ -564,15 +563,31 @@ class IPRange(ContactsMixin, PrimaryModel): }) # Check for overlapping ranges - overlapping_range = IPRange.objects.exclude(pk=self.pk).filter(vrf=self.vrf).filter( - Q(start_address__gte=self.start_address, start_address__lte=self.end_address) | # Starts inside - Q(end_address__gte=self.start_address, end_address__lte=self.end_address) | # Ends inside - Q(start_address__lte=self.start_address, end_address__gte=self.end_address) # Starts & ends outside - ).first() - if overlapping_range: + overlapping_ranges = ( + IPRange.objects.exclude(pk=self.pk) + .filter(vrf=self.vrf) + .filter( + # Starts inside + Q( + start_address__host__inet__gte=self.start_address.ip, + start_address__host__inet__lte=self.end_address.ip, + ) | + # Ends inside + Q( + end_address__host__inet__gte=self.start_address.ip, + end_address__host__inet__lte=self.end_address.ip, + ) | + # Starts & ends outside + Q( + start_address__host__inet__lte=self.start_address.ip, + end_address__host__inet__gte=self.end_address.ip, + ) + ) + ) + if overlapping_ranges.exists(): raise ValidationError( _("Defined addresses overlap with range {overlapping_range} in VRF {vrf}").format( - overlapping_range=overlapping_range, + overlapping_range=overlapping_ranges.first(), vrf=self.vrf )) @@ -722,6 +737,7 @@ class IPAddress(ContactsMixin, PrimaryModel): max_length=50, choices=IPAddressRoleChoices, blank=True, + null=True, help_text=_('The functional role of this IP') ) assigned_object_type = models.ForeignKey( @@ -868,10 +884,12 @@ class IPAddress(ContactsMixin, PrimaryModel): # can't use is_primary_ip as self.assigned_object might be changed is_primary = False - if self.family == 4 and hasattr(original_parent, 'primary_ip4') and original_parent.primary_ip4_id == self.pk: - is_primary = True - if self.family == 6 and hasattr(original_parent, 'primary_ip6') and original_parent.primary_ip6_id == self.pk: - is_primary = True + if self.family == 4 and hasattr(original_parent, 'primary_ip4'): + if original_parent.primary_ip4_id == self.pk: + is_primary = True + if self.family == 6 and hasattr(original_parent, 'primary_ip6'): + if original_parent.primary_ip6_id == self.pk: + is_primary = True if is_primary and (parent != original_parent): raise ValidationError( diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py index 23f7c41c7..4c7f191c9 100644 --- a/netbox/ipam/models/vlans.py +++ b/netbox/ipam/models/vlans.py @@ -10,13 +10,15 @@ from dcim.models import Interface from ipam.choices import * from ipam.constants import * from ipam.querysets import VLANQuerySet, VLANGroupQuerySet -from netbox.models import OrganizationalModel, PrimaryModel +from netbox.models import OrganizationalModel, PrimaryModel, NetBoxModel from utilities.data import check_ranges_overlap, ranges_to_string from virtualization.models import VMInterface __all__ = ( 'VLAN', 'VLANGroup', + 'VLANTranslationPolicy', + 'VLANTranslationRule', ) @@ -33,7 +35,8 @@ class VLANGroup(OrganizationalModel): """ name = models.CharField( verbose_name=_('name'), - max_length=100 + max_length=100, + db_collation="natural_sort" ) slug = models.SlugField( verbose_name=_('slug'), @@ -93,16 +96,32 @@ class VLANGroup(OrganizationalModel): raise ValidationError(_("Cannot set scope_id without scope_type.")) # Validate VID ranges - if self.vid_ranges and check_ranges_overlap(self.vid_ranges): - raise ValidationError({'vid_ranges': _("Ranges cannot overlap.")}) for vid_range in self.vid_ranges: - if vid_range.lower > vid_range.upper: + lower_vid = vid_range.lower if vid_range.lower_inc else vid_range.lower + 1 + upper_vid = vid_range.upper if vid_range.upper_inc else vid_range.upper - 1 + if lower_vid < VLAN_VID_MIN: + raise ValidationError({ + 'vid_ranges': _("Starting VLAN ID in range ({value}) cannot be less than {minimum}").format( + value=lower_vid, minimum=VLAN_VID_MIN + ) + }) + if upper_vid > VLAN_VID_MAX: + raise ValidationError({ + 'vid_ranges': _("Ending VLAN ID in range ({value}) cannot exceed {maximum}").format( + value=upper_vid, maximum=VLAN_VID_MAX + ) + }) + if lower_vid > upper_vid: raise ValidationError({ 'vid_ranges': _( - "Maximum child VID must be greater than or equal to minimum child VID ({value})" - ).format(value=vid_range) + "Ending VLAN ID in range must be greater than or equal to the starting VLAN ID ({range})" + ).format(range=f'{lower_vid}-{upper_vid}') }) + # Check for overlapping VID ranges + if self.vid_ranges and check_ranges_overlap(self.vid_ranges): + raise ValidationError({'vid_ranges': _("Ranges cannot overlap.")}) + def save(self, *args, **kwargs): self._total_vlan_ids = 0 for vid_range in self.vid_ranges: @@ -202,6 +221,21 @@ class VLAN(PrimaryModel): null=True, help_text=_("The primary function of this VLAN") ) + qinq_svlan = models.ForeignKey( + to='self', + on_delete=models.PROTECT, + related_name='qinq_cvlans', + blank=True, + null=True + ) + qinq_role = models.CharField( + verbose_name=_('Q-in-Q role'), + max_length=50, + choices=VLANQinQRoleChoices, + blank=True, + null=True, + help_text=_("Customer/service VLAN designation (for Q-in-Q/IEEE 802.1ad)") + ) l2vpn_terminations = GenericRelation( to='vpn.L2VPNTermination', content_type_field='assigned_object_type', @@ -212,7 +246,7 @@ class VLAN(PrimaryModel): objects = VLANQuerySet.as_manager() clone_fields = [ - 'site', 'group', 'tenant', 'status', 'role', 'description', + 'site', 'group', 'tenant', 'status', 'role', 'description', 'qinq_role', 'qinq_svlan', ] class Meta: @@ -226,6 +260,14 @@ class VLAN(PrimaryModel): fields=('group', 'name'), name='%(app_label)s_%(class)s_unique_group_name' ), + models.UniqueConstraint( + fields=('qinq_svlan', 'vid'), + name='%(app_label)s_%(class)s_unique_qinq_svlan_vid' + ), + models.UniqueConstraint( + fields=('qinq_svlan', 'name'), + name='%(app_label)s_%(class)s_unique_qinq_svlan_name' + ), ) verbose_name = _('VLAN') verbose_name_plural = _('VLANs') @@ -253,9 +295,24 @@ class VLAN(PrimaryModel): ).format(ranges=ranges_to_string(self.group.vid_ranges), group=self.group) }) + # Only Q-in-Q customer VLANs may be assigned to a service VLAN + if self.qinq_svlan and self.qinq_role != VLANQinQRoleChoices.ROLE_CUSTOMER: + raise ValidationError({ + 'qinq_svlan': _("Only Q-in-Q customer VLANs maybe assigned to a service VLAN.") + }) + + # A Q-in-Q customer VLAN must be assigned to a service VLAN + if self.qinq_role == VLANQinQRoleChoices.ROLE_CUSTOMER and not self.qinq_svlan: + raise ValidationError({ + 'qinq_role': _("A Q-in-Q customer VLAN must be assigned to a service VLAN.") + }) + def get_status_color(self): return VLANStatusChoices.colors.get(self.status) + def get_qinq_role_color(self): + return VLANQinQRoleChoices.colors.get(self.qinq_role) + def get_interfaces(self): # Return all device interfaces assigned to this VLAN return Interface.objects.filter( @@ -273,3 +330,75 @@ class VLAN(PrimaryModel): @property def l2vpn_termination(self): return self.l2vpn_terminations.first() + + +class VLANTranslationPolicy(PrimaryModel): + name = models.CharField( + verbose_name=_('name'), + max_length=100, + unique=True, + ) + + class Meta: + verbose_name = _('VLAN translation policy') + verbose_name_plural = _('VLAN translation policies') + ordering = ('name',) + + def __str__(self): + return self.name + + +class VLANTranslationRule(NetBoxModel): + policy = models.ForeignKey( + to=VLANTranslationPolicy, + related_name='rules', + on_delete=models.CASCADE, + ) + description = models.CharField( + verbose_name=_('description'), + max_length=200, + blank=True + ) + local_vid = models.PositiveSmallIntegerField( + verbose_name=_('Local VLAN ID'), + validators=( + MinValueValidator(VLAN_VID_MIN), + MaxValueValidator(VLAN_VID_MAX) + ), + help_text=_("Numeric VLAN ID (1-4094)") + ) + remote_vid = models.PositiveSmallIntegerField( + verbose_name=_('Remote VLAN ID'), + validators=( + MinValueValidator(VLAN_VID_MIN), + MaxValueValidator(VLAN_VID_MAX) + ), + help_text=_("Numeric VLAN ID (1-4094)") + ) + prerequisite_models = ( + 'ipam.VLANTranslationPolicy', + ) + + clone_fields = ['policy'] + + class Meta: + verbose_name = _('VLAN translation rule') + ordering = ('policy', 'local_vid',) + constraints = ( + models.UniqueConstraint( + fields=('policy', 'local_vid'), + name='%(app_label)s_%(class)s_unique_policy_local_vid' + ), + models.UniqueConstraint( + fields=('policy', 'remote_vid'), + name='%(app_label)s_%(class)s_unique_policy_remote_vid' + ), + ) + + def __str__(self): + return f'{self.local_vid} -> {self.remote_vid} ({self.policy})' + + def to_objectchange(self, action): + objectchange = super().to_objectchange(action) + objectchange.related_object = self.policy + return objectchange diff --git a/netbox/ipam/models/vrfs.py b/netbox/ipam/models/vrfs.py index 26afb7927..6a8b8d649 100644 --- a/netbox/ipam/models/vrfs.py +++ b/netbox/ipam/models/vrfs.py @@ -18,7 +18,8 @@ class VRF(PrimaryModel): """ name = models.CharField( verbose_name=_('name'), - max_length=100 + max_length=100, + db_collation="natural_sort" ) rd = models.CharField( max_length=VRF_RD_MAX_LENGTH, @@ -74,7 +75,8 @@ class RouteTarget(PrimaryModel): verbose_name=_('name'), max_length=VRF_RD_MAX_LENGTH, # Same format options as VRF RD (RFC 4360 section 4) unique=True, - help_text=_('Route target value (formatted in accordance with RFC 4360)') + help_text=_('Route target value (formatted in accordance with RFC 4360)'), + db_collation="natural_sort" ) tenant = models.ForeignKey( to='tenancy.Tenant', diff --git a/netbox/ipam/querysets.py b/netbox/ipam/querysets.py index 771e9b3b9..77ab8194a 100644 --- a/netbox/ipam/querysets.py +++ b/netbox/ipam/querysets.py @@ -148,7 +148,7 @@ class VLANQuerySet(RestrictedQuerySet): # Find all relevant VLANGroups q = Q() - site = vm.site or vm.cluster.site + site = vm.site or vm.cluster._site if vm.cluster: # Add VLANGroups scoped to the assigned cluster (or its group) q |= Q( diff --git a/netbox/ipam/search.py b/netbox/ipam/search.py index 16a8eba3c..d200abacf 100644 --- a/netbox/ipam/search.py +++ b/netbox/ipam/search.py @@ -160,6 +160,27 @@ class VLANGroupIndex(SearchIndex): display_attrs = ('scope_type', 'description') +@register_search +class VLANTranslationPolicyIndex(SearchIndex): + model = models.VLANTranslationPolicy + fields = ( + ('name', 100), + ('description', 500), + ) + display_attrs = ('description',) + + +@register_search +class VLANTranslationRuleIndex(SearchIndex): + model = models.VLANTranslationRule + fields = ( + ('policy', 100), + ('local_vid', 200), + ('remote_vid', 200), + ) + display_attrs = ('policy', 'local_vid', 'remote_vid') + + @register_search class VRFIndex(SearchIndex): model = models.VRF diff --git a/netbox/ipam/tables/ip.py b/netbox/ipam/tables/ip.py index 8ec7a5967..dbbeb3454 100644 --- a/netbox/ipam/tables/ip.py +++ b/netbox/ipam/tables/ip.py @@ -6,6 +6,7 @@ from django_tables2.utils import Accessor from ipam.models import * from netbox.tables import NetBoxTable, columns from tenancy.tables import TenancyColumnsMixin, TenantColumn +from .template_code import * __all__ = ( 'AggregateTable', @@ -20,61 +21,6 @@ __all__ = ( AVAILABLE_LABEL = mark_safe('Available') -AGGREGATE_COPY_BUTTON = """ -{% copy_content record.pk prefix="aggregate_" %} -""" - -PREFIX_LINK = """ -{% if record.pk %} - {{ record.prefix }} -{% else %} - {{ record.prefix }} -{% endif %} -""" - -PREFIX_COPY_BUTTON = """ -{% copy_content record.pk prefix="prefix_" %} -""" - -PREFIX_LINK_WITH_DEPTH = """ -{% load helpers %} -{% if record.depth %} -
    - {% for i in record.depth|as_range %} - - {% endfor %} -
    -{% endif %} -""" + PREFIX_LINK - -IPADDRESS_LINK = """ -{% if record.pk %} - {{ record.address }} -{% elif perms.ipam.add_ipaddress %} - {% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available -{% else %} - {% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available -{% endif %} -""" - -IPADDRESS_COPY_BUTTON = """ -{% copy_content record.pk prefix="ipaddress_" %} -""" - -IPADDRESS_ASSIGN_LINK = """ -{{ record }} -""" - -VRF_LINK = """ -{% if value %} - {{ record.vrf }} -{% elif object.vrf %} - {{ object.vrf }} -{% else %} - Global -{% endif %} -""" - # # RIRs @@ -241,8 +187,11 @@ class PrefixTable(TenancyColumnsMixin, NetBoxTable): template_code=VRF_LINK, verbose_name=_('VRF') ) - site = tables.Column( - verbose_name=_('Site'), + scope_type = columns.ContentTypeColumn( + verbose_name=_('Scope Type'), + ) + scope = tables.Column( + verbose_name=_('Scope'), linkify=True ) vlan_group = tables.Column( @@ -285,11 +234,12 @@ class PrefixTable(TenancyColumnsMixin, NetBoxTable): model = Prefix fields = ( 'pk', 'id', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'tenant_group', - 'site', 'vlan_group', 'vlan', 'role', 'is_pool', 'mark_utilized', 'description', 'comments', 'tags', - 'created', 'last_updated', + 'scope', 'scope_type', 'vlan_group', 'vlan', 'role', 'is_pool', 'mark_utilized', 'description', 'comments', + 'tags', 'created', 'last_updated', ) default_columns = ( - 'pk', 'prefix', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description', + 'pk', 'prefix', 'status', 'children', 'vrf', 'utilization', 'tenant', 'scope', 'vlan', 'role', + 'description', ) row_attrs = { 'class': lambda record: 'success' if not record.pk else '', diff --git a/netbox/ipam/tables/template_code.py b/netbox/ipam/tables/template_code.py new file mode 100644 index 000000000..fb969345e --- /dev/null +++ b/netbox/ipam/tables/template_code.py @@ -0,0 +1,88 @@ +AGGREGATE_COPY_BUTTON = """ +{% copy_content record.pk prefix="aggregate_" %} +""" + +PREFIX_LINK = """ +{% if record.pk %} + {{ record.prefix }} +{% else %} + {{ record.prefix }} +{% endif %} +""" + +PREFIX_COPY_BUTTON = """ +{% copy_content record.pk prefix="prefix_" %} +""" + +PREFIX_LINK_WITH_DEPTH = """ +{% load helpers %} +{% if record.depth %} +
    + {% for i in record.depth|as_range %} + + {% endfor %} +
    +{% endif %} +""" + PREFIX_LINK + +IPADDRESS_LINK = """ +{% if record.pk %} + {{ record.address }} +{% elif perms.ipam.add_ipaddress %} + {% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available +{% else %} + {% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available +{% endif %} +""" + +IPADDRESS_COPY_BUTTON = """ +{% copy_content record.pk prefix="ipaddress_" %} +""" + +IPADDRESS_ASSIGN_LINK = """ +{{ record }} +""" + +VRF_LINK = """ +{% if value %} + {{ record.vrf }} +{% elif object.vrf %} + {{ object.vrf }} +{% else %} + Global +{% endif %} +""" + +VLAN_LINK = """ +{% if record.pk %} + {{ record.vid }} +{% elif perms.ipam.add_vlan %} + {{ record.available }} VLAN{{ record.available|pluralize }} available +{% else %} + {{ record.available }} VLAN{{ record.available|pluralize }} available +{% endif %} +""" + +VLAN_PREFIXES = """ +{% for prefix in value.all %} + {{ prefix }}{% if not forloop.last %}
    {% endif %} +{% endfor %} +""" + +VLANGROUP_BUTTONS = """ +{% with next_vid=record.get_next_available_vid %} + {% if next_vid and perms.ipam.add_vlan %} + + + + {% endif %} +{% endwith %} +""" + +VLAN_MEMBER_TAGGED = """ +{% if record.untagged_vlan_id == object.pk %} + +{% else %} + +{% endif %} +""" diff --git a/netbox/ipam/tables/vlans.py b/netbox/ipam/tables/vlans.py index 5387ce24c..aa1900e41 100644 --- a/netbox/ipam/tables/vlans.py +++ b/netbox/ipam/tables/vlans.py @@ -8,6 +8,7 @@ from ipam.models import * from netbox.tables import NetBoxTable, columns from tenancy.tables import TenancyColumnsMixin, TenantColumn from virtualization.models import VMInterface +from .template_code import * __all__ = ( 'InterfaceVLANTable', @@ -16,44 +17,12 @@ __all__ = ( 'VLANMembersTable', 'VLANTable', 'VLANVirtualMachinesTable', + 'VLANTranslationPolicyTable', + 'VLANTranslationRuleTable', ) AVAILABLE_LABEL = mark_safe('Available') -VLAN_LINK = """ -{% if record.pk %} - {{ record.vid }} -{% elif perms.ipam.add_vlan %} - {{ record.available }} VLAN{{ record.available|pluralize }} available -{% else %} - {{ record.available }} VLAN{{ record.available|pluralize }} available -{% endif %} -""" - -VLAN_PREFIXES = """ -{% for prefix in value.all %} - {{ prefix }}{% if not forloop.last %}
    {% endif %} -{% endfor %} -""" - -VLANGROUP_BUTTONS = """ -{% with next_vid=record.get_next_available_vid %} - {% if next_vid and perms.ipam.add_vlan %} - - - - {% endif %} -{% endwith %} -""" - -VLAN_MEMBER_TAGGED = """ -{% if record.untagged_vlan_id == object.pk %} - -{% else %} - -{% endif %} -""" - # # VLAN groups @@ -130,6 +99,13 @@ class VLANTable(TenancyColumnsMixin, NetBoxTable): verbose_name=_('Role'), linkify=True ) + qinq_role = columns.ChoiceFieldColumn( + verbose_name=_('Q-in-Q role') + ) + qinq_svlan = tables.Column( + verbose_name=_('Q-in-Q SVLAN'), + linkify=True + ) l2vpn = tables.Column( accessor=tables.A('l2vpn_termination__l2vpn'), linkify=True, @@ -152,7 +128,7 @@ class VLANTable(TenancyColumnsMixin, NetBoxTable): model = VLAN fields = ( 'pk', 'id', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'tenant_group', 'status', 'role', - 'description', 'comments', 'tags', 'l2vpn', 'created', 'last_updated', + 'qinq_role', 'qinq_svlan', 'description', 'comments', 'tags', 'l2vpn', 'created', 'last_updated', ) default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description') row_attrs = { @@ -244,3 +220,59 @@ class InterfaceVLANTable(NetBoxTable): def __init__(self, interface, *args, **kwargs): self.interface = interface super().__init__(*args, **kwargs) + + +# +# VLAN Translation +# + +class VLANTranslationPolicyTable(NetBoxTable): + name = tables.Column( + verbose_name=_('Name'), + linkify=True + ) + rule_count = columns.LinkedCountColumn( + viewname='ipam:vlantranslationrule_list', + url_params={'policy_id': 'pk'}, + verbose_name=_('Rules') + ) + description = tables.Column( + verbose_name=_('Description'), + ) + tags = columns.TagColumn( + url_name='ipam:vlantranslationpolicy_list' + ) + + class Meta(NetBoxTable.Meta): + model = VLANTranslationPolicy + fields = ( + 'pk', 'id', 'name', 'rule_count', 'description', 'tags', 'created', 'last_updated', + ) + default_columns = ('pk', 'name', 'rule_count', 'description') + + +class VLANTranslationRuleTable(NetBoxTable): + policy = tables.Column( + verbose_name=_('Policy'), + linkify=True + ) + local_vid = tables.Column( + verbose_name=_('Local VID'), + linkify=True + ) + remote_vid = tables.Column( + verbose_name=_('Remote VID'), + ) + description = tables.Column( + verbose_name=_('Description'), + ) + tags = columns.TagColumn( + url_name='ipam:vlantranslationrule_list' + ) + + class Meta(NetBoxTable.Meta): + model = VLANTranslationRule + fields = ( + 'pk', 'id', 'name', 'policy', 'local_vid', 'remote_vid', 'description', 'tags', 'created', 'last_updated', + ) + default_columns = ('pk', 'policy', 'local_vid', 'remote_vid', 'description') diff --git a/netbox/ipam/tests/test_api.py b/netbox/ipam/tests/test_api.py index 1d2cdf1b7..e9dcacc16 100644 --- a/netbox/ipam/tests/test_api.py +++ b/netbox/ipam/tests/test_api.py @@ -732,10 +732,19 @@ class FHRPGroupTest(APIViewTestCases.APIViewTestCase): @classmethod def setUpTestData(cls): - fhrp_groups = ( - FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, group_id=10, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, auth_key='foobar123'), - FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3, group_id=20, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, auth_key='foobar123'), + FHRPGroup( + protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, + group_id=10, + auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, + auth_key='foobar123', + ), + FHRPGroup( + protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3, + group_id=20, + auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, + auth_key='foobar123', + ), FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_HSRP, group_id=30), ) FHRPGroup.objects.bulk_create(fhrp_groups) @@ -980,6 +989,7 @@ class VLANTest(APIViewTestCases.APIViewTestCase): VLAN(name='VLAN 1', vid=1, group=vlan_groups[0]), VLAN(name='VLAN 2', vid=2, group=vlan_groups[0]), VLAN(name='VLAN 3', vid=3, group=vlan_groups[0]), + VLAN(name='SVLAN 1', vid=1001, qinq_role=VLANQinQRoleChoices.ROLE_SERVICE), ) VLAN.objects.bulk_create(vlans) @@ -999,6 +1009,12 @@ class VLANTest(APIViewTestCases.APIViewTestCase): 'name': 'VLAN 6', 'group': vlan_groups[1].pk, }, + { + 'vid': 2001, + 'name': 'CVLAN 1', + 'qinq_role': VLANQinQRoleChoices.ROLE_CUSTOMER, + 'qinq_svlan': vlans[3].pk, + }, ] def test_delete_vlan_with_prefix(self): @@ -1020,6 +1036,112 @@ class VLANTest(APIViewTestCases.APIViewTestCase): self.assertTrue(content['detail'].startswith('Unable to delete object.')) +class VLANTranslationPolicyTest(APIViewTestCases.APIViewTestCase): + model = VLANTranslationPolicy + brief_fields = ['description', 'display', 'id', 'name', 'url',] + bulk_update_data = { + 'description': 'New description', + } + + @classmethod + def setUpTestData(cls): + + vlan_translation_policies = ( + VLANTranslationPolicy( + name='Policy 1', + description='foobar1', + ), + VLANTranslationPolicy( + name='Policy 2', + description='foobar2', + ), + VLANTranslationPolicy( + name='Policy 3', + description='foobar3', + ), + ) + VLANTranslationPolicy.objects.bulk_create(vlan_translation_policies) + + cls.create_data = [ + { + 'name': 'Policy 4', + 'description': 'foobar4', + }, + { + 'name': 'Policy 5', + 'description': 'foobar5', + }, + { + 'name': 'Policy 6', + 'description': 'foobar6', + }, + ] + + +class VLANTranslationRuleTest(APIViewTestCases.APIViewTestCase): + model = VLANTranslationRule + brief_fields = ['description', 'display', 'id', 'local_vid', 'policy', 'remote_vid', 'url'] + + @classmethod + def setUpTestData(cls): + + vlan_translation_policies = ( + VLANTranslationPolicy( + name='Policy 1', + description='foobar1', + ), + VLANTranslationPolicy( + name='Policy 2', + description='foobar2', + ), + ) + VLANTranslationPolicy.objects.bulk_create(vlan_translation_policies) + + vlan_translation_rules = ( + VLANTranslationRule( + policy=vlan_translation_policies[0], + local_vid=100, + remote_vid=200, + description='foo', + ), + VLANTranslationRule( + policy=vlan_translation_policies[0], + local_vid=101, + remote_vid=201, + description='bar', + ), + VLANTranslationRule( + policy=vlan_translation_policies[1], + local_vid=102, + remote_vid=202, + description='baz', + ), + ) + VLANTranslationRule.objects.bulk_create(vlan_translation_rules) + + cls.create_data = [ + { + 'policy': vlan_translation_policies[0].pk, + 'local_vid': 300, + 'remote_vid': 400, + }, + { + 'policy': vlan_translation_policies[0].pk, + 'local_vid': 301, + 'remote_vid': 401, + }, + { + 'policy': vlan_translation_policies[1].pk, + 'local_vid': 302, + 'remote_vid': 402, + }, + ] + + cls.bulk_update_data = { + 'policy': vlan_translation_policies[1].pk, + } + + class ServiceTemplateTest(APIViewTestCases.APIViewTestCase): model = ServiceTemplate brief_fields = ['description', 'display', 'id', 'name', 'ports', 'protocol', 'url'] diff --git a/netbox/ipam/tests/test_filtersets.py b/netbox/ipam/tests/test_filtersets.py index 4e38b1450..5455beb9c 100644 --- a/netbox/ipam/tests/test_filtersets.py +++ b/netbox/ipam/tests/test_filtersets.py @@ -496,8 +496,12 @@ class AggregateTestCase(TestCase, ChangeLoggedFilterSetTests): Tenant.objects.bulk_create(tenants) aggregates = ( - Aggregate(prefix='10.1.0.0/16', rir=rirs[0], tenant=tenants[0], date_added='2020-01-01', description='foobar1'), - Aggregate(prefix='10.2.0.0/16', rir=rirs[0], tenant=tenants[1], date_added='2020-01-02', description='foobar2'), + Aggregate( + prefix='10.1.0.0/16', rir=rirs[0], tenant=tenants[0], date_added='2020-01-01', description='foobar1' + ), + Aggregate( + prefix='10.2.0.0/16', rir=rirs[0], tenant=tenants[1], date_added='2020-01-02', description='foobar2' + ), Aggregate(prefix='10.3.0.0/16', rir=rirs[1], tenant=tenants[2], date_added='2020-01-03'), Aggregate(prefix='2001:db8:1::/48', rir=rirs[1], tenant=tenants[0], date_added='2020-01-04'), Aggregate(prefix='2001:db8:2::/48', rir=rirs[2], tenant=tenants[1], date_added='2020-01-05'), @@ -656,14 +660,80 @@ class PrefixTestCase(TestCase, ChangeLoggedFilterSetTests): Tenant.objects.bulk_create(tenants) prefixes = ( - Prefix(prefix='10.0.0.0/24', tenant=None, site=None, vrf=None, vlan=None, role=None, is_pool=True, mark_utilized=True, description='foobar1'), - Prefix(prefix='10.0.1.0/24', tenant=tenants[0], site=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0], description='foobar2'), - Prefix(prefix='10.0.2.0/24', tenant=tenants[1], site=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED), - Prefix(prefix='10.0.3.0/24', tenant=tenants[2], site=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED), - Prefix(prefix='2001:db8::/64', tenant=None, site=None, vrf=None, vlan=None, role=None, is_pool=True, mark_utilized=True), - Prefix(prefix='2001:db8:0:1::/64', tenant=tenants[0], site=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0]), - Prefix(prefix='2001:db8:0:2::/64', tenant=tenants[1], site=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED), - Prefix(prefix='2001:db8:0:3::/64', tenant=tenants[2], site=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED), + Prefix( + prefix='10.0.0.0/24', + tenant=None, + scope=None, + vrf=None, + vlan=None, + role=None, + is_pool=True, + mark_utilized=True, + description='foobar1', + ), + Prefix( + prefix='10.0.1.0/24', + tenant=tenants[0], + scope=sites[0], + vrf=vrfs[0], + vlan=vlans[0], + role=roles[0], + description='foobar2', + ), + Prefix( + prefix='10.0.2.0/24', + tenant=tenants[1], + scope=sites[1], + vrf=vrfs[1], + vlan=vlans[1], + role=roles[1], + status=PrefixStatusChoices.STATUS_DEPRECATED, + ), + Prefix( + prefix='10.0.3.0/24', + tenant=tenants[2], + scope=sites[2], + vrf=vrfs[2], + vlan=vlans[2], + role=roles[2], + status=PrefixStatusChoices.STATUS_RESERVED, + ), + Prefix( + prefix='2001:db8::/64', + tenant=None, + scope=None, + vrf=None, + vlan=None, + role=None, + is_pool=True, + mark_utilized=True, + ), + Prefix( + prefix='2001:db8:0:1::/64', + tenant=tenants[0], + scope=sites[0], + vrf=vrfs[0], + vlan=vlans[0], + role=roles[0] + ), + Prefix( + prefix='2001:db8:0:2::/64', + tenant=tenants[1], + scope=sites[1], + vrf=vrfs[1], + vlan=vlans[1], + role=roles[1], + status=PrefixStatusChoices.STATUS_DEPRECATED, + ), + Prefix( + prefix='2001:db8:0:3::/64', + tenant=tenants[2], + scope=sites[2], + vrf=vrfs[2], + vlan=vlans[2], + role=roles[2], + status=PrefixStatusChoices.STATUS_RESERVED, + ), Prefix(prefix='10.0.0.0/16'), Prefix(prefix='2001:db8::/32'), ) @@ -1365,7 +1435,10 @@ class FHRPGroupTestCase(TestCase, ChangeLoggedFilterSetTests): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_auth_type(self): - params = {'auth_type': [FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5]} + params = {'auth_type': [ + FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, + FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, + ]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_auth_key(self): @@ -1630,6 +1703,7 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests): Site(name='Site 4', slug='site-4', region=regions[0], group=site_groups[0]), Site(name='Site 5', slug='site-5', region=regions[1], group=site_groups[1]), Site(name='Site 6', slug='site-6', region=regions[2], group=site_groups[2]), + Site(name='Site 7', slug='site-7'), ) Site.objects.bulk_create(sites) @@ -1652,9 +1726,15 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests): device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1') role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1') devices = ( - Device(name='Device 1', site=sites[0], location=locations[0], rack=racks[0], device_type=device_type, role=role), - Device(name='Device 2', site=sites[1], location=locations[1], rack=racks[1], device_type=device_type, role=role), - Device(name='Device 3', site=sites[2], location=locations[2], rack=racks[2], device_type=device_type, role=role), + Device( + name='Device 1', site=sites[0], location=locations[0], rack=racks[0], device_type=device_type, role=role + ), + Device( + name='Device 2', site=sites[1], location=locations[1], rack=racks[1], device_type=device_type, role=role + ), + Device( + name='Device 3', site=sites[2], location=locations[2], rack=racks[2], device_type=device_type, role=role + ), ) Device.objects.bulk_create(devices) @@ -1674,11 +1754,12 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests): cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1') clusters = ( - Cluster(name='Cluster 1', type=cluster_type, group=cluster_groups[0], site=sites[0]), - Cluster(name='Cluster 2', type=cluster_type, group=cluster_groups[1], site=sites[1]), - Cluster(name='Cluster 3', type=cluster_type, group=cluster_groups[2], site=sites[2]), + Cluster(name='Cluster 1', type=cluster_type, group=cluster_groups[0], scope=sites[0]), + Cluster(name='Cluster 2', type=cluster_type, group=cluster_groups[1], scope=sites[1]), + Cluster(name='Cluster 3', type=cluster_type, group=cluster_groups[2], scope=sites[2]), ) - Cluster.objects.bulk_create(clusters) + for cluster in clusters: + cluster.save() virtual_machines = ( VirtualMachine(name='Virtual Machine 1', cluster=clusters[0]), @@ -1771,22 +1852,98 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests): VLAN(vid=19, name='Cluster 1', group=groups[18]), VLAN(vid=20, name='Cluster 2', group=groups[19]), VLAN(vid=21, name='Cluster 3', group=groups[20]), - - VLAN(vid=101, name='VLAN 101', site=sites[3], group=groups[21], role=roles[0], tenant=tenants[0], status=VLANStatusChoices.STATUS_ACTIVE), - VLAN(vid=102, name='VLAN 102', site=sites[3], group=groups[21], role=roles[0], tenant=tenants[0], status=VLANStatusChoices.STATUS_ACTIVE), - VLAN(vid=201, name='VLAN 201', site=sites[4], group=groups[22], role=roles[1], tenant=tenants[1], status=VLANStatusChoices.STATUS_DEPRECATED), - VLAN(vid=202, name='VLAN 202', site=sites[4], group=groups[22], role=roles[1], tenant=tenants[1], status=VLANStatusChoices.STATUS_DEPRECATED), - VLAN(vid=301, name='VLAN 301', site=sites[5], group=groups[23], role=roles[2], tenant=tenants[2], status=VLANStatusChoices.STATUS_RESERVED), - VLAN(vid=302, name='VLAN 302', site=sites[5], group=groups[23], role=roles[2], tenant=tenants[2], status=VLANStatusChoices.STATUS_RESERVED), - + VLAN( + vid=101, + name='VLAN 101', + site=sites[3], + group=groups[21], + role=roles[0], + tenant=tenants[0], + status=VLANStatusChoices.STATUS_ACTIVE, + ), + VLAN( + vid=102, + name='VLAN 102', + site=sites[3], + group=groups[21], + role=roles[0], + tenant=tenants[0], + status=VLANStatusChoices.STATUS_ACTIVE, + ), + VLAN( + vid=201, + name='VLAN 201', + site=sites[4], + group=groups[22], + role=roles[1], + tenant=tenants[1], + status=VLANStatusChoices.STATUS_DEPRECATED, + ), + VLAN( + vid=202, + name='VLAN 202', + site=sites[4], + group=groups[22], + role=roles[1], + tenant=tenants[1], + status=VLANStatusChoices.STATUS_DEPRECATED, + ), + VLAN( + vid=301, + name='VLAN 301', + site=sites[5], + group=groups[23], + role=roles[2], + tenant=tenants[2], + status=VLANStatusChoices.STATUS_RESERVED, + ), + VLAN( + vid=302, + name='VLAN 302', + site=sites[5], + group=groups[23], + role=roles[2], + tenant=tenants[2], + status=VLANStatusChoices.STATUS_RESERVED, + ), # Create one globally available VLAN on a VLAN group VLAN(vid=500, name='VLAN Group 1', group=groups[24]), - # Create one globally available VLAN VLAN(vid=1000, name='Global VLAN'), + # Create some Q-in-Q service VLANs + VLAN(vid=2001, name='SVLAN 1', site=sites[6], qinq_role=VLANQinQRoleChoices.ROLE_SERVICE), + VLAN(vid=2002, name='SVLAN 2', site=sites[6], qinq_role=VLANQinQRoleChoices.ROLE_SERVICE), + VLAN(vid=2003, name='SVLAN 3', site=sites[6], qinq_role=VLANQinQRoleChoices.ROLE_SERVICE), ) VLAN.objects.bulk_create(vlans) + # Create Q-in-Q customer VLANs + VLAN.objects.bulk_create( + [ + VLAN( + vid=3001, + name='CVLAN 1', + site=sites[6], + qinq_svlan=vlans[29], + qinq_role=VLANQinQRoleChoices.ROLE_CUSTOMER, + ), + VLAN( + vid=3002, + name='CVLAN 2', + site=sites[6], + qinq_svlan=vlans[30], + qinq_role=VLANQinQRoleChoices.ROLE_CUSTOMER, + ), + VLAN( + vid=3003, + name='CVLAN 3', + site=sites[6], + qinq_svlan=vlans[31], + qinq_role=VLANQinQRoleChoices.ROLE_CUSTOMER, + ), + ] + ) + # Assign VLANs to device interfaces interfaces[0].untagged_vlan = vlans[0] interfaces[0].tagged_vlans.add(vlans[1]) @@ -1897,6 +2054,110 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'vminterface_id': vminterface_id} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_qinq_role(self): + params = {'qinq_role': [VLANQinQRoleChoices.ROLE_SERVICE, VLANQinQRoleChoices.ROLE_CUSTOMER]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6) + + def test_qinq_svlan(self): + vlans = VLAN.objects.filter(qinq_role=VLANQinQRoleChoices.ROLE_SERVICE)[:2] + params = {'qinq_svlan_id': [vlans[0].pk, vlans[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'qinq_svlan_vid': [vlans[0].vid, vlans[1].vid]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + +class VLANTranslationPolicyTestCase(TestCase, ChangeLoggedFilterSetTests): + queryset = VLANTranslationPolicy.objects.all() + filterset = VLANTranslationPolicyFilterSet + + @classmethod + def setUpTestData(cls): + + vlan_translation_policies = ( + VLANTranslationPolicy( + name='Policy 1', + description='foobar1', + ), + VLANTranslationPolicy( + name='Policy 2', + description='foobar2', + ), + VLANTranslationPolicy( + name='Policy 3', + description='foobar3', + ), + ) + VLANTranslationPolicy.objects.bulk_create(vlan_translation_policies) + + def test_name(self): + params = {'name': ['Policy 1', 'Policy 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 VLANTranslationRuleTestCase(TestCase, ChangeLoggedFilterSetTests): + queryset = VLANTranslationRule.objects.all() + filterset = VLANTranslationRuleFilterSet + + @classmethod + def setUpTestData(cls): + + vlan_translation_policies = ( + VLANTranslationPolicy( + name='Policy 1', + description='foobar1', + ), + VLANTranslationPolicy( + name='Policy 2', + description='foobar2', + ), + ) + VLANTranslationPolicy.objects.bulk_create(vlan_translation_policies) + + vlan_translation_rules = ( + VLANTranslationRule( + policy=vlan_translation_policies[0], + local_vid=100, + remote_vid=200, + description='foo', + ), + VLANTranslationRule( + policy=vlan_translation_policies[0], + local_vid=101, + remote_vid=201, + description='bar', + ), + VLANTranslationRule( + policy=vlan_translation_policies[1], + local_vid=100, + remote_vid=200, + description='baz', + ), + ) + VLANTranslationRule.objects.bulk_create(vlan_translation_rules) + + def test_policy_id(self): + policies = VLANTranslationPolicy.objects.all()[:2] + params = {'policy_id': [policies[0].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'policy': [policies[0].name]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_local_vid(self): + params = {'local_vid': [100]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_remote_vid(self): + params = {'remote_vid': [200]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_description(self): + params = {'description': ['foo', 'bar']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + class ServiceTemplateTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = ServiceTemplate.objects.all() @@ -2007,12 +2268,39 @@ class ServiceTestCase(TestCase, ChangeLoggedFilterSetTests): VirtualMachine.objects.bulk_create(virtual_machines) services = ( - Service(device=devices[0], name='Service 1', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[1001], description='foobar1'), - Service(device=devices[1], name='Service 2', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[1002], description='foobar2'), + Service( + device=devices[0], + name='Service 1', + protocol=ServiceProtocolChoices.PROTOCOL_TCP, + ports=[1001], + description='foobar1', + ), + Service( + device=devices[1], + name='Service 2', + protocol=ServiceProtocolChoices.PROTOCOL_TCP, + ports=[1002], + description='foobar2', + ), Service(device=devices[2], name='Service 3', protocol=ServiceProtocolChoices.PROTOCOL_UDP, ports=[1003]), - Service(virtual_machine=virtual_machines[0], name='Service 4', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[2001]), - Service(virtual_machine=virtual_machines[1], name='Service 5', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[2002]), - Service(virtual_machine=virtual_machines[2], name='Service 6', protocol=ServiceProtocolChoices.PROTOCOL_UDP, ports=[2003]), + Service( + virtual_machine=virtual_machines[0], + name='Service 4', + protocol=ServiceProtocolChoices.PROTOCOL_TCP, + ports=[2001], + ), + Service( + virtual_machine=virtual_machines[1], + name='Service 5', + protocol=ServiceProtocolChoices.PROTOCOL_TCP, + ports=[2002], + ), + Service( + virtual_machine=virtual_machines[2], + name='Service 6', + protocol=ServiceProtocolChoices.PROTOCOL_UDP, + ports=[2003], + ), ) Service.objects.bulk_create(services) services[0].ipaddresses.add(ip_addresses[0]) diff --git a/netbox/ipam/tests/test_models.py b/netbox/ipam/tests/test_models.py index 8a5d918a9..62eb74123 100644 --- a/netbox/ipam/tests/test_models.py +++ b/netbox/ipam/tests/test_models.py @@ -36,6 +36,56 @@ class TestAggregate(TestCase): self.assertEqual(aggregate.get_utilization(), 100) +class TestIPRange(TestCase): + + def test_overlapping_range(self): + iprange_192_168 = IPRange.objects.create( + start_address=IPNetwork('192.168.0.1/22'), end_address=IPNetwork('192.168.0.49/22') + ) + iprange_192_168.clean() + iprange_3_1_99 = IPRange.objects.create( + start_address=IPNetwork('1.2.3.1/24'), end_address=IPNetwork('1.2.3.99/24') + ) + iprange_3_1_99.clean() + iprange_3_100_199 = IPRange.objects.create( + start_address=IPNetwork('1.2.3.100/24'), end_address=IPNetwork('1.2.3.199/24') + ) + iprange_3_100_199.clean() + iprange_3_200_255 = IPRange.objects.create( + start_address=IPNetwork('1.2.3.200/24'), end_address=IPNetwork('1.2.3.255/24') + ) + iprange_3_200_255.clean() + iprange_4_1_99 = IPRange.objects.create( + start_address=IPNetwork('1.2.4.1/24'), end_address=IPNetwork('1.2.4.99/24') + ) + iprange_4_1_99.clean() + iprange_4_200 = IPRange.objects.create( + start_address=IPNetwork('1.2.4.200/24'), end_address=IPNetwork('1.2.4.255/24') + ) + iprange_4_200.clean() + + # Overlapping range entirely within existing + with self.assertRaises(ValidationError): + iprange_3_123_124 = IPRange.objects.create( + start_address=IPNetwork('1.2.3.123/26'), end_address=IPNetwork('1.2.3.124/26') + ) + iprange_3_123_124.clean() + + # Overlapping range starting within existing + with self.assertRaises(ValidationError): + iprange_4_98_101 = IPRange.objects.create( + start_address=IPNetwork('1.2.4.98/24'), end_address=IPNetwork('1.2.4.101/24') + ) + iprange_4_98_101.clean() + + # Overlapping range ending within existing + with self.assertRaises(ValidationError): + iprange_4_198_201 = IPRange.objects.create( + start_address=IPNetwork('1.2.4.198/24'), end_address=IPNetwork('1.2.4.201/24') + ) + iprange_4_198_201.clean() + + class TestPrefix(TestCase): def test_get_duplicates(self): @@ -76,13 +126,30 @@ class TestPrefix(TestCase): def test_get_child_ranges(self): prefix = Prefix(prefix='192.168.0.16/28') prefix.save() - ranges = IPRange.objects.bulk_create(( - IPRange(start_address=IPNetwork('192.168.0.1/24'), end_address=IPNetwork('192.168.0.10/24'), size=10), # No overlap - IPRange(start_address=IPNetwork('192.168.0.11/24'), end_address=IPNetwork('192.168.0.17/24'), size=7), # Partial overlap - IPRange(start_address=IPNetwork('192.168.0.18/24'), end_address=IPNetwork('192.168.0.23/24'), size=6), # Full overlap - IPRange(start_address=IPNetwork('192.168.0.24/24'), end_address=IPNetwork('192.168.0.30/24'), size=7), # Full overlap - IPRange(start_address=IPNetwork('192.168.0.31/24'), end_address=IPNetwork('192.168.0.40/24'), size=10), # Partial overlap - )) + ranges = IPRange.objects.bulk_create( + ( + # No overlap + IPRange( + start_address=IPNetwork('192.168.0.1/24'), end_address=IPNetwork('192.168.0.10/24'), size=10 + ), + # Partial overlap + IPRange( + start_address=IPNetwork('192.168.0.11/24'), end_address=IPNetwork('192.168.0.17/24'), size=7 + ), + # Full overlap + IPRange( + start_address=IPNetwork('192.168.0.18/24'), end_address=IPNetwork('192.168.0.23/24'), size=6 + ), + # Full overlap + IPRange( + start_address=IPNetwork('192.168.0.24/24'), end_address=IPNetwork('192.168.0.30/24'), size=7 + ), + # Partial overlap + IPRange( + start_address=IPNetwork('192.168.0.31/24'), end_address=IPNetwork('192.168.0.40/24'), size=10 + ), + ) + ) child_ranges = prefix.get_child_ranges() @@ -557,3 +624,24 @@ class TestVLANGroup(TestCase): vlangroup.vid_ranges = string_to_ranges('2-2') vlangroup.full_clean() vlangroup.save() + + +class TestVLAN(TestCase): + + @classmethod + def setUpTestData(cls): + VLAN.objects.bulk_create(( + VLAN(name='VLAN 1', vid=1, qinq_role=VLANQinQRoleChoices.ROLE_SERVICE), + )) + + def test_qinq_role(self): + svlan = VLAN.objects.filter(qinq_role=VLANQinQRoleChoices.ROLE_SERVICE).first() + + vlan = VLAN( + name='VLAN X', + vid=999, + qinq_role=VLANQinQRoleChoices.ROLE_SERVICE, + qinq_svlan=svlan + ) + with self.assertRaises(ValidationError): + vlan.full_clean() diff --git a/netbox/ipam/tests/test_ordering.py b/netbox/ipam/tests/test_ordering.py index 8d69af847..2f7a5342d 100644 --- a/netbox/ipam/tests/test_ordering.py +++ b/netbox/ipam/tests/test_ordering.py @@ -42,7 +42,7 @@ class PrefixOrderingTestCase(OrderingTestBase): """ This is a very basic test, which tests both prefixes without VRFs and prefixes with VRFs """ - vrf1, vrf2, vrf3 = list(VRF.objects.all()) + vrf1, vrf2 = VRF.objects.all()[:2] prefixes = ( Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, prefix=netaddr.IPNetwork('192.168.0.0/16')), Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, prefix=netaddr.IPNetwork('192.168.0.0/24')), @@ -92,8 +92,8 @@ class PrefixOrderingTestCase(OrderingTestBase): def test_prefix_complex_ordering(self): """ - This function tests a complex ordering of interwoven prefixes and vrfs. This is the current expected ordering of VRFs - This includes the testing of the Container status. + This function tests a complex ordering of interwoven prefixes and vrfs. This is the current expected ordering + of VRFs. This includes the testing of the Container status. The proper ordering, to get proper containerization should be: None:10.0.0.0/8 @@ -106,7 +106,7 @@ class PrefixOrderingTestCase(OrderingTestBase): VRF A:10.1.1.0/24 None: 192.168.0.0/16 """ - vrf1, vrf2, vrf3 = list(VRF.objects.all()) + vrf1 = VRF.objects.first() prefixes = [ Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, prefix=netaddr.IPNetwork('10.0.0.0/8')), Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, prefix=netaddr.IPNetwork('10.0.0.0/16')), @@ -125,12 +125,11 @@ class PrefixOrderingTestCase(OrderingTestBase): class IPAddressOrderingTestCase(OrderingTestBase): - def test_address_vrf_ordering(self): """ This function tests ordering with the inclusion of vrfs """ - vrf1, vrf2, vrf3 = list(VRF.objects.all()) + vrf1, vrf2 = VRF.objects.all()[:2] addresses = ( IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf1, address=netaddr.IPNetwork('10.0.0.1/24')), IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf1, address=netaddr.IPNetwork('10.0.1.1/24')), @@ -147,24 +146,54 @@ class IPAddressOrderingTestCase(OrderingTestBase): IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf1, address=netaddr.IPNetwork('10.2.2.1/24')), IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf1, address=netaddr.IPNetwork('10.2.3.1/24')), IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf1, address=netaddr.IPNetwork('10.2.4.1/24')), - - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.0.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.1.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.2.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.3.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.4.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.0.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.1.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.2.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.3.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.4.1/24')), - - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.0.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.1.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.2.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.3.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.4.1/24')), - IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.5.1/24')), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.0.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.1.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.2.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.3.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.16.4.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.0.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.1.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.2.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.3.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrf2, address=netaddr.IPNetwork('172.17.4.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.0.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.1.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.2.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.3.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.4.1/24') + ), + IPAddress( + status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, address=netaddr.IPNetwork('192.168.5.1/24') + ), ) IPAddress.objects.bulk_create(addresses) diff --git a/netbox/ipam/tests/test_views.py b/netbox/ipam/tests/test_views.py index 95b311878..d7d367bb7 100644 --- a/netbox/ipam/tests/test_views.py +++ b/netbox/ipam/tests/test_views.py @@ -1,5 +1,6 @@ import datetime +from django.contrib.contenttypes.models import ContentType from django.test import override_settings from django.urls import reverse from netaddr import IPNetwork @@ -409,9 +410,9 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase): Role.objects.bulk_create(roles) prefixes = ( - Prefix(prefix=IPNetwork('10.1.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]), - Prefix(prefix=IPNetwork('10.2.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]), - Prefix(prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]), + Prefix(prefix=IPNetwork('10.1.0.0/16'), vrf=vrfs[0], scope=sites[0], role=roles[0]), + Prefix(prefix=IPNetwork('10.2.0.0/16'), vrf=vrfs[0], scope=sites[0], role=roles[0]), + Prefix(prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], scope=sites[0], role=roles[0]), ) Prefix.objects.bulk_create(prefixes) @@ -419,7 +420,8 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase): cls.form_data = { 'prefix': IPNetwork('192.0.2.0/24'), - 'site': sites[1].pk, + 'scope_type': ContentType.objects.get_for_model(Site).pk, + 'scope': sites[1].pk, 'vrf': vrfs[1].pk, 'tenant': None, 'vlan': None, @@ -430,11 +432,12 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'tags': [t.pk for t in tags], } + site = sites[0].pk cls.csv_data = ( - "vrf,prefix,status", - "VRF 1,10.4.0.0/16,active", - "VRF 1,10.5.0.0/16,active", - "VRF 1,10.6.0.0/16,active", + "vrf,prefix,status,scope_type,scope_id", + f"VRF 1,10.4.0.0/16,active,dcim.site,{site}", + f"VRF 1,10.5.0.0/16,active,dcim.site,{site}", + f"VRF 1,10.6.0.0/16,active,dcim.site,{site}", ) cls.csv_update_data = ( @@ -445,7 +448,6 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase): ) cls.bulk_edit_data = { - 'site': sites[1].pk, 'vrf': vrfs[1].pk, 'tenant': None, 'status': PrefixStatusChoices.STATUS_RESERVED, @@ -501,11 +503,13 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase): """ Custom import test for YAML-based imports (versus CSV) """ - IMPORT_DATA = """ + site = Site.objects.get(name='Site 1') + IMPORT_DATA = f""" prefix: 10.1.1.0/24 status: active vlan: 101 -site: Site 1 +scope_type: dcim.site +scope_id: {site.pk} """ # Note, a site is not tied to the VLAN to verify the fix for #12622 VLAN.objects.create(vid=101, name='VLAN101') @@ -517,25 +521,27 @@ site: Site 1 'data': IMPORT_DATA, 'format': 'yaml' } - response = self.client.post(reverse('ipam:prefix_import'), data=form_data, follow=True) + response = self.client.post(reverse('ipam:prefix_bulk_import'), data=form_data, follow=True) self.assertHttpStatus(response, 200) prefix = Prefix.objects.get(prefix='10.1.1.0/24') self.assertEqual(prefix.status, PrefixStatusChoices.STATUS_ACTIVE) self.assertEqual(prefix.vlan.vid, 101) - self.assertEqual(prefix.site.name, "Site 1") + self.assertEqual(prefix.scope, site) @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) def test_prefix_import_with_vlan_group(self): """ This test covers a unique import edge case where VLAN group is specified during the import. """ - IMPORT_DATA = """ + site = Site.objects.get(name='Site 1') + IMPORT_DATA = f""" prefix: 10.1.2.0/24 status: active -vlan: 102 -site: Site 1 +scope_type: dcim.site +scope_id: {site.pk} vlan_group: Group 1 +vlan: 102 """ vlan_group = VLANGroup.objects.create(name='Group 1', slug='group-1', scope=Site.objects.get(name="Site 1")) VLAN.objects.create(vid=102, name='VLAN102', group=vlan_group) @@ -547,13 +553,13 @@ vlan_group: Group 1 'data': IMPORT_DATA, 'format': 'yaml' } - response = self.client.post(reverse('ipam:prefix_import'), data=form_data, follow=True) + response = self.client.post(reverse('ipam:prefix_bulk_import'), data=form_data, follow=True) self.assertHttpStatus(response, 200) prefix = Prefix.objects.get(prefix='10.1.2.0/24') self.assertEqual(prefix.status, PrefixStatusChoices.STATUS_ACTIVE) self.assertEqual(prefix.vlan.vid, 102) - self.assertEqual(prefix.site.name, "Site 1") + self.assertEqual(prefix.scope, site) class IPRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase): @@ -701,11 +707,23 @@ class FHRPGroupTestCase(ViewTestCases.PrimaryObjectViewTestCase): @classmethod def setUpTestData(cls): - fhrp_groups = ( - FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, group_id=10, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, auth_key='foobar123'), - FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3, group_id=20, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, auth_key='foobar123'), - FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_HSRP, group_id=30), + FHRPGroup( + protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, + group_id=10, + auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, + auth_key='foobar123', + ), + FHRPGroup( + protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3, + group_id=20, + auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, + auth_key='foobar123', + ), + FHRPGroup( + protocol=FHRPGroupProtocolChoices.PROTOCOL_HSRP, + group_id=30 + ), ) FHRPGroup.objects.bulk_create(fhrp_groups) @@ -857,6 +875,121 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase): } +class VLANTranslationPolicyTestCase(ViewTestCases.PrimaryObjectViewTestCase): + model = VLANTranslationPolicy + + @classmethod + def setUpTestData(cls): + + vlan_translation_policies = ( + VLANTranslationPolicy( + name='Policy 1', + description='foobar1', + ), + VLANTranslationPolicy( + name='Policy 2', + description='foobar2', + ), + VLANTranslationPolicy( + name='Policy 3', + description='foobar3', + ), + ) + VLANTranslationPolicy.objects.bulk_create(vlan_translation_policies) + + tags = create_tags('Alpha', 'Bravo', 'Charlie') + + cls.form_data = { + 'name': 'Policy999', + 'description': 'A new VLAN Translation Policy', + 'tags': [t.pk for t in tags], + } + + cls.csv_data = ( + "name,description", + "Policy101,foobar1", + "Policy102,foobar2", + "Policy103,foobar3", + ) + + cls.csv_update_data = ( + "id,name,description", + f"{vlan_translation_policies[0].pk},Policy101,New description 1", + f"{vlan_translation_policies[1].pk},Policy102,New description 2", + f"{vlan_translation_policies[2].pk},Policy103,New description 3", + ) + + cls.bulk_edit_data = { + 'description': 'New description', + } + + +class VLANTranslationRuleTestCase(ViewTestCases.PrimaryObjectViewTestCase): + model = VLANTranslationRule + + @classmethod + def setUpTestData(cls): + + vlan_translation_policies = ( + VLANTranslationPolicy( + name='Policy 1', + description='foobar1', + ), + VLANTranslationPolicy( + name='Policy 2', + description='foobar2', + ), + VLANTranslationPolicy( + name='Policy 3', + description='foobar3', + ), + ) + VLANTranslationPolicy.objects.bulk_create(vlan_translation_policies) + + vlan_translation_rules = ( + VLANTranslationRule( + policy=vlan_translation_policies[0], + local_vid=100, + remote_vid=200, + ), + VLANTranslationRule( + policy=vlan_translation_policies[0], + local_vid=101, + remote_vid=201, + ), + VLANTranslationRule( + policy=vlan_translation_policies[1], + local_vid=102, + remote_vid=202, + ), + ) + VLANTranslationRule.objects.bulk_create(vlan_translation_rules) + + cls.form_data = { + 'policy': vlan_translation_policies[0].pk, + 'local_vid': 300, + 'remote_vid': 400, + } + + cls.csv_data = ( + "policy,local_vid,remote_vid", + f"{vlan_translation_policies[0].name},103,203", + f"{vlan_translation_policies[0].name},104,204", + f"{vlan_translation_policies[1].name},105,205", + ) + + cls.csv_update_data = ( + "id,local_vid,remote_vid", + f"{vlan_translation_rules[0].pk},105,205", + f"{vlan_translation_rules[1].pk},106,206", + f"{vlan_translation_rules[2].pk},107,207", + ) + + cls.bulk_edit_data = { + 'policy': vlan_translation_policies[2].pk, + } + + class ServiceTemplateTestCase(ViewTestCases.PrimaryObjectViewTestCase): model = ServiceTemplate diff --git a/netbox/ipam/urls.py b/netbox/ipam/urls.py index 61deeff4b..c55e874a1 100644 --- a/netbox/ipam/urls.py +++ b/netbox/ipam/urls.py @@ -1,134 +1,62 @@ from django.urls import include, path from utilities.urls import get_model_urls -from . import views +from . import views # noqa F401 app_name = 'ipam' urlpatterns = [ - # ASN ranges - path('asn-ranges/', views.ASNRangeListView.as_view(), name='asnrange_list'), - path('asn-ranges/add/', views.ASNRangeEditView.as_view(), name='asnrange_add'), - path('asn-ranges/import/', views.ASNRangeBulkImportView.as_view(), name='asnrange_import'), - path('asn-ranges/edit/', views.ASNRangeBulkEditView.as_view(), name='asnrange_bulk_edit'), - path('asn-ranges/delete/', views.ASNRangeBulkDeleteView.as_view(), name='asnrange_bulk_delete'), + path('asn-ranges/', include(get_model_urls('ipam', 'asnrange', detail=False))), path('asn-ranges//', include(get_model_urls('ipam', 'asnrange'))), - # ASNs - path('asns/', views.ASNListView.as_view(), name='asn_list'), - path('asns/add/', views.ASNEditView.as_view(), name='asn_add'), - path('asns/import/', views.ASNBulkImportView.as_view(), name='asn_import'), - path('asns/edit/', views.ASNBulkEditView.as_view(), name='asn_bulk_edit'), - path('asns/delete/', views.ASNBulkDeleteView.as_view(), name='asn_bulk_delete'), + path('asns/', include(get_model_urls('ipam', 'asn', detail=False))), path('asns//', include(get_model_urls('ipam', 'asn'))), - # VRFs - path('vrfs/', views.VRFListView.as_view(), name='vrf_list'), - path('vrfs/add/', views.VRFEditView.as_view(), name='vrf_add'), - path('vrfs/import/', views.VRFBulkImportView.as_view(), name='vrf_import'), - path('vrfs/edit/', views.VRFBulkEditView.as_view(), name='vrf_bulk_edit'), - path('vrfs/delete/', views.VRFBulkDeleteView.as_view(), name='vrf_bulk_delete'), + path('vrfs/', include(get_model_urls('ipam', 'vrf', detail=False))), path('vrfs//', include(get_model_urls('ipam', 'vrf'))), - # Route targets - path('route-targets/', views.RouteTargetListView.as_view(), name='routetarget_list'), - path('route-targets/add/', views.RouteTargetEditView.as_view(), name='routetarget_add'), - path('route-targets/import/', views.RouteTargetBulkImportView.as_view(), name='routetarget_import'), - path('route-targets/edit/', views.RouteTargetBulkEditView.as_view(), name='routetarget_bulk_edit'), - path('route-targets/delete/', views.RouteTargetBulkDeleteView.as_view(), name='routetarget_bulk_delete'), + path('route-targets/', include(get_model_urls('ipam', 'routetarget', detail=False))), path('route-targets//', include(get_model_urls('ipam', 'routetarget'))), - # RIRs - path('rirs/', views.RIRListView.as_view(), name='rir_list'), - path('rirs/add/', views.RIREditView.as_view(), name='rir_add'), - path('rirs/import/', views.RIRBulkImportView.as_view(), name='rir_import'), - path('rirs/edit/', views.RIRBulkEditView.as_view(), name='rir_bulk_edit'), - path('rirs/delete/', views.RIRBulkDeleteView.as_view(), name='rir_bulk_delete'), + path('rirs/', include(get_model_urls('ipam', 'rir', detail=False))), path('rirs//', include(get_model_urls('ipam', 'rir'))), - # Aggregates - path('aggregates/', views.AggregateListView.as_view(), name='aggregate_list'), - path('aggregates/add/', views.AggregateEditView.as_view(), name='aggregate_add'), - path('aggregates/import/', views.AggregateBulkImportView.as_view(), name='aggregate_import'), - path('aggregates/edit/', views.AggregateBulkEditView.as_view(), name='aggregate_bulk_edit'), - path('aggregates/delete/', views.AggregateBulkDeleteView.as_view(), name='aggregate_bulk_delete'), + path('aggregates/', include(get_model_urls('ipam', 'aggregate', detail=False))), path('aggregates//', include(get_model_urls('ipam', 'aggregate'))), - # Roles - path('roles/', views.RoleListView.as_view(), name='role_list'), - path('roles/add/', views.RoleEditView.as_view(), name='role_add'), - path('roles/import/', views.RoleBulkImportView.as_view(), name='role_import'), - path('roles/edit/', views.RoleBulkEditView.as_view(), name='role_bulk_edit'), - path('roles/delete/', views.RoleBulkDeleteView.as_view(), name='role_bulk_delete'), + path('roles/', include(get_model_urls('ipam', 'role', detail=False))), path('roles//', include(get_model_urls('ipam', 'role'))), - # Prefixes - path('prefixes/', views.PrefixListView.as_view(), name='prefix_list'), - path('prefixes/add/', views.PrefixEditView.as_view(), name='prefix_add'), - path('prefixes/import/', views.PrefixBulkImportView.as_view(), name='prefix_import'), - path('prefixes/edit/', views.PrefixBulkEditView.as_view(), name='prefix_bulk_edit'), - path('prefixes/delete/', views.PrefixBulkDeleteView.as_view(), name='prefix_bulk_delete'), + path('prefixes/', include(get_model_urls('ipam', 'prefix', detail=False))), path('prefixes//', include(get_model_urls('ipam', 'prefix'))), - # IP ranges - path('ip-ranges/', views.IPRangeListView.as_view(), name='iprange_list'), - path('ip-ranges/add/', views.IPRangeEditView.as_view(), name='iprange_add'), - path('ip-ranges/import/', views.IPRangeBulkImportView.as_view(), name='iprange_import'), - path('ip-ranges/edit/', views.IPRangeBulkEditView.as_view(), name='iprange_bulk_edit'), - path('ip-ranges/delete/', views.IPRangeBulkDeleteView.as_view(), name='iprange_bulk_delete'), + path('ip-ranges/', include(get_model_urls('ipam', 'iprange', detail=False))), path('ip-ranges//', include(get_model_urls('ipam', 'iprange'))), - # IP addresses - path('ip-addresses/', views.IPAddressListView.as_view(), name='ipaddress_list'), - path('ip-addresses/add/', views.IPAddressEditView.as_view(), name='ipaddress_add'), - path('ip-addresses/bulk-add/', views.IPAddressBulkCreateView.as_view(), name='ipaddress_bulk_add'), - path('ip-addresses/import/', views.IPAddressBulkImportView.as_view(), name='ipaddress_import'), - path('ip-addresses/edit/', views.IPAddressBulkEditView.as_view(), name='ipaddress_bulk_edit'), - path('ip-addresses/delete/', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'), - path('ip-addresses/assign/', views.IPAddressAssignView.as_view(), name='ipaddress_assign'), + path('ip-addresses/', include(get_model_urls('ipam', 'ipaddress', detail=False))), path('ip-addresses//', include(get_model_urls('ipam', 'ipaddress'))), - # FHRP groups - path('fhrp-groups/', views.FHRPGroupListView.as_view(), name='fhrpgroup_list'), - path('fhrp-groups/add/', views.FHRPGroupEditView.as_view(), name='fhrpgroup_add'), - path('fhrp-groups/import/', views.FHRPGroupBulkImportView.as_view(), name='fhrpgroup_import'), - path('fhrp-groups/edit/', views.FHRPGroupBulkEditView.as_view(), name='fhrpgroup_bulk_edit'), - path('fhrp-groups/delete/', views.FHRPGroupBulkDeleteView.as_view(), name='fhrpgroup_bulk_delete'), + path('fhrp-groups/', include(get_model_urls('ipam', 'fhrpgroup', detail=False))), path('fhrp-groups//', include(get_model_urls('ipam', 'fhrpgroup'))), - # FHRP group assignments - path('fhrp-group-assignments/add/', views.FHRPGroupAssignmentEditView.as_view(), name='fhrpgroupassignment_add'), + path('fhrp-group-assignments/', include(get_model_urls('ipam', 'fhrpgroupassignment', detail=False))), path('fhrp-group-assignments//', include(get_model_urls('ipam', 'fhrpgroupassignment'))), - # VLAN groups - path('vlan-groups/', views.VLANGroupListView.as_view(), name='vlangroup_list'), - path('vlan-groups/add/', views.VLANGroupEditView.as_view(), name='vlangroup_add'), - path('vlan-groups/import/', views.VLANGroupBulkImportView.as_view(), name='vlangroup_import'), - path('vlan-groups/edit/', views.VLANGroupBulkEditView.as_view(), name='vlangroup_bulk_edit'), - path('vlan-groups/delete/', views.VLANGroupBulkDeleteView.as_view(), name='vlangroup_bulk_delete'), + path('vlan-groups/', include(get_model_urls('ipam', 'vlangroup', detail=False))), path('vlan-groups//', include(get_model_urls('ipam', 'vlangroup'))), - # VLANs - path('vlans/', views.VLANListView.as_view(), name='vlan_list'), - path('vlans/add/', views.VLANEditView.as_view(), name='vlan_add'), - path('vlans/import/', views.VLANBulkImportView.as_view(), name='vlan_import'), - path('vlans/edit/', views.VLANBulkEditView.as_view(), name='vlan_bulk_edit'), - path('vlans/delete/', views.VLANBulkDeleteView.as_view(), name='vlan_bulk_delete'), + path('vlans/', include(get_model_urls('ipam', 'vlan', detail=False))), path('vlans//', include(get_model_urls('ipam', 'vlan'))), - # Service templates - path('service-templates/', views.ServiceTemplateListView.as_view(), name='servicetemplate_list'), - path('service-templates/add/', views.ServiceTemplateEditView.as_view(), name='servicetemplate_add'), - path('service-templates/import/', views.ServiceTemplateBulkImportView.as_view(), name='servicetemplate_import'), - path('service-templates/edit/', views.ServiceTemplateBulkEditView.as_view(), name='servicetemplate_bulk_edit'), - path('service-templates/delete/', views.ServiceTemplateBulkDeleteView.as_view(), name='servicetemplate_bulk_delete'), + path('vlan-translation-policies/', include(get_model_urls('ipam', 'vlantranslationpolicy', detail=False))), + path('vlan-translation-policies//', include(get_model_urls('ipam', 'vlantranslationpolicy'))), + + path('vlan-translation-rules/', include(get_model_urls('ipam', 'vlantranslationrule', detail=False))), + path('vlan-translation-rules//', include(get_model_urls('ipam', 'vlantranslationrule'))), + + path('service-templates/', include(get_model_urls('ipam', 'servicetemplate', detail=False))), path('service-templates//', include(get_model_urls('ipam', 'servicetemplate'))), - # Services - path('services/', views.ServiceListView.as_view(), name='service_list'), - path('services/add/', views.ServiceCreateView.as_view(), name='service_add'), - path('services/import/', views.ServiceBulkImportView.as_view(), name='service_import'), - path('services/edit/', views.ServiceBulkEditView.as_view(), name='service_bulk_edit'), - path('services/delete/', views.ServiceBulkDeleteView.as_view(), name='service_bulk_delete'), + path('services/', include(get_model_urls('ipam', 'service', detail=False))), path('services//', include(get_model_urls('ipam', 'service'))), ] diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 67d56f15e..f145716e9 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -3,12 +3,13 @@ from django.db.models import Prefetch from django.db.models.expressions import RawSQL from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from circuits.models import Provider from dcim.filtersets import InterfaceFilterSet from dcim.forms import InterfaceFilterForm from dcim.models import Interface, Site +from ipam.tables import VLANTranslationRuleTable from netbox.views import generic from tenancy.views import ObjectContactsView from utilities.query import count_related @@ -28,6 +29,7 @@ from .utils import add_requested_prefixes, add_available_ipaddresses, add_availa # VRFs # +@register_model_view(VRF, 'list', path='', detail=False) class VRFListView(generic.ObjectListView): queryset = VRF.objects.all() filterset = filtersets.VRFFilterSet @@ -56,6 +58,7 @@ class VRFView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(VRF, 'add', detail=False) @register_model_view(VRF, 'edit') class VRFEditView(generic.ObjectEditView): queryset = VRF.objects.all() @@ -67,11 +70,13 @@ class VRFDeleteView(generic.ObjectDeleteView): queryset = VRF.objects.all() +@register_model_view(VRF, 'bulk_import', detail=False) class VRFBulkImportView(generic.BulkImportView): queryset = VRF.objects.all() model_form = forms.VRFImportForm +@register_model_view(VRF, 'bulk_edit', path='edit', detail=False) class VRFBulkEditView(generic.BulkEditView): queryset = VRF.objects.all() filterset = filtersets.VRFFilterSet @@ -79,6 +84,7 @@ class VRFBulkEditView(generic.BulkEditView): form = forms.VRFBulkEditForm +@register_model_view(VRF, 'bulk_delete', path='delete', detail=False) class VRFBulkDeleteView(generic.BulkDeleteView): queryset = VRF.objects.all() filterset = filtersets.VRFFilterSet @@ -89,6 +95,7 @@ class VRFBulkDeleteView(generic.BulkDeleteView): # Route targets # +@register_model_view(RouteTarget, 'list', path='', detail=False) class RouteTargetListView(generic.ObjectListView): queryset = RouteTarget.objects.all() filterset = filtersets.RouteTargetFilterSet @@ -101,6 +108,7 @@ class RouteTargetView(generic.ObjectView): queryset = RouteTarget.objects.all() +@register_model_view(RouteTarget, 'add', detail=False) @register_model_view(RouteTarget, 'edit') class RouteTargetEditView(generic.ObjectEditView): queryset = RouteTarget.objects.all() @@ -112,11 +120,13 @@ class RouteTargetDeleteView(generic.ObjectDeleteView): queryset = RouteTarget.objects.all() +@register_model_view(RouteTarget, 'bulk_import', detail=False) class RouteTargetBulkImportView(generic.BulkImportView): queryset = RouteTarget.objects.all() model_form = forms.RouteTargetImportForm +@register_model_view(RouteTarget, 'bulk_edit', path='edit', detail=False) class RouteTargetBulkEditView(generic.BulkEditView): queryset = RouteTarget.objects.all() filterset = filtersets.RouteTargetFilterSet @@ -124,6 +134,7 @@ class RouteTargetBulkEditView(generic.BulkEditView): form = forms.RouteTargetBulkEditForm +@register_model_view(RouteTarget, 'bulk_delete', path='delete', detail=False) class RouteTargetBulkDeleteView(generic.BulkDeleteView): queryset = RouteTarget.objects.all() filterset = filtersets.RouteTargetFilterSet @@ -134,6 +145,7 @@ class RouteTargetBulkDeleteView(generic.BulkDeleteView): # RIRs # +@register_model_view(RIR, 'list', path='', detail=False) class RIRListView(generic.ObjectListView): queryset = RIR.objects.annotate( aggregate_count=count_related(Aggregate, 'rir') @@ -153,6 +165,7 @@ class RIRView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(RIR, 'add', detail=False) @register_model_view(RIR, 'edit') class RIREditView(generic.ObjectEditView): queryset = RIR.objects.all() @@ -164,11 +177,13 @@ class RIRDeleteView(generic.ObjectDeleteView): queryset = RIR.objects.all() +@register_model_view(RIR, 'bulk_import', detail=False) class RIRBulkImportView(generic.BulkImportView): queryset = RIR.objects.all() model_form = forms.RIRImportForm +@register_model_view(RIR, 'bulk_edit', path='edit', detail=False) class RIRBulkEditView(generic.BulkEditView): queryset = RIR.objects.annotate( aggregate_count=count_related(Aggregate, 'rir') @@ -178,6 +193,7 @@ class RIRBulkEditView(generic.BulkEditView): form = forms.RIRBulkEditForm +@register_model_view(RIR, 'bulk_delete', path='delete', detail=False) class RIRBulkDeleteView(generic.BulkDeleteView): queryset = RIR.objects.annotate( aggregate_count=count_related(Aggregate, 'rir') @@ -190,6 +206,7 @@ class RIRBulkDeleteView(generic.BulkDeleteView): # ASN ranges # +@register_model_view(ASNRange, 'list', path='', detail=False) class ASNRangeListView(generic.ObjectListView): queryset = ASNRange.objects.annotate_asn_counts() filterset = filtersets.ASNRangeFilterSet @@ -223,6 +240,7 @@ class ASNRangeASNsView(generic.ObjectChildrenView): ) +@register_model_view(ASNRange, 'add', detail=False) @register_model_view(ASNRange, 'edit') class ASNRangeEditView(generic.ObjectEditView): queryset = ASNRange.objects.all() @@ -234,11 +252,13 @@ class ASNRangeDeleteView(generic.ObjectDeleteView): queryset = ASNRange.objects.all() +@register_model_view(ASNRange, 'bulk_import', detail=False) class ASNRangeBulkImportView(generic.BulkImportView): queryset = ASNRange.objects.all() model_form = forms.ASNRangeImportForm +@register_model_view(ASNRange, 'bulk_edit', path='edit', detail=False) class ASNRangeBulkEditView(generic.BulkEditView): queryset = ASNRange.objects.annotate_asn_counts() filterset = filtersets.ASNRangeFilterSet @@ -246,6 +266,7 @@ class ASNRangeBulkEditView(generic.BulkEditView): form = forms.ASNRangeBulkEditForm +@register_model_view(ASNRange, 'bulk_delete', path='delete', detail=False) class ASNRangeBulkDeleteView(generic.BulkDeleteView): queryset = ASNRange.objects.annotate_asn_counts() filterset = filtersets.ASNRangeFilterSet @@ -256,6 +277,7 @@ class ASNRangeBulkDeleteView(generic.BulkDeleteView): # ASNs # +@register_model_view(ASN, 'list', path='', detail=False) class ASNListView(generic.ObjectListView): queryset = ASN.objects.annotate( site_count=count_related(Site, 'asns'), @@ -283,6 +305,7 @@ class ASNView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(ASN, 'add', detail=False) @register_model_view(ASN, 'edit') class ASNEditView(generic.ObjectEditView): queryset = ASN.objects.all() @@ -294,11 +317,13 @@ class ASNDeleteView(generic.ObjectDeleteView): queryset = ASN.objects.all() +@register_model_view(ASN, 'bulk_import', detail=False) class ASNBulkImportView(generic.BulkImportView): queryset = ASN.objects.all() model_form = forms.ASNImportForm +@register_model_view(ASN, 'bulk_edit', path='edit', detail=False) class ASNBulkEditView(generic.BulkEditView): queryset = ASN.objects.annotate( site_count=count_related(Site, 'asns') @@ -308,6 +333,7 @@ class ASNBulkEditView(generic.BulkEditView): form = forms.ASNBulkEditForm +@register_model_view(ASN, 'bulk_delete', path='delete', detail=False) class ASNBulkDeleteView(generic.BulkDeleteView): queryset = ASN.objects.annotate( site_count=count_related(Site, 'asns') @@ -320,6 +346,7 @@ class ASNBulkDeleteView(generic.BulkDeleteView): # Aggregates # +@register_model_view(Aggregate, 'list', path='', detail=False) class AggregateListView(generic.ObjectListView): queryset = Aggregate.objects.annotate( child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) @@ -352,7 +379,7 @@ class AggregatePrefixesView(generic.ObjectChildrenView): def get_children(self, request, parent): return Prefix.objects.restrict(request.user, 'view').filter( prefix__net_contained_or_equal=str(parent.prefix) - ).prefetch_related('site', 'role', 'tenant', 'tenant__group', 'vlan') + ).prefetch_related('scope', 'role', 'tenant', 'tenant__group', 'vlan') def prep_table_data(self, request, queryset, parent): # Determine whether to show assigned prefixes, available prefixes, or both @@ -370,6 +397,7 @@ class AggregatePrefixesView(generic.ObjectChildrenView): } +@register_model_view(Aggregate, 'add', detail=False) @register_model_view(Aggregate, 'edit') class AggregateEditView(generic.ObjectEditView): queryset = Aggregate.objects.all() @@ -381,11 +409,13 @@ class AggregateDeleteView(generic.ObjectDeleteView): queryset = Aggregate.objects.all() +@register_model_view(Aggregate, 'bulk_import', detail=False) class AggregateBulkImportView(generic.BulkImportView): queryset = Aggregate.objects.all() model_form = forms.AggregateImportForm +@register_model_view(Aggregate, 'bulk_edit', path='edit', detail=False) class AggregateBulkEditView(generic.BulkEditView): queryset = Aggregate.objects.annotate( child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) @@ -395,6 +425,7 @@ class AggregateBulkEditView(generic.BulkEditView): form = forms.AggregateBulkEditForm +@register_model_view(Aggregate, 'bulk_delete', path='delete', detail=False) class AggregateBulkDeleteView(generic.BulkDeleteView): queryset = Aggregate.objects.annotate( child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) @@ -412,6 +443,7 @@ class AggregateContactsView(ObjectContactsView): # Prefix/VLAN roles # +@register_model_view(Role, 'list', path='', detail=False) class RoleListView(generic.ObjectListView): queryset = Role.objects.annotate( prefix_count=count_related(Prefix, 'role'), @@ -433,6 +465,7 @@ class RoleView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(Role, 'add', detail=False) @register_model_view(Role, 'edit') class RoleEditView(generic.ObjectEditView): queryset = Role.objects.all() @@ -444,11 +477,13 @@ class RoleDeleteView(generic.ObjectDeleteView): queryset = Role.objects.all() +@register_model_view(Role, 'bulk_import', detail=False) class RoleBulkImportView(generic.BulkImportView): queryset = Role.objects.all() model_form = forms.RoleImportForm +@register_model_view(Role, 'bulk_edit', path='edit', detail=False) class RoleBulkEditView(generic.BulkEditView): queryset = Role.objects.all() filterset = filtersets.RoleFilterSet @@ -456,6 +491,7 @@ class RoleBulkEditView(generic.BulkEditView): form = forms.RoleBulkEditForm +@register_model_view(Role, 'bulk_delete', path='delete', detail=False) class RoleBulkDeleteView(generic.BulkDeleteView): queryset = Role.objects.all() filterset = filtersets.RoleFilterSet @@ -466,6 +502,7 @@ class RoleBulkDeleteView(generic.BulkDeleteView): # Prefixes # +@register_model_view(Prefix, 'list', path='', detail=False) class PrefixListView(generic.ObjectListView): queryset = Prefix.objects.all() filterset = filtersets.PrefixFilterSet @@ -492,7 +529,7 @@ class PrefixView(generic.ObjectView): ).filter( prefix__net_contains=str(instance.prefix) ).prefetch_related( - 'site', 'role', 'tenant', 'vlan', + 'scope', 'role', 'tenant', 'vlan', ) parent_prefix_table = tables.PrefixTable( list(parent_prefixes), @@ -506,7 +543,7 @@ class PrefixView(generic.ObjectView): ).exclude( pk=instance.pk ).prefetch_related( - 'site', 'role', 'tenant', 'vlan', + 'scope', 'role', 'tenant', 'vlan', ) duplicate_prefix_table = tables.PrefixTable( list(duplicate_prefixes), @@ -538,7 +575,7 @@ class PrefixPrefixesView(generic.ObjectChildrenView): def get_children(self, request, parent): return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related( - 'site', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group' + 'scope', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group' ) def prep_table_data(self, request, queryset, parent): @@ -614,6 +651,7 @@ class PrefixIPAddressesView(generic.ObjectChildrenView): } +@register_model_view(Prefix, 'add', detail=False) @register_model_view(Prefix, 'edit') class PrefixEditView(generic.ObjectEditView): queryset = Prefix.objects.all() @@ -625,11 +663,13 @@ class PrefixDeleteView(generic.ObjectDeleteView): queryset = Prefix.objects.all() +@register_model_view(Prefix, 'bulk_import', detail=False) class PrefixBulkImportView(generic.BulkImportView): queryset = Prefix.objects.all() model_form = forms.PrefixImportForm +@register_model_view(Prefix, 'bulk_edit', path='edit', detail=False) class PrefixBulkEditView(generic.BulkEditView): queryset = Prefix.objects.prefetch_related('vrf__tenant') filterset = filtersets.PrefixFilterSet @@ -637,6 +677,7 @@ class PrefixBulkEditView(generic.BulkEditView): form = forms.PrefixBulkEditForm +@register_model_view(Prefix, 'bulk_delete', path='delete', detail=False) class PrefixBulkDeleteView(generic.BulkDeleteView): queryset = Prefix.objects.prefetch_related('vrf__tenant') filterset = filtersets.PrefixFilterSet @@ -652,6 +693,7 @@ class PrefixContactsView(ObjectContactsView): # IP Ranges # +@register_model_view(IPRange, 'list', path='', detail=False) class IPRangeListView(generic.ObjectListView): queryset = IPRange.objects.all() filterset = filtersets.IPRangeFilterSet @@ -703,6 +745,7 @@ class IPRangeIPAddressesView(generic.ObjectChildrenView): return parent.get_child_ips().restrict(request.user, 'view') +@register_model_view(IPRange, 'add', detail=False) @register_model_view(IPRange, 'edit') class IPRangeEditView(generic.ObjectEditView): queryset = IPRange.objects.all() @@ -714,11 +757,13 @@ class IPRangeDeleteView(generic.ObjectDeleteView): queryset = IPRange.objects.all() +@register_model_view(IPRange, 'bulk_import', detail=False) class IPRangeBulkImportView(generic.BulkImportView): queryset = IPRange.objects.all() model_form = forms.IPRangeImportForm +@register_model_view(IPRange, 'bulk_edit', path='edit', detail=False) class IPRangeBulkEditView(generic.BulkEditView): queryset = IPRange.objects.all() filterset = filtersets.IPRangeFilterSet @@ -726,6 +771,7 @@ class IPRangeBulkEditView(generic.BulkEditView): form = forms.IPRangeBulkEditForm +@register_model_view(IPRange, 'bulk_delete', path='delete', detail=False) class IPRangeBulkDeleteView(generic.BulkDeleteView): queryset = IPRange.objects.all() filterset = filtersets.IPRangeFilterSet @@ -741,6 +787,7 @@ class IPRangeContactsView(ObjectContactsView): # IP addresses # +@register_model_view(IPAddress, 'list', path='', detail=False) class IPAddressListView(generic.ObjectListView): queryset = IPAddress.objects.all() filterset = filtersets.IPAddressFilterSet @@ -787,6 +834,7 @@ class IPAddressView(generic.ObjectView): } +@register_model_view(IPAddress, 'add', detail=False) @register_model_view(IPAddress, 'edit') class IPAddressEditView(generic.ObjectEditView): queryset = IPAddress.objects.all() @@ -817,6 +865,7 @@ class IPAddressEditView(generic.ObjectEditView): # TODO: Standardize or remove this view +@register_model_view(IPAddress, 'assign', path='assign', detail=False) class IPAddressAssignView(generic.ObjectView): """ Search for IPAddresses to be assigned to an Interface. @@ -861,6 +910,7 @@ class IPAddressDeleteView(generic.ObjectDeleteView): queryset = IPAddress.objects.all() +@register_model_view(IPAddress, 'bulk_add', path='bulk-add', detail=False) class IPAddressBulkCreateView(generic.BulkCreateView): queryset = IPAddress.objects.all() form = forms.IPAddressBulkCreateForm @@ -869,11 +919,13 @@ class IPAddressBulkCreateView(generic.BulkCreateView): template_name = 'ipam/ipaddress_bulk_add.html' +@register_model_view(IPAddress, 'bulk_import', detail=False) class IPAddressBulkImportView(generic.BulkImportView): queryset = IPAddress.objects.all() model_form = forms.IPAddressImportForm +@register_model_view(IPAddress, 'bulk_edit', path='edit', detail=False) class IPAddressBulkEditView(generic.BulkEditView): queryset = IPAddress.objects.prefetch_related('vrf__tenant') filterset = filtersets.IPAddressFilterSet @@ -881,6 +933,7 @@ class IPAddressBulkEditView(generic.BulkEditView): form = forms.IPAddressBulkEditForm +@register_model_view(IPAddress, 'bulk_delete', path='delete', detail=False) class IPAddressBulkDeleteView(generic.BulkDeleteView): queryset = IPAddress.objects.prefetch_related('vrf__tenant') filterset = filtersets.IPAddressFilterSet @@ -914,6 +967,7 @@ class IPAddressContactsView(ObjectContactsView): # VLAN groups # +@register_model_view(VLANGroup, 'list', path='', detail=False) class VLANGroupListView(generic.ObjectListView): queryset = VLANGroup.objects.annotate_utilization() filterset = filtersets.VLANGroupFilterSet @@ -931,6 +985,7 @@ class VLANGroupView(GetRelatedModelsMixin, generic.ObjectView): } +@register_model_view(VLANGroup, 'add', detail=False) @register_model_view(VLANGroup, 'edit') class VLANGroupEditView(generic.ObjectEditView): queryset = VLANGroup.objects.all() @@ -942,11 +997,13 @@ class VLANGroupDeleteView(generic.ObjectDeleteView): queryset = VLANGroup.objects.all() +@register_model_view(VLANGroup, 'bulk_import', detail=False) class VLANGroupBulkImportView(generic.BulkImportView): queryset = VLANGroup.objects.all() model_form = forms.VLANGroupImportForm +@register_model_view(VLANGroup, 'bulk_edit', path='edit', detail=False) class VLANGroupBulkEditView(generic.BulkEditView): queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags') filterset = filtersets.VLANGroupFilterSet @@ -954,6 +1011,7 @@ class VLANGroupBulkEditView(generic.BulkEditView): form = forms.VLANGroupBulkEditForm +@register_model_view(VLANGroup, 'bulk_delete', path='delete', detail=False) class VLANGroupBulkDeleteView(generic.BulkDeleteView): queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags') filterset = filtersets.VLANGroupFilterSet @@ -986,10 +1044,127 @@ class VLANGroupVLANsView(generic.ObjectChildrenView): return queryset +# +# VLAN Translation Policies +# + +@register_model_view(VLANTranslationPolicy, 'list', path='', detail=False) +class VLANTranslationPolicyListView(generic.ObjectListView): + queryset = VLANTranslationPolicy.objects.annotate( + rule_count=count_related(VLANTranslationRule, 'policy'), + ) + filterset = filtersets.VLANTranslationPolicyFilterSet + filterset_form = forms.VLANTranslationPolicyFilterForm + table = tables.VLANTranslationPolicyTable + + +@register_model_view(VLANTranslationPolicy) +class VLANTranslationPolicyView(GetRelatedModelsMixin, generic.ObjectView): + queryset = VLANTranslationPolicy.objects.all() + + def get_extra_context(self, request, instance): + vlan_translation_table = VLANTranslationRuleTable( + data=instance.rules.all(), + orderable=False + ) + return { + 'vlan_translation_table': vlan_translation_table, + } + + +@register_model_view(VLANTranslationPolicy, 'add', detail=False) +@register_model_view(VLANTranslationPolicy, 'edit') +class VLANTranslationPolicyEditView(generic.ObjectEditView): + queryset = VLANTranslationPolicy.objects.all() + form = forms.VLANTranslationPolicyForm + + +@register_model_view(VLANTranslationPolicy, 'delete') +class VLANTranslationPolicyDeleteView(generic.ObjectDeleteView): + queryset = VLANTranslationPolicy.objects.all() + + +@register_model_view(VLANTranslationPolicy, 'bulk_import', detail=False) +class VLANTranslationPolicyBulkImportView(generic.BulkImportView): + queryset = VLANTranslationPolicy.objects.all() + model_form = forms.VLANTranslationPolicyImportForm + + +@register_model_view(VLANTranslationPolicy, 'bulk_edit', path='edit', detail=False) +class VLANTranslationPolicyBulkEditView(generic.BulkEditView): + queryset = VLANTranslationPolicy.objects.all() + filterset = filtersets.VLANTranslationPolicyFilterSet + table = tables.VLANTranslationPolicyTable + form = forms.VLANTranslationPolicyBulkEditForm + + +@register_model_view(VLANTranslationPolicy, 'bulk_delete', path='delete', detail=False) +class VLANTranslationPolicyBulkDeleteView(generic.BulkDeleteView): + queryset = VLANTranslationPolicy.objects.all() + filterset = filtersets.VLANTranslationPolicyFilterSet + table = tables.VLANTranslationPolicyTable + + +# +# VLAN Translation Rules +# + +@register_model_view(VLANTranslationRule, 'list', path='', detail=False) +class VLANTranslationRuleListView(generic.ObjectListView): + queryset = VLANTranslationRule.objects.all() + filterset = filtersets.VLANTranslationRuleFilterSet + filterset_form = forms.VLANTranslationRuleFilterForm + table = tables.VLANTranslationRuleTable + + +@register_model_view(VLANTranslationRule) +class VLANTranslationRuleView(GetRelatedModelsMixin, generic.ObjectView): + queryset = VLANTranslationRule.objects.all() + + def get_extra_context(self, request, instance): + return { + 'related_models': self.get_related_models(request, instance), + } + + +@register_model_view(VLANTranslationRule, 'add', detail=False) +@register_model_view(VLANTranslationRule, 'edit') +class VLANTranslationRuleEditView(generic.ObjectEditView): + queryset = VLANTranslationRule.objects.all() + form = forms.VLANTranslationRuleForm + + +@register_model_view(VLANTranslationRule, 'delete') +class VLANTranslationRuleDeleteView(generic.ObjectDeleteView): + queryset = VLANTranslationRule.objects.all() + + +@register_model_view(VLANTranslationRule, 'bulk_import', detail=False) +class VLANTranslationRuleBulkImportView(generic.BulkImportView): + queryset = VLANTranslationRule.objects.all() + model_form = forms.VLANTranslationRuleImportForm + + +@register_model_view(VLANTranslationRule, 'bulk_edit', path='edit', detail=False) +class VLANTranslationRuleBulkEditView(generic.BulkEditView): + queryset = VLANTranslationRule.objects.all() + filterset = filtersets.VLANTranslationRuleFilterSet + table = tables.VLANTranslationRuleTable + form = forms.VLANTranslationRuleBulkEditForm + + +@register_model_view(VLANTranslationRule, 'bulk_delete', path='delete', detail=False) +class VLANTranslationRuleBulkDeleteView(generic.BulkDeleteView): + queryset = VLANTranslationRule.objects.all() + filterset = filtersets.VLANTranslationRuleFilterSet + table = tables.VLANTranslationRuleTable + + # # FHRP groups # +@register_model_view(FHRPGroup, 'list', path='', detail=False) class FHRPGroupListView(generic.ObjectListView): queryset = FHRPGroup.objects.annotate( member_count=count_related(FHRPGroupAssignment, 'group') @@ -1017,6 +1192,7 @@ class FHRPGroupView(generic.ObjectView): } +@register_model_view(FHRPGroup, 'add', detail=False) @register_model_view(FHRPGroup, 'edit') class FHRPGroupEditView(generic.ObjectEditView): queryset = FHRPGroup.objects.all() @@ -1044,11 +1220,13 @@ class FHRPGroupDeleteView(generic.ObjectDeleteView): queryset = FHRPGroup.objects.all() +@register_model_view(FHRPGroup, 'bulk_import', detail=False) class FHRPGroupBulkImportView(generic.BulkImportView): queryset = FHRPGroup.objects.all() model_form = forms.FHRPGroupImportForm +@register_model_view(FHRPGroup, 'bulk_edit', path='edit', detail=False) class FHRPGroupBulkEditView(generic.BulkEditView): queryset = FHRPGroup.objects.all() filterset = filtersets.FHRPGroupFilterSet @@ -1056,6 +1234,7 @@ class FHRPGroupBulkEditView(generic.BulkEditView): form = forms.FHRPGroupBulkEditForm +@register_model_view(FHRPGroup, 'bulk_delete', path='delete', detail=False) class FHRPGroupBulkDeleteView(generic.BulkDeleteView): queryset = FHRPGroup.objects.all() filterset = filtersets.FHRPGroupFilterSet @@ -1066,6 +1245,7 @@ class FHRPGroupBulkDeleteView(generic.BulkDeleteView): # FHRP group assignments # +@register_model_view(FHRPGroupAssignment, 'add', detail=False) @register_model_view(FHRPGroupAssignment, 'edit') class FHRPGroupAssignmentEditView(generic.ObjectEditView): queryset = FHRPGroupAssignment.objects.all() @@ -1094,6 +1274,7 @@ class FHRPGroupAssignmentDeleteView(generic.ObjectDeleteView): # VLANs # +@register_model_view(VLAN, 'list', path='', detail=False) class VLANListView(generic.ObjectListView): queryset = VLAN.objects.all() filterset = filtersets.VLANFilterSet @@ -1152,6 +1333,7 @@ class VLANVMInterfacesView(generic.ObjectChildrenView): return parent.get_vminterfaces().restrict(request.user, 'view') +@register_model_view(VLAN, 'add', detail=False) @register_model_view(VLAN, 'edit') class VLANEditView(generic.ObjectEditView): queryset = VLAN.objects.all() @@ -1164,11 +1346,13 @@ class VLANDeleteView(generic.ObjectDeleteView): queryset = VLAN.objects.all() +@register_model_view(VLAN, 'bulk_import', detail=False) class VLANBulkImportView(generic.BulkImportView): queryset = VLAN.objects.all() model_form = forms.VLANImportForm +@register_model_view(VLAN, 'bulk_edit', path='edit', detail=False) class VLANBulkEditView(generic.BulkEditView): queryset = VLAN.objects.all() filterset = filtersets.VLANFilterSet @@ -1176,6 +1360,7 @@ class VLANBulkEditView(generic.BulkEditView): form = forms.VLANBulkEditForm +@register_model_view(VLAN, 'bulk_delete', path='delete', detail=False) class VLANBulkDeleteView(generic.BulkDeleteView): queryset = VLAN.objects.all() filterset = filtersets.VLANFilterSet @@ -1186,6 +1371,7 @@ class VLANBulkDeleteView(generic.BulkDeleteView): # Service templates # +@register_model_view(ServiceTemplate, 'list', path='', detail=False) class ServiceTemplateListView(generic.ObjectListView): queryset = ServiceTemplate.objects.all() filterset = filtersets.ServiceTemplateFilterSet @@ -1198,6 +1384,7 @@ class ServiceTemplateView(generic.ObjectView): queryset = ServiceTemplate.objects.all() +@register_model_view(ServiceTemplate, 'add', detail=False) @register_model_view(ServiceTemplate, 'edit') class ServiceTemplateEditView(generic.ObjectEditView): queryset = ServiceTemplate.objects.all() @@ -1209,11 +1396,13 @@ class ServiceTemplateDeleteView(generic.ObjectDeleteView): queryset = ServiceTemplate.objects.all() +@register_model_view(ServiceTemplate, 'bulk_import', detail=False) class ServiceTemplateBulkImportView(generic.BulkImportView): queryset = ServiceTemplate.objects.all() model_form = forms.ServiceTemplateImportForm +@register_model_view(ServiceTemplate, 'bulk_edit', path='edit', detail=False) class ServiceTemplateBulkEditView(generic.BulkEditView): queryset = ServiceTemplate.objects.all() filterset = filtersets.ServiceTemplateFilterSet @@ -1221,6 +1410,7 @@ class ServiceTemplateBulkEditView(generic.BulkEditView): form = forms.ServiceTemplateBulkEditForm +@register_model_view(ServiceTemplate, 'bulk_delete', path='delete', detail=False) class ServiceTemplateBulkDeleteView(generic.BulkDeleteView): queryset = ServiceTemplate.objects.all() filterset = filtersets.ServiceTemplateFilterSet @@ -1231,6 +1421,7 @@ class ServiceTemplateBulkDeleteView(generic.BulkDeleteView): # Services # +@register_model_view(Service, 'list', path='', detail=False) class ServiceListView(generic.ObjectListView): queryset = Service.objects.prefetch_related('device', 'virtual_machine') filterset = filtersets.ServiceFilterSet @@ -1243,6 +1434,7 @@ class ServiceView(generic.ObjectView): queryset = Service.objects.all() +@register_model_view(Service, 'add', detail=False) class ServiceCreateView(generic.ObjectEditView): queryset = Service.objects.all() form = forms.ServiceCreateForm @@ -1259,11 +1451,13 @@ class ServiceDeleteView(generic.ObjectDeleteView): queryset = Service.objects.all() +@register_model_view(Service, 'bulk_import', detail=False) class ServiceBulkImportView(generic.BulkImportView): queryset = Service.objects.all() model_form = forms.ServiceImportForm +@register_model_view(Service, 'bulk_edit', path='edit', detail=False) class ServiceBulkEditView(generic.BulkEditView): queryset = Service.objects.prefetch_related('device', 'virtual_machine') filterset = filtersets.ServiceFilterSet @@ -1271,6 +1465,7 @@ class ServiceBulkEditView(generic.BulkEditView): form = forms.ServiceBulkEditForm +@register_model_view(Service, 'bulk_delete', path='delete', detail=False) class ServiceBulkDeleteView(generic.BulkDeleteView): queryset = Service.objects.prefetch_related('device', 'virtual_machine') filterset = filtersets.ServiceFilterSet diff --git a/netbox/manage.py b/netbox/manage.py index 2ce3867f3..737592ae8 100755 --- a/netbox/manage.py +++ b/netbox/manage.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os import sys diff --git a/netbox/netbox/api/pagination.py b/netbox/netbox/api/pagination.py index 5ecade264..f47434ebd 100644 --- a/netbox/netbox/api/pagination.py +++ b/netbox/netbox/api/pagination.py @@ -83,3 +83,28 @@ class StripCountAnnotationsPaginator(OptionalLimitOffsetPagination): cloned_queryset.query.annotations.clear() return cloned_queryset.count() + + +class LimitOffsetListPagination(LimitOffsetPagination): + """ + DRF LimitOffset Paginator but for list instead of queryset + """ + count = 0 + offset = 0 + + def paginate_list(self, data, request, view=None): + self.request = request + self.limit = self.get_limit(request) + self.count = len(data) + self.offset = self.get_offset(request) + + if self.limit is None: + self.limit = self.count + + if self.count == 0 or self.offset > self.count: + return [] + + if self.count > self.limit and self.template is not None: + self.display_page_controls = True + + return data[self.offset:self.offset + self.limit] diff --git a/netbox/netbox/authentication/__init__.py b/netbox/netbox/authentication/__init__.py index f80454f99..83f699e42 100644 --- a/netbox/netbox/authentication/__init__.py +++ b/netbox/netbox/authentication/__init__.py @@ -20,10 +20,10 @@ AUTH_BACKEND_ATTRS = { 'amazon': ('Amazon AWS', 'aws'), 'apple': ('Apple', 'apple'), 'auth0': ('Auth0', None), - 'entraid-oauth2': ('Microsoft Entra ID', 'microsoft'), - 'entraid-b2c-oauth2': ('Microsoft Entra ID', 'microsoft'), - 'entraid-tenant-oauth2': ('Microsoft Entra ID', 'microsoft'), - 'entraid-v2-tenant-oauth2': ('Microsoft Entra ID', 'microsoft'), + 'azuread-oauth2': ('Microsoft Entra ID', 'microsoft'), + 'azuread-b2c-oauth2': ('Microsoft Entra ID', 'microsoft'), + 'azuread-tenant-oauth2': ('Microsoft Entra ID', 'microsoft'), + 'azuread-v2-tenant-oauth2': ('Microsoft Entra ID', 'microsoft'), 'bitbucket': ('BitBucket', 'bitbucket'), 'bitbucket-oauth2': ('BitBucket', 'bitbucket'), 'digitalocean': ('DigitalOcean', 'digital-ocean'), @@ -107,7 +107,7 @@ class ObjectPermissionMixin: return perms def has_perm(self, user_obj, perm, obj=None): - app_label, action, model_name = resolve_permission(perm) + app_label, __, model_name = resolve_permission(perm) # Superusers implicitly have all permissions if user_obj.is_active and user_obj.is_superuser: diff --git a/netbox/netbox/constants.py b/netbox/netbox/constants.py index b8c679ec0..8d20fed45 100644 --- a/netbox/netbox/constants.py +++ b/netbox/netbox/constants.py @@ -31,8 +31,8 @@ ADVISORY_LOCK_KEYS = { # Default view action permission mapping DEFAULT_ACTION_PERMISSIONS = { 'add': {'add'}, - 'import': {'add'}, 'export': {'view'}, + 'bulk_import': {'add'}, 'bulk_edit': {'change'}, 'bulk_delete': {'delete'}, } diff --git a/netbox/netbox/filtersets.py b/netbox/netbox/filtersets.py index 637a40bf1..b8fbe7ad5 100644 --- a/netbox/netbox/filtersets.py +++ b/netbox/netbox/filtersets.py @@ -179,6 +179,8 @@ class BaseFilterSet(django_filters.FilterSet): # The filter field has been explicitly defined on the filterset class so we must manually # create the new filter with the same type because there is no guarantee the defined type # is the same as the default type for the field + if field is None: + raise ValueError('Invalid field name/lookup on {}: {}'.format(existing_filter_name, field_name)) resolve_field(field, lookup_expr) # Will raise FieldLookupError if the lookup is invalid filter_cls = type(existing_filter) if lookup_expr == 'empty': @@ -262,7 +264,9 @@ class ChangeLoggedModelFilterSet(BaseFilterSet): action = { 'created_by_request': Q(action=ObjectChangeActionChoices.ACTION_CREATE), 'updated_by_request': Q(action=ObjectChangeActionChoices.ACTION_UPDATE), - 'modified_by_request': Q(action__in=[ObjectChangeActionChoices.ACTION_CREATE, ObjectChangeActionChoices.ACTION_UPDATE]), + 'modified_by_request': Q( + action__in=[ObjectChangeActionChoices.ACTION_CREATE, ObjectChangeActionChoices.ACTION_UPDATE] + ), }.get(name) request_id = value pks = ObjectChange.objects.filter( diff --git a/netbox/netbox/graphql/views.py b/netbox/netbox/graphql/views.py index 85a01f025..46a073c93 100644 --- a/netbox/netbox/graphql/views.py +++ b/netbox/netbox/graphql/views.py @@ -14,7 +14,6 @@ class NetBoxGraphQLView(GraphQLView): """ Extends strawberry's GraphQLView to support DRF's token-based authentication. """ - graphiql_template = 'graphiql.html' @csrf_exempt def dispatch(self, request, *args, **kwargs): diff --git a/netbox/netbox/jobs.py b/netbox/netbox/jobs.py index 087c24896..3af3af554 100644 --- a/netbox/netbox/jobs.py +++ b/netbox/netbox/jobs.py @@ -2,6 +2,7 @@ import logging from abc import ABC, abstractmethod from datetime import timedelta +from django.core.exceptions import ImproperlyConfigured from django.utils.functional import classproperty from django_pglocks import advisory_lock from rq.timeouts import JobTimeoutException @@ -9,12 +10,30 @@ from rq.timeouts import JobTimeoutException from core.choices import JobStatusChoices from core.models import Job, ObjectType from netbox.constants import ADVISORY_LOCK_KEYS +from netbox.registry import registry __all__ = ( 'JobRunner', + 'system_job', ) +def system_job(interval): + """ + Decorator for registering a `JobRunner` class as system background job. + """ + if type(interval) is not int: + raise ImproperlyConfigured("System job interval must be an integer (minutes).") + + def _wrapper(cls): + registry['system_jobs'][cls] = { + 'interval': interval + } + return cls + + return _wrapper + + class JobRunner(ABC): """ Background Job helper class. @@ -68,8 +87,11 @@ class JobRunner(ABC): finally: if job.interval: new_scheduled_time = (job.scheduled or job.started) + timedelta(minutes=job.interval) + if job.object and getattr(job.object, "python_class", None): + kwargs["job_timeout"] = job.object.python_class.job_timeout cls.enqueue( instance=job.object, + name=job.name, user=job.user, schedule_at=new_scheduled_time, interval=job.interval, @@ -127,7 +149,7 @@ class JobRunner(ABC): if job: # If the job parameters haven't changed, don't schedule a new job and keep the current schedule. Otherwise, # delete the existing job and schedule a new job instead. - if (schedule_at and job.scheduled == schedule_at) and (job.interval == interval): + if (not schedule_at or job.scheduled == schedule_at) and (job.interval == interval): return job job.delete() diff --git a/netbox/netbox/models/mixins.py b/netbox/netbox/models/mixins.py index 804e0b71a..dc706c7c2 100644 --- a/netbox/netbox/models/mixins.py +++ b/netbox/netbox/models/mixins.py @@ -23,6 +23,7 @@ class WeightMixin(models.Model): max_length=50, choices=WeightUnitChoices, blank=True, + null=True, ) # Stores the normalized weight (in grams) for database ordering _abs_weight = models.PositiveBigIntegerField( @@ -64,6 +65,7 @@ class DistanceMixin(models.Model): max_length=50, choices=DistanceUnitChoices, blank=True, + null=True, ) # Stores the normalized distance (in meters) for database ordering _abs_distance = models.DecimalField( @@ -85,7 +87,7 @@ class DistanceMixin(models.Model): # Clear distance_unit if no distance is defined if self.distance is None: - self.distance_unit = '' + self.distance_unit = None super().save(*args, **kwargs) diff --git a/netbox/netbox/navigation/__init__.py b/netbox/netbox/navigation/__init__.py index b4f7dbd9f..75ca8f440 100644 --- a/netbox/netbox/navigation/__init__.py +++ b/netbox/netbox/navigation/__init__.py @@ -60,7 +60,7 @@ class Menu: # Utility functions # -def get_model_item(app_label, model_name, label, actions=('add', 'import')): +def get_model_item(app_label, model_name, label, actions=('add', 'bulk_import')): return MenuItem( link=f'{app_label}:{model_name}_list', link_text=label, @@ -69,7 +69,7 @@ def get_model_item(app_label, model_name, label, actions=('add', 'import')): ) -def get_model_buttons(app_label, model_name, actions=('add', 'import')): +def get_model_buttons(app_label, model_name, actions=('add', 'bulk_import')): buttons = [] if 'add' in actions: @@ -81,10 +81,10 @@ def get_model_buttons(app_label, model_name, actions=('add', 'import')): permissions=[f'{app_label}.add_{model_name}'] ) ) - if 'import' in actions: + if 'bulk_import' in actions: buttons.append( MenuItemButton( - link=f'{app_label}:{model_name}_import', + link=f'{app_label}:{model_name}_bulk_import', title='Import', icon_class='mdi mdi-upload', permissions=[f'{app_label}.add_{model_name}'] diff --git a/netbox/netbox/navigation/menu.py b/netbox/netbox/navigation/menu.py index 9d8ffaaf8..fbbd3d0b1 100644 --- a/netbox/netbox/navigation/menu.py +++ b/netbox/netbox/navigation/menu.py @@ -33,7 +33,7 @@ ORGANIZATION_MENU = Menu( get_model_item('tenancy', 'contact', _('Contacts')), get_model_item('tenancy', 'contactgroup', _('Contact Groups')), get_model_item('tenancy', 'contactrole', _('Contact Roles')), - get_model_item('tenancy', 'contactassignment', _('Contact Assignments'), actions=['import']), + get_model_item('tenancy', 'contactassignment', _('Contact Assignments'), actions=['bulk_import']), ), ), ), @@ -104,6 +104,12 @@ DEVICES_MENU = Menu( get_model_item('dcim', 'inventoryitemrole', _('Inventory Item Roles')), ), ), + MenuGroup( + label=_('Addressing'), + items=( + get_model_item('dcim', 'macaddress', _('MAC Addresses')), + ), + ), ), ) @@ -194,6 +200,8 @@ IPAM_MENU = Menu( items=( get_model_item('ipam', 'vlan', _('VLANs')), get_model_item('ipam', 'vlangroup', _('VLAN Groups')), + get_model_item('ipam', 'vlantranslationpolicy', _('VLAN Translation Policies')), + get_model_item('ipam', 'vlantranslationrule', _('VLAN Translation Rules')), ), ), MenuGroup( @@ -276,6 +284,13 @@ CIRCUITS_MENU = Menu( get_model_item('circuits', 'circuittermination', _('Circuit Terminations')), ), ), + MenuGroup( + label=_('Virtual Circuits'), + items=( + get_model_item('circuits', 'virtualcircuit', _('Virtual Circuits')), + get_model_item('circuits', 'virtualcircuittermination', _('Virtual Circuit Terminations')), + ), + ), MenuGroup( label=_('Providers'), items=( @@ -371,7 +386,7 @@ OPERATIONS_MENU = Menu( label=_('Logging'), items=( get_model_item('extras', 'notificationgroup', _('Notification Groups')), - get_model_item('extras', 'journalentry', _('Journal Entries'), actions=['import']), + get_model_item('extras', 'journalentry', _('Journal Entries'), actions=['bulk_import']), get_model_item('core', 'objectchange', _('Change Log'), actions=[]), ), ), @@ -398,7 +413,7 @@ ADMIN_MENU = Menu( permissions=['users.add_user'] ), MenuItemButton( - link='users:user_import', + link='users:user_bulk_import', title='Import', icon_class='mdi mdi-upload', permissions=['users.add_user'] @@ -418,7 +433,7 @@ ADMIN_MENU = Menu( permissions=['users.add_group'] ), MenuItemButton( - link='users:group_import', + link='users:group_bulk_import', title='Import', icon_class='mdi mdi-upload', permissions=['users.add_group'] diff --git a/netbox/netbox/plugins/__init__.py b/netbox/netbox/plugins/__init__.py index e2f0f22fc..69881a251 100644 --- a/netbox/netbox/plugins/__init__.py +++ b/netbox/netbox/plugins/__init__.py @@ -78,6 +78,7 @@ class PluginConfig(AppConfig): menu_items = None template_extensions = None user_preferences = None + events_pipeline = [] def _load_resource(self, name): # Import from the configured path, if defined. diff --git a/netbox/netbox/plugins/registration.py b/netbox/netbox/plugins/registration.py index c84572794..515405f1b 100644 --- a/netbox/netbox/plugins/registration.py +++ b/netbox/netbox/plugins/registration.py @@ -1,4 +1,5 @@ import inspect +import warnings from django.utils.translation import gettext_lazy as _ from netbox.registry import registry @@ -37,7 +38,12 @@ def register_template_extensions(class_list): # Registration for multiple models models = template_extension.models elif template_extension.model: - # Registration for a single model + # Registration for a single model (deprecated) + warnings.warn( + "PluginTemplateExtension.model is deprecated and will be removed in a future release. Use " + "'models' instead.", + DeprecationWarning + ) models = [template_extension.model] else: # Global registration (no specific models) diff --git a/netbox/netbox/plugins/templates.py b/netbox/netbox/plugins/templates.py index e1f4b7a47..4ea90b4db 100644 --- a/netbox/netbox/plugins/templates.py +++ b/netbox/netbox/plugins/templates.py @@ -21,7 +21,7 @@ class PluginTemplateExtension: * config - Plugin-specific configuration parameters """ models = None - model = None + model = None # Deprecated; use `models` instead def __init__(self, context): self.context = context diff --git a/netbox/netbox/registry.py b/netbox/netbox/registry.py index 0920cbccf..48d7921f2 100644 --- a/netbox/netbox/registry.py +++ b/netbox/netbox/registry.py @@ -30,6 +30,7 @@ registry = Registry({ 'models': collections.defaultdict(set), 'plugins': dict(), 'search': dict(), + 'system_jobs': dict(), 'tables': collections.defaultdict(dict), 'views': collections.defaultdict(dict), 'widgets': dict(), diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 2b057b9ab..0682e713d 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -110,9 +110,9 @@ 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', {}) -EVENTS_PIPELINE = getattr(configuration, 'EVENTS_PIPELINE', ( +EVENTS_PIPELINE = getattr(configuration, 'EVENTS_PIPELINE', [ 'extras.events.process_event_queue', -)) +]) EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', []) FIELD_CHOICES = getattr(configuration, 'FIELD_CHOICES', {}) FILE_UPLOAD_MAX_MEMORY_SIZE = getattr(configuration, 'FILE_UPLOAD_MAX_MEMORY_SIZE', 2621440) @@ -787,6 +787,10 @@ STRAWBERRY_DJANGO = { PLUGIN_CATALOG_URL = 'https://api.netbox.oss.netboxlabs.com/v1/plugins' +EVENTS_PIPELINE = list(EVENTS_PIPELINE) +if 'extras.events.process_event_queue' not in EVENTS_PIPELINE: + EVENTS_PIPELINE.insert(0, 'extras.events.process_event_queue') + # Register any configured plugins for plugin_name in PLUGINS: try: @@ -857,6 +861,13 @@ for plugin_name in PLUGINS: f"{plugin_name}.{queue}": RQ_PARAMS for queue in plugin_config.queues }) + events_pipeline = plugin_config.events_pipeline + if events_pipeline: + if type(events_pipeline) in (list, tuple): + EVENTS_PIPELINE.extend(events_pipeline) + else: + raise ImproperlyConfigured(f"events_pipline in plugin: {plugin_name} must be a list or tuple") + # UNSUPPORTED FUNCTIONALITY: Import any local overrides. try: from .local_settings import * diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py index ee1420396..cf6e1f133 100644 --- a/netbox/netbox/tables/columns.py +++ b/netbox/netbox/tables/columns.py @@ -286,7 +286,8 @@ class ActionsColumn(tables.Column): if len(self.actions) == 1 or (self.split_actions and idx == 0): dropdown_class = attrs.css_class button = ( - f'' + f'' f'' ) @@ -303,7 +304,8 @@ class ActionsColumn(tables.Column): html += ( f'' f' {button}' - f' ' + f' ' f' {toggle_text}' f' ' f'' diff --git a/netbox/netbox/tests/dummy_plugin/__init__.py b/netbox/netbox/tests/dummy_plugin/__init__.py index 3ade8f9df..2ca7c290c 100644 --- a/netbox/netbox/tests/dummy_plugin/__init__.py +++ b/netbox/netbox/tests/dummy_plugin/__init__.py @@ -17,6 +17,14 @@ class DummyPluginConfig(PluginConfig): 'testing-medium', 'testing-high' ] + events_pipeline = [ + 'netbox.tests.dummy_plugin.events.process_events_queue' + ] + + def ready(self): + super().ready() + + from . import jobs # noqa: F401 config = DummyPluginConfig diff --git a/netbox/netbox/tests/dummy_plugin/events.py b/netbox/netbox/tests/dummy_plugin/events.py new file mode 100644 index 000000000..934594643 --- /dev/null +++ b/netbox/netbox/tests/dummy_plugin/events.py @@ -0,0 +1,2 @@ +def process_events_queue(events): + pass diff --git a/netbox/netbox/tests/dummy_plugin/jobs.py b/netbox/netbox/tests/dummy_plugin/jobs.py new file mode 100644 index 000000000..3b9dc7a5f --- /dev/null +++ b/netbox/netbox/tests/dummy_plugin/jobs.py @@ -0,0 +1,9 @@ +from core.choices import JobIntervalChoices +from netbox.jobs import JobRunner, system_job + + +@system_job(interval=JobIntervalChoices.INTERVAL_HOURLY) +class DummySystemJob(JobRunner): + + def run(self, *args, **kwargs): + pass diff --git a/netbox/netbox/tests/test_authentication.py b/netbox/netbox/tests/test_authentication.py index ae6d3f4c2..9eb21661d 100644 --- a/netbox/netbox/tests/test_authentication.py +++ b/netbox/netbox/tests/test_authentication.py @@ -4,12 +4,10 @@ from django.conf import settings from django.test import Client from django.test.utils import override_settings from django.urls import reverse -from netaddr import IPNetwork from rest_framework.test import APIClient from core.models import ObjectType -from dcim.models import Site -from ipam.models import Prefix +from dcim.models import Rack, Site from users.models import Group, ObjectPermission, Token, User from utilities.testing import TestCase from utilities.testing.api import APITestCase @@ -410,18 +408,18 @@ class ObjectPermissionAPIViewTestCase(TestCase): ) Site.objects.bulk_create(cls.sites) - cls.prefixes = ( - Prefix(prefix=IPNetwork('10.0.0.0/24'), site=cls.sites[0]), - Prefix(prefix=IPNetwork('10.0.1.0/24'), site=cls.sites[0]), - Prefix(prefix=IPNetwork('10.0.2.0/24'), site=cls.sites[0]), - Prefix(prefix=IPNetwork('10.0.3.0/24'), site=cls.sites[1]), - Prefix(prefix=IPNetwork('10.0.4.0/24'), site=cls.sites[1]), - Prefix(prefix=IPNetwork('10.0.5.0/24'), site=cls.sites[1]), - Prefix(prefix=IPNetwork('10.0.6.0/24'), site=cls.sites[2]), - Prefix(prefix=IPNetwork('10.0.7.0/24'), site=cls.sites[2]), - Prefix(prefix=IPNetwork('10.0.8.0/24'), site=cls.sites[2]), + cls.racks = ( + Rack(name='Rack 1', site=cls.sites[0]), + Rack(name='Rack 2', site=cls.sites[0]), + Rack(name='Rack 3', site=cls.sites[0]), + Rack(name='Rack 4', site=cls.sites[1]), + Rack(name='Rack 5', site=cls.sites[1]), + Rack(name='Rack 6', site=cls.sites[1]), + Rack(name='Rack 7', site=cls.sites[2]), + Rack(name='Rack 8', site=cls.sites[2]), + Rack(name='Rack 9', site=cls.sites[2]), ) - Prefix.objects.bulk_create(cls.prefixes) + Rack.objects.bulk_create(cls.racks) def setUp(self): """ @@ -435,8 +433,7 @@ class ObjectPermissionAPIViewTestCase(TestCase): def test_get_object(self): # Attempt to retrieve object without permission - url = reverse('ipam-api:prefix-detail', - kwargs={'pk': self.prefixes[0].pk}) + url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk}) response = self.client.get(url, **self.header) self.assertEqual(response.status_code, 403) @@ -448,23 +445,21 @@ class ObjectPermissionAPIViewTestCase(TestCase): ) obj_perm.save() obj_perm.users.add(self.user) - obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix)) + obj_perm.object_types.add(ObjectType.objects.get_for_model(Rack)) # Retrieve permitted object - url = reverse('ipam-api:prefix-detail', - kwargs={'pk': self.prefixes[0].pk}) + url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk}) response = self.client.get(url, **self.header) self.assertEqual(response.status_code, 200) # Attempt to retrieve non-permitted object - url = reverse('ipam-api:prefix-detail', - kwargs={'pk': self.prefixes[3].pk}) + url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[3].pk}) response = self.client.get(url, **self.header) self.assertEqual(response.status_code, 404) @override_settings(EXEMPT_VIEW_PERMISSIONS=[]) def test_list_objects(self): - url = reverse('ipam-api:prefix-list') + url = reverse('dcim-api:rack-list') # Attempt to list objects without permission response = self.client.get(url, **self.header) @@ -478,7 +473,7 @@ class ObjectPermissionAPIViewTestCase(TestCase): ) obj_perm.save() obj_perm.users.add(self.user) - obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix)) + obj_perm.object_types.add(ObjectType.objects.get_for_model(Rack)) # Retrieve all objects. Only permitted objects should be returned. response = self.client.get(url, **self.header) @@ -487,12 +482,12 @@ class ObjectPermissionAPIViewTestCase(TestCase): @override_settings(EXEMPT_VIEW_PERMISSIONS=[]) def test_create_object(self): - url = reverse('ipam-api:prefix-list') + url = reverse('dcim-api:rack-list') data = { - 'prefix': '10.0.9.0/24', + 'name': 'Rack 10', 'site': self.sites[1].pk, } - initial_count = Prefix.objects.count() + initial_count = Rack.objects.count() # Attempt to create an object without permission response = self.client.post(url, data, format='json', **self.header) @@ -506,26 +501,25 @@ class ObjectPermissionAPIViewTestCase(TestCase): ) obj_perm.save() obj_perm.users.add(self.user) - obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix)) + obj_perm.object_types.add(ObjectType.objects.get_for_model(Rack)) # Attempt to create a non-permitted object response = self.client.post(url, data, format='json', **self.header) self.assertEqual(response.status_code, 403) - self.assertEqual(Prefix.objects.count(), initial_count) + self.assertEqual(Rack.objects.count(), initial_count) # Create a permitted object data['site'] = self.sites[0].pk response = self.client.post(url, data, format='json', **self.header) self.assertEqual(response.status_code, 201) - self.assertEqual(Prefix.objects.count(), initial_count + 1) + self.assertEqual(Rack.objects.count(), initial_count + 1) @override_settings(EXEMPT_VIEW_PERMISSIONS=[]) def test_edit_object(self): # Attempt to edit an object without permission data = {'site': self.sites[0].pk} - url = reverse('ipam-api:prefix-detail', - kwargs={'pk': self.prefixes[0].pk}) + url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk}) response = self.client.patch(url, data, format='json', **self.header) self.assertEqual(response.status_code, 403) @@ -537,26 +531,23 @@ class ObjectPermissionAPIViewTestCase(TestCase): ) obj_perm.save() obj_perm.users.add(self.user) - obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix)) + obj_perm.object_types.add(ObjectType.objects.get_for_model(Rack)) # Attempt to edit a non-permitted object data = {'site': self.sites[0].pk} - url = reverse('ipam-api:prefix-detail', - kwargs={'pk': self.prefixes[3].pk}) + url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[3].pk}) response = self.client.patch(url, data, format='json', **self.header) self.assertEqual(response.status_code, 404) # Edit a permitted object data['status'] = 'reserved' - url = reverse('ipam-api:prefix-detail', - kwargs={'pk': self.prefixes[0].pk}) + url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk}) response = self.client.patch(url, data, format='json', **self.header) self.assertEqual(response.status_code, 200) # Attempt to modify a permitted object to a non-permitted object data['site'] = self.sites[1].pk - url = reverse('ipam-api:prefix-detail', - kwargs={'pk': self.prefixes[0].pk}) + url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk}) response = self.client.patch(url, data, format='json', **self.header) self.assertEqual(response.status_code, 403) @@ -564,8 +555,7 @@ class ObjectPermissionAPIViewTestCase(TestCase): def test_delete_object(self): # Attempt to delete an object without permission - url = reverse('ipam-api:prefix-detail', - kwargs={'pk': self.prefixes[0].pk}) + url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk}) response = self.client.delete(url, format='json', **self.header) self.assertEqual(response.status_code, 403) @@ -577,16 +567,14 @@ class ObjectPermissionAPIViewTestCase(TestCase): ) obj_perm.save() obj_perm.users.add(self.user) - obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix)) + obj_perm.object_types.add(ObjectType.objects.get_for_model(Rack)) # Attempt to delete a non-permitted object - url = reverse('ipam-api:prefix-detail', - kwargs={'pk': self.prefixes[3].pk}) + url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[3].pk}) response = self.client.delete(url, format='json', **self.header) self.assertEqual(response.status_code, 404) # Delete a permitted object - url = reverse('ipam-api:prefix-detail', - kwargs={'pk': self.prefixes[0].pk}) + url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk}) response = self.client.delete(url, format='json', **self.header) self.assertEqual(response.status_code, 204) diff --git a/netbox/netbox/tests/test_import.py b/netbox/netbox/tests/test_import.py index 16711ef72..690a6dc14 100644 --- a/netbox/netbox/tests/test_import.py +++ b/netbox/netbox/tests/test_import.py @@ -37,7 +37,7 @@ class CSVImportTestCase(ModelViewTestCase): } # Form validation should fail with invalid header present - self.assertHttpStatus(self.client.post(self._get_url('import'), data), 200) + self.assertHttpStatus(self.client.post(self._get_url('bulk_import'), data), 200) self.assertEqual(Region.objects.count(), 0) # Correct the CSV header name @@ -45,7 +45,7 @@ class CSVImportTestCase(ModelViewTestCase): data['data'] = self._get_csv_data(csv_data) # Validation should succeed - self.assertHttpStatus(self.client.post(self._get_url('import'), data), 302) + self.assertHttpStatus(self.client.post(self._get_url('bulk_import'), data), 302) self.assertEqual(Region.objects.count(), 3) @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) @@ -71,10 +71,10 @@ class CSVImportTestCase(ModelViewTestCase): obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) # Try GET with model-level permission - self.assertHttpStatus(self.client.get(self._get_url('import')), 200) + self.assertHttpStatus(self.client.get(self._get_url('bulk_import')), 200) # Test POST with permission - self.assertHttpStatus(self.client.post(self._get_url('import'), data), 302) + self.assertHttpStatus(self.client.post(self._get_url('bulk_import'), data), 302) regions = Region.objects.all() self.assertEqual(regions.count(), 4) self.assertEqual( @@ -111,10 +111,10 @@ class CSVImportTestCase(ModelViewTestCase): obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) # Try GET with model-level permission - self.assertHttpStatus(self.client.get(self._get_url('import')), 200) + self.assertHttpStatus(self.client.get(self._get_url('bulk_import')), 200) # Test POST with permission - self.assertHttpStatus(self.client.post(self._get_url('import'), data), 200) + self.assertHttpStatus(self.client.post(self._get_url('bulk_import'), data), 200) self.assertEqual(Region.objects.count(), 0) @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) @@ -138,6 +138,6 @@ class CSVImportTestCase(ModelViewTestCase): ) cf.object_types.set([ObjectType.objects.get_for_model(self.model)]) - self.assertHttpStatus(self.client.post(self._get_url('import'), data), 302) + self.assertHttpStatus(self.client.post(self._get_url('bulk_import'), data), 302) region = Region.objects.get(slug='region-1') self.assertEqual(region.cf['tcf'], 'def-cf-text') diff --git a/netbox/netbox/tests/test_jobs.py b/netbox/netbox/tests/test_jobs.py index cb3024038..e3e24a235 100644 --- a/netbox/netbox/tests/test_jobs.py +++ b/netbox/netbox/tests/test_jobs.py @@ -5,7 +5,7 @@ from django.utils import timezone from django_rq import get_queue from ..jobs import * -from core.models import Job +from core.models import DataSource, Job from core.choices import JobStatusChoices @@ -68,7 +68,7 @@ class EnqueueTest(JobRunnerTestCase): """ def test_enqueue(self): - instance = Job() + instance = DataSource() for i in range(1, 3): job = TestJobRunner.enqueue(instance, schedule_at=self.get_schedule_at()) @@ -76,13 +76,13 @@ class EnqueueTest(JobRunnerTestCase): self.assertEqual(TestJobRunner.get_jobs(instance).count(), i) def test_enqueue_once(self): - job = TestJobRunner.enqueue_once(instance=Job(), schedule_at=self.get_schedule_at()) + job = TestJobRunner.enqueue_once(instance=DataSource(), schedule_at=self.get_schedule_at()) self.assertIsInstance(job, Job) self.assertEqual(job.name, TestJobRunner.__name__) def test_enqueue_once_twice_same(self): - instance = Job() + instance = DataSource() schedule_at = self.get_schedule_at() job1 = TestJobRunner.enqueue_once(instance, schedule_at=schedule_at) job2 = TestJobRunner.enqueue_once(instance, schedule_at=schedule_at) @@ -90,8 +90,17 @@ class EnqueueTest(JobRunnerTestCase): self.assertEqual(job1, job2) self.assertEqual(TestJobRunner.get_jobs(instance).count(), 1) + def test_enqueue_once_twice_same_no_schedule_at(self): + instance = DataSource() + schedule_at = self.get_schedule_at() + job1 = TestJobRunner.enqueue_once(instance, schedule_at=schedule_at) + job2 = TestJobRunner.enqueue_once(instance) + + self.assertEqual(job1, job2) + self.assertEqual(TestJobRunner.get_jobs(instance).count(), 1) + def test_enqueue_once_twice_different_schedule_at(self): - instance = Job() + instance = DataSource() job1 = TestJobRunner.enqueue_once(instance, schedule_at=self.get_schedule_at()) job2 = TestJobRunner.enqueue_once(instance, schedule_at=self.get_schedule_at(2)) @@ -100,7 +109,7 @@ class EnqueueTest(JobRunnerTestCase): self.assertEqual(TestJobRunner.get_jobs(instance).count(), 1) def test_enqueue_once_twice_different_interval(self): - instance = Job() + instance = DataSource() schedule_at = self.get_schedule_at() job1 = TestJobRunner.enqueue_once(instance, schedule_at=schedule_at) job2 = TestJobRunner.enqueue_once(instance, schedule_at=schedule_at, interval=60) @@ -112,7 +121,7 @@ class EnqueueTest(JobRunnerTestCase): self.assertEqual(TestJobRunner.get_jobs(instance).count(), 1) def test_enqueue_once_with_enqueue(self): - instance = Job() + instance = DataSource() job1 = TestJobRunner.enqueue_once(instance, schedule_at=self.get_schedule_at(2)) job2 = TestJobRunner.enqueue(instance, schedule_at=self.get_schedule_at()) @@ -120,10 +129,37 @@ class EnqueueTest(JobRunnerTestCase): self.assertEqual(TestJobRunner.get_jobs(instance).count(), 2) def test_enqueue_once_after_enqueue(self): - instance = Job() + instance = DataSource() job1 = TestJobRunner.enqueue(instance, schedule_at=self.get_schedule_at()) job2 = TestJobRunner.enqueue_once(instance, schedule_at=self.get_schedule_at(2)) self.assertNotEqual(job1, job2) self.assertRaises(Job.DoesNotExist, job1.refresh_from_db) self.assertEqual(TestJobRunner.get_jobs(instance).count(), 1) + + +class SystemJobTest(JobRunnerTestCase): + """ + Test that system jobs can be scheduled. + + General functionality already tested by `JobRunnerTest` and `EnqueueTest`. + """ + + def test_scheduling(self): + # Can job be enqueued? + job = TestJobRunner.enqueue(schedule_at=self.get_schedule_at()) + self.assertIsInstance(job, Job) + self.assertEqual(TestJobRunner.get_jobs().count(), 1) + + # Can job be deleted again? + job.delete() + self.assertRaises(Job.DoesNotExist, job.refresh_from_db) + self.assertEqual(TestJobRunner.get_jobs().count(), 0) + + def test_enqueue_once(self): + schedule_at = self.get_schedule_at() + job1 = TestJobRunner.enqueue_once(schedule_at=schedule_at) + job2 = TestJobRunner.enqueue_once(schedule_at=schedule_at) + + self.assertEqual(job1, job2) + self.assertEqual(TestJobRunner.get_jobs().count(), 1) diff --git a/netbox/netbox/tests/test_plugins.py b/netbox/netbox/tests/test_plugins.py index ba44378c5..264c8e6f9 100644 --- a/netbox/netbox/tests/test_plugins.py +++ b/netbox/netbox/tests/test_plugins.py @@ -5,8 +5,10 @@ from django.core.exceptions import ImproperlyConfigured from django.test import Client, TestCase, override_settings from django.urls import reverse +from core.choices import JobIntervalChoices from netbox.tests.dummy_plugin import config as dummy_config from netbox.tests.dummy_plugin.data_backends import DummyBackend +from netbox.tests.dummy_plugin.jobs import DummySystemJob from netbox.plugins.navigation import PluginMenu from netbox.plugins.utils import get_plugin_config from netbox.graphql.schema import Query @@ -130,6 +132,13 @@ class PluginTest(TestCase): self.assertIn('dummy', registry['data_backends']) self.assertIs(registry['data_backends']['dummy'], DummyBackend) + def test_system_jobs(self): + """ + Check registered system jobs. + """ + self.assertIn(DummySystemJob, registry['system_jobs']) + self.assertEqual(registry['system_jobs'][DummySystemJob]['interval'], JobIntervalChoices.INTERVAL_HOURLY) + def test_queues(self): """ Check that plugin queues are registered with the accurate name. @@ -203,3 +212,9 @@ class PluginTest(TestCase): self.assertEqual(get_plugin_config(plugin, 'foo'), 123) self.assertEqual(get_plugin_config(plugin, 'bar'), None) self.assertEqual(get_plugin_config(plugin, 'bar', default=456), 456) + + def test_events_pipeline(self): + """ + Check that events pipeline is registered. + """ + self.assertIn('netbox.tests.dummy_plugin.events.process_events_queue', settings.EVENTS_PIPELINE) diff --git a/netbox/netbox/tests/test_views.py b/netbox/netbox/tests/test_views.py index ccba73baa..50cfa5755 100644 --- a/netbox/netbox/tests/test_views.py +++ b/netbox/netbox/tests/test_views.py @@ -1,7 +1,7 @@ import urllib.parse from django.urls import reverse -from django.test import override_settings +from django.test import Client, override_settings from dcim.models import Site from netbox.constants import EMPTY_TABLE_TEXT @@ -74,3 +74,21 @@ class SearchViewTestCase(TestCase): self.assertHttpStatus(response, 200) content = str(response.content) self.assertIn(EMPTY_TABLE_TEXT, content) + + +class MediaViewTestCase(TestCase): + + def test_media_login_required(self): + url = reverse('media', kwargs={'path': 'foo.txt'}) + response = Client().get(url) + + # Unauthenticated request should redirect to login page + self.assertHttpStatus(response, 302) + + @override_settings(LOGIN_REQUIRED=False) + def test_media_login_not_required(self): + url = reverse('media', kwargs={'path': 'foo.txt'}) + response = Client().get(url) + + # Unauthenticated request should return a 404 (not found) + self.assertHttpStatus(response, 404) diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py index 08c9a46a8..90d70a357 100644 --- a/netbox/netbox/urls.py +++ b/netbox/netbox/urls.py @@ -2,7 +2,6 @@ from django.conf import settings from django.conf.urls import include from django.urls import path from django.views.decorators.cache import cache_page -from django.views.static import serve from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView from account.views import LoginView, LogoutView @@ -10,7 +9,7 @@ from netbox.api.views import APIRootView, StatusView from netbox.graphql.schema import schema from netbox.graphql.views import NetBoxGraphQLView from netbox.plugins.urls import plugin_patterns, plugin_api_patterns -from netbox.views import HomeView, StaticMediaFailureView, SearchView, htmx +from netbox.views import HomeView, MediaView, StaticMediaFailureView, SearchView, htmx _patterns = [ @@ -69,7 +68,7 @@ _patterns = [ path('graphql/', NetBoxGraphQLView.as_view(schema=schema), name='graphql'), # Serving static media in Django to pipe it through LoginRequiredMiddleware - path('media/', serve, {'document_root': settings.MEDIA_ROOT}), + path('media/', MediaView.as_view(), name='media'), path('media-failure/', StaticMediaFailureView.as_view(), name='media_failure'), # Plugins diff --git a/netbox/netbox/views/errors.py b/netbox/netbox/views/errors.py index 9e8ed5a3a..5872a59cd 100644 --- a/netbox/netbox/views/errors.py +++ b/netbox/netbox/views/errors.py @@ -49,7 +49,7 @@ def handler_500(request, template_name=ERROR_500_TEMPLATE_NAME): template = loader.get_template(template_name) except TemplateDoesNotExist: return HttpResponseServerError('

    Server Error (500)

    ', content_type='text/html') - type_, error, traceback = sys.exc_info() + type_, error = sys.exc_info()[:2] return HttpResponseServerError(template.render({ 'error': error, diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index d8c4c5e27..521758102 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -3,7 +3,7 @@ import re from copy import deepcopy from django.contrib import messages -from django.contrib.contenttypes.fields import GenericRel +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRel from django.contrib.contenttypes.models import ContentType from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError from django.db import transaction, IntegrityError @@ -567,6 +567,17 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView): def get_required_permission(self): return get_permission_for_model(self.queryset.model, 'change') + def post_save_operations(self, form, obj): + """ + This method is called for each object in _update_objects. Override to perform additional object-level + operations that are specific to a particular ModelForm. + """ + # Add/remove tags + if form.cleaned_data.get('add_tags', None): + obj.tags.add(*form.cleaned_data['add_tags']) + if form.cleaned_data.get('remove_tags', None): + obj.tags.remove(*form.cleaned_data['remove_tags']) + def _update_objects(self, form, request): custom_fields = getattr(form, 'custom_fields', {}) standard_fields = [ @@ -602,7 +613,10 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView): for name, model_field in model_fields.items(): # Handle nullification if name in form.nullable_fields and name in nullified_fields: - setattr(obj, name, None if model_field.null else '') + if type(model_field) is GenericForeignKey: + setattr(obj, name, None) + else: + setattr(obj, name, None if model_field.null else '') # Normal fields elif name in form.changed_data: setattr(obj, name, form.cleaned_data[name]) @@ -635,11 +649,7 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView): elif form.cleaned_data[name]: getattr(obj, name).set(form.cleaned_data[name]) - # Add/remove tags - if form.cleaned_data.get('add_tags', None): - obj.tags.add(*form.cleaned_data['add_tags']) - if form.cleaned_data.get('remove_tags', None): - obj.tags.remove(*form.cleaned_data['remove_tags']) + self.post_save_operations(form, obj) # Rebuild the tree for MPTT models if issubclass(self.queryset.model, MPTTModel): @@ -1011,7 +1021,8 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView): form.add_error(field, '{}: {}'.format(obj, ', '.join(e))) # Enforce object-level permissions - if self.queryset.filter(pk__in=[obj.pk for obj in new_components]).count() != len(new_components): + component_ids = [obj.pk for obj in new_components] + if self.queryset.filter(pk__in=component_ids).count() != len(new_components): raise PermissionsViolation except IntegrityError: diff --git a/netbox/netbox/views/generic/feature_views.py b/netbox/netbox/views/generic/feature_views.py index 451c9c01d..1e17d5354 100644 --- a/netbox/netbox/views/generic/feature_views.py +++ b/netbox/netbox/views/generic/feature_views.py @@ -4,7 +4,7 @@ from django.contrib import messages from django.db import transaction from django.db.models import Q from django.shortcuts import get_object_or_404, redirect, render -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from django.views.generic import View from core.models import Job, ObjectChange @@ -143,7 +143,12 @@ class ObjectJobsView(ConditionalLoginRequiredMixin, View): """ Render a list of all Job assigned to an object. For example: - path('data-sources//jobs/', ObjectJobsView.as_view(), name='datasource_jobs', kwargs={'model': DataSource}), + path( + 'data-sources//jobs/', + ObjectJobsView.as_view(), + name='datasource_jobs', + kwargs={'model': DataSource} + ) Attributes: base_template: The name of the template to extend. If not provided, "{app}/{model}.html" will be used. diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 0686e52b7..fb554ca4f 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -233,18 +233,23 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView): form = self.form(instance=obj, initial=initial_data) restrict_form_fields(form, request.user) - # If this is an HTMX request, return only the rendered form HTML - if htmx_partial(request): - return render(request, self.htmx_template_name, { - 'model': model, - 'object': obj, - 'form': form, - }) - - return render(request, self.template_name, { + context = { 'model': model, 'object': obj, 'form': form, + } + + # If the form is being displayed within a "quick add" widget, + # use the appropriate template + if request.GET.get('_quickadd'): + return render(request, 'htmx/quick_add.html', context) + + # If this is an HTMX request, return only the rendered form HTML + if htmx_partial(request): + return render(request, self.htmx_template_name, context) + + return render(request, self.template_name, { + **context, 'return_url': self.get_return_url(request, obj), 'prerequisite_model': get_prerequisite_model(self.queryset), **self.get_extra_context(request, obj), @@ -259,6 +264,7 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView): """ logger = logging.getLogger('netbox.views.ObjectEditView') obj = self.get_object(**kwargs) + model = self.queryset.model # Take a snapshot for change logging (if editing an existing object) if obj.pk and hasattr(obj, 'snapshot'): @@ -292,6 +298,12 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView): msg = f'{msg} {obj}' messages.success(request, msg) + # Object was created via "quick add" modal + if '_quickadd' in request.POST: + return render(request, 'htmx/quick_add_created.html', { + 'object': obj, + }) + # If adding another object, redirect back to the edit form if '_addanother' in request.POST: redirect_url = request.path @@ -324,12 +336,19 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView): else: logger.debug("Form validation failed") - return render(request, self.template_name, { + context = { + 'model': model, 'object': obj, 'form': form, 'return_url': self.get_return_url(request, obj), **self.get_extra_context(request, obj), - }) + } + + # Form was submitted via a "quick add" widget + if '_quickadd' in request.POST: + return render(request, 'htmx/quick_add.html', context) + + return render(request, self.template_name, context) class ObjectDeleteView(GetReturnURLMixin, BaseObjectView): diff --git a/netbox/netbox/views/misc.py b/netbox/netbox/views/misc.py index c584e99e4..f28b0f7b1 100644 --- a/netbox/netbox/views/misc.py +++ b/netbox/netbox/views/misc.py @@ -8,6 +8,7 @@ from django.core.cache import cache from django.shortcuts import redirect, render from django.utils.translation import gettext_lazy as _ from django.views.generic import View +from django.views.static import serve from django_tables2 import RequestConfig from packaging import version @@ -23,6 +24,7 @@ from utilities.views import ConditionalLoginRequiredMixin __all__ = ( 'HomeView', + 'MediaView', 'SearchView', ) @@ -115,3 +117,11 @@ class SearchView(ConditionalLoginRequiredMixin, View): 'form': form, 'table': table, }) + + +class MediaView(ConditionalLoginRequiredMixin, View): + """ + Wrap Django's serve() view to enforce LOGIN_REQUIRED for static media. + """ + def get(self, request, path): + return serve(request, path, document_root=settings.MEDIA_ROOT) diff --git a/netbox/project-static/dist/graphiql/graphiql.min.js b/netbox/project-static/dist/graphiql/graphiql.min.js index 862ce3a80..03d4ac1e1 100644 --- a/netbox/project-static/dist/graphiql/graphiql.min.js +++ b/netbox/project-static/dist/graphiql/graphiql.min.js @@ -22986,17 +22986,79 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.GraphQLError = void 0; +exports.formatError = formatError; +exports.printError = printError; var _isObjectLike = __webpack_require__(/*! ../jsutils/isObjectLike.mjs */ "../../../node_modules/graphql/jsutils/isObjectLike.mjs"); var _location = __webpack_require__(/*! ../language/location.mjs */ "../../../node_modules/graphql/language/location.mjs"); var _printLocation = __webpack_require__(/*! ../language/printLocation.mjs */ "../../../node_modules/graphql/language/printLocation.mjs"); +function toNormalizedOptions(args) { + const firstArg = args[0]; + if (firstArg == null || 'kind' in firstArg || 'length' in firstArg) { + return { + nodes: firstArg, + source: args[1], + positions: args[2], + path: args[3], + originalError: args[4], + extensions: args[5] + }; + } + return firstArg; +} /** * A GraphQLError describes an Error found during the parse, validate, or * execute phases of performing a GraphQL operation. In addition to a message * and stack trace, it also includes information about the locations in a * GraphQL document and/or execution result that correspond to the Error. */ + class GraphQLError extends Error { - constructor(message, options = {}) { + /** + * An array of `{ line, column }` locations within the source GraphQL document + * which correspond to this error. + * + * Errors during validation often contain multiple locations, for example to + * point out two things with the same name. Errors during execution include a + * single location, the field which produced the error. + * + * Enumerable, and appears in the result of JSON.stringify(). + */ + + /** + * An array describing the JSON-path into the execution response which + * corresponds to this error. Only included for errors during execution. + * + * Enumerable, and appears in the result of JSON.stringify(). + */ + + /** + * An array of GraphQL AST Nodes corresponding to this error. + */ + + /** + * The source GraphQL document for the first location of this error. + * + * Note that if this Error represents more than one node, the source may not + * represent nodes after the first node. + */ + + /** + * An array of character offsets within the source GraphQL document + * which correspond to this error. + */ + + /** + * The original error thrown from a field resolver during execution. + */ + + /** + * Extension fields to add to the formatted error. + */ + + /** + * @deprecated Please use the `GraphQLErrorOptions` constructor overload instead. + */ + constructor(message, ...rawArgs) { var _this$nodes, _nodeLocations$, _ref; const { nodes, @@ -23005,22 +23067,22 @@ class GraphQLError extends Error { path, originalError, extensions - } = options; + } = toNormalizedOptions(rawArgs); super(message); this.name = 'GraphQLError'; this.path = path !== null && path !== void 0 ? path : undefined; - this.originalError = originalError !== null && originalError !== void 0 ? originalError : undefined; - // Compute list of blame nodes. + this.originalError = originalError !== null && originalError !== void 0 ? originalError : undefined; // Compute list of blame nodes. + this.nodes = undefinedIfEmpty(Array.isArray(nodes) ? nodes : nodes ? [nodes] : undefined); - const nodeLocations = undefinedIfEmpty((_this$nodes = this.nodes) === null || _this$nodes === void 0 ? void 0 : _this$nodes.map(node => node.loc).filter(loc => loc != null)); - // Compute locations in the source for the given nodes/positions. + const nodeLocations = undefinedIfEmpty((_this$nodes = this.nodes) === null || _this$nodes === void 0 ? void 0 : _this$nodes.map(node => node.loc).filter(loc => loc != null)); // Compute locations in the source for the given nodes/positions. + this.source = source !== null && source !== void 0 ? source : nodeLocations === null || nodeLocations === void 0 ? void 0 : (_nodeLocations$ = nodeLocations[0]) === null || _nodeLocations$ === void 0 ? void 0 : _nodeLocations$.source; this.positions = positions !== null && positions !== void 0 ? positions : nodeLocations === null || nodeLocations === void 0 ? void 0 : nodeLocations.map(loc => loc.start); this.locations = positions && source ? positions.map(pos => (0, _location.getLocation)(source, pos)) : nodeLocations === null || nodeLocations === void 0 ? void 0 : nodeLocations.map(loc => (0, _location.getLocation)(loc.source, loc.start)); const originalExtensions = (0, _isObjectLike.isObjectLike)(originalError === null || originalError === void 0 ? void 0 : originalError.extensions) ? originalError === null || originalError === void 0 ? void 0 : originalError.extensions : undefined; - this.extensions = (_ref = extensions !== null && extensions !== void 0 ? extensions : originalExtensions) !== null && _ref !== void 0 ? _ref : Object.create(null); - // Only properties prescribed by the spec should be enumerable. + this.extensions = (_ref = extensions !== null && extensions !== void 0 ? extensions : originalExtensions) !== null && _ref !== void 0 ? _ref : Object.create(null); // Only properties prescribed by the spec should be enumerable. // Keep the rest as non-enumerable. + Object.defineProperties(this, { message: { writable: true, @@ -23041,17 +23103,18 @@ class GraphQLError extends Error { originalError: { enumerable: false } - }); - // Include (non-enumerable) stack trace. + }); // Include (non-enumerable) stack trace. + /* c8 ignore start */ // FIXME: https://github.com/graphql/graphql-js/issues/2317 - if ((originalError === null || originalError === void 0 ? void 0 : originalError.stack) != null) { + + if (originalError !== null && originalError !== void 0 && originalError.stack) { Object.defineProperty(this, 'stack', { value: originalError.stack, writable: true, configurable: true }); - } else if (Error.captureStackTrace != null) { + } else if (Error.captureStackTrace) { Error.captureStackTrace(this, GraphQLError); } else { Object.defineProperty(this, 'stack', { @@ -23100,6 +23163,29 @@ exports.GraphQLError = GraphQLError; function undefinedIfEmpty(array) { return array === undefined || array.length === 0 ? undefined : array; } +/** + * See: https://spec.graphql.org/draft/#sec-Errors + */ + +/** + * Prints a GraphQLError to a string, representing useful location information + * about the error's position in the source. + * + * @deprecated Please use `error.toString` instead. Will be removed in v17 + */ +function printError(error) { + return error.toString(); +} +/** + * Given a GraphQLError, format it according to the rules described by the + * Response Format, Errors section of the GraphQL Specification. + * + * @deprecated Please use `error.toJSON` instead. Will be removed in v17 + */ + +function formatError(error) { + return error.toJSON(); +} /***/ }), @@ -23120,12 +23206,24 @@ Object.defineProperty(exports, "GraphQLError", ({ return _GraphQLError.GraphQLError; } })); +Object.defineProperty(exports, "formatError", ({ + enumerable: true, + get: function () { + return _GraphQLError.formatError; + } +})); Object.defineProperty(exports, "locatedError", ({ enumerable: true, get: function () { return _locatedError.locatedError; } })); +Object.defineProperty(exports, "printError", ({ + enumerable: true, + get: function () { + return _GraphQLError.printError; + } +})); Object.defineProperty(exports, "syntaxError", ({ enumerable: true, get: function () { @@ -23157,15 +23255,16 @@ var _GraphQLError = __webpack_require__(/*! ./GraphQLError.mjs */ "../../../node * GraphQL operation, produce a new GraphQLError aware of the location in the * document responsible for the original Error. */ + function locatedError(rawOriginalError, nodes, path) { - var _originalError$nodes; - const originalError = (0, _toError.toError)(rawOriginalError); - // Note: this uses a brand-check to support GraphQL errors originating from other contexts. + var _nodes; + const originalError = (0, _toError.toError)(rawOriginalError); // Note: this uses a brand-check to support GraphQL errors originating from other contexts. + if (isLocatedGraphQLError(originalError)) { return originalError; } return new _GraphQLError.GraphQLError(originalError.message, { - nodes: (_originalError$nodes = originalError.nodes) !== null && _originalError$nodes !== void 0 ? _originalError$nodes : nodes, + nodes: (_nodes = originalError.nodes) !== null && _nodes !== void 0 ? _nodes : nodes, source: originalError.source, positions: originalError.positions, path, @@ -23195,6 +23294,7 @@ var _GraphQLError = __webpack_require__(/*! ./GraphQLError.mjs */ "../../../node * Produces a GraphQLError representing a syntax error, containing useful * descriptive information about the syntax error's position in the source. */ + function syntaxError(source, position, description) { return new _GraphQLError.GraphQLError(`Syntax Error: ${description}`, { source, @@ -23204,613 +23304,6 @@ function syntaxError(source, position, description) { /***/ }), -/***/ "../../../node_modules/graphql/execution/IncrementalGraph.mjs": -/*!********************************************************************!*\ - !*** ../../../node_modules/graphql/execution/IncrementalGraph.mjs ***! - \********************************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.IncrementalGraph = void 0; -var _BoxedPromiseOrValue = __webpack_require__(/*! ../jsutils/BoxedPromiseOrValue.mjs */ "../../../node_modules/graphql/jsutils/BoxedPromiseOrValue.mjs"); -var _invariant = __webpack_require__(/*! ../jsutils/invariant.mjs */ "../../../node_modules/graphql/jsutils/invariant.mjs"); -var _isPromise = __webpack_require__(/*! ../jsutils/isPromise.mjs */ "../../../node_modules/graphql/jsutils/isPromise.mjs"); -var _promiseWithResolvers = __webpack_require__(/*! ../jsutils/promiseWithResolvers.mjs */ "../../../node_modules/graphql/jsutils/promiseWithResolvers.mjs"); -var _types = __webpack_require__(/*! ./types.mjs */ "../../../node_modules/graphql/execution/types.mjs"); -/** - * @internal - */ -class IncrementalGraph { - constructor() { - this._rootNodes = new Set(); - this._completedQueue = []; - this._nextQueue = []; - } - getNewRootNodes(incrementalDataRecords) { - const initialResultChildren = new Set(); - this._addIncrementalDataRecords(incrementalDataRecords, undefined, initialResultChildren); - return this._promoteNonEmptyToRoot(initialResultChildren); - } - addCompletedSuccessfulExecutionGroup(successfulExecutionGroup) { - for (const deferredFragmentRecord of successfulExecutionGroup.pendingExecutionGroup.deferredFragmentRecords) { - deferredFragmentRecord.pendingExecutionGroups.delete(successfulExecutionGroup.pendingExecutionGroup); - deferredFragmentRecord.successfulExecutionGroups.add(successfulExecutionGroup); - } - const incrementalDataRecords = successfulExecutionGroup.incrementalDataRecords; - if (incrementalDataRecords !== undefined) { - this._addIncrementalDataRecords(incrementalDataRecords, successfulExecutionGroup.pendingExecutionGroup.deferredFragmentRecords); - } - } - *currentCompletedBatch() { - let completed; - while ((completed = this._completedQueue.shift()) !== undefined) { - yield completed; - } - if (this._rootNodes.size === 0) { - for (const resolve of this._nextQueue) { - resolve(undefined); - } - } - } - nextCompletedBatch() { - const { - promise, - resolve - } = (0, _promiseWithResolvers.promiseWithResolvers)(); - this._nextQueue.push(resolve); - return promise; - } - abort() { - for (const resolve of this._nextQueue) { - resolve(undefined); - } - } - hasNext() { - return this._rootNodes.size > 0; - } - completeDeferredFragment(deferredFragmentRecord) { - if (!this._rootNodes.has(deferredFragmentRecord) || deferredFragmentRecord.pendingExecutionGroups.size > 0) { - return; - } - const successfulExecutionGroups = Array.from(deferredFragmentRecord.successfulExecutionGroups); - this._removeRootNode(deferredFragmentRecord); - for (const successfulExecutionGroup of successfulExecutionGroups) { - for (const otherDeferredFragmentRecord of successfulExecutionGroup.pendingExecutionGroup.deferredFragmentRecords) { - otherDeferredFragmentRecord.successfulExecutionGroups.delete(successfulExecutionGroup); - } - } - const newRootNodes = this._promoteNonEmptyToRoot(deferredFragmentRecord.children); - return { - newRootNodes, - successfulExecutionGroups - }; - } - removeDeferredFragment(deferredFragmentRecord) { - if (!this._rootNodes.has(deferredFragmentRecord)) { - return false; - } - this._removeRootNode(deferredFragmentRecord); - return true; - } - removeStream(streamRecord) { - this._removeRootNode(streamRecord); - } - _removeRootNode(deliveryGroup) { - this._rootNodes.delete(deliveryGroup); - } - _addIncrementalDataRecords(incrementalDataRecords, parents, initialResultChildren) { - for (const incrementalDataRecord of incrementalDataRecords) { - if ((0, _types.isPendingExecutionGroup)(incrementalDataRecord)) { - for (const deferredFragmentRecord of incrementalDataRecord.deferredFragmentRecords) { - this._addDeferredFragment(deferredFragmentRecord, initialResultChildren); - deferredFragmentRecord.pendingExecutionGroups.add(incrementalDataRecord); - } - if (this._completesRootNode(incrementalDataRecord)) { - this._onExecutionGroup(incrementalDataRecord); - } - } else if (parents === undefined) { - initialResultChildren !== undefined || (0, _invariant.invariant)(false); - initialResultChildren.add(incrementalDataRecord); - } else { - for (const parent of parents) { - this._addDeferredFragment(parent, initialResultChildren); - parent.children.add(incrementalDataRecord); - } - } - } - } - _promoteNonEmptyToRoot(maybeEmptyNewRootNodes) { - const newRootNodes = []; - for (const node of maybeEmptyNewRootNodes) { - if ((0, _types.isDeferredFragmentRecord)(node)) { - if (node.pendingExecutionGroups.size > 0) { - for (const pendingExecutionGroup of node.pendingExecutionGroups) { - if (!this._completesRootNode(pendingExecutionGroup)) { - this._onExecutionGroup(pendingExecutionGroup); - } - } - this._rootNodes.add(node); - newRootNodes.push(node); - continue; - } - for (const child of node.children) { - maybeEmptyNewRootNodes.add(child); - } - } else { - this._rootNodes.add(node); - newRootNodes.push(node); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this._onStreamItems(node); - } - } - return newRootNodes; - } - _completesRootNode(pendingExecutionGroup) { - return pendingExecutionGroup.deferredFragmentRecords.some(deferredFragmentRecord => this._rootNodes.has(deferredFragmentRecord)); - } - _addDeferredFragment(deferredFragmentRecord, initialResultChildren) { - if (this._rootNodes.has(deferredFragmentRecord)) { - return; - } - const parent = deferredFragmentRecord.parent; - if (parent === undefined) { - initialResultChildren !== undefined || (0, _invariant.invariant)(false); - initialResultChildren.add(deferredFragmentRecord); - return; - } - parent.children.add(deferredFragmentRecord); - this._addDeferredFragment(parent, initialResultChildren); - } - _onExecutionGroup(pendingExecutionGroup) { - let completedExecutionGroup = pendingExecutionGroup.result; - if (!(completedExecutionGroup instanceof _BoxedPromiseOrValue.BoxedPromiseOrValue)) { - completedExecutionGroup = completedExecutionGroup(); - } - const value = completedExecutionGroup.value; - if ((0, _isPromise.isPromise)(value)) { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - value.then(resolved => this._enqueue(resolved)); - } else { - this._enqueue(value); - } - } - async _onStreamItems(streamRecord) { - let items = []; - let errors = []; - let incrementalDataRecords = []; - const streamItemQueue = streamRecord.streamItemQueue; - let streamItemRecord; - while ((streamItemRecord = streamItemQueue.shift()) !== undefined) { - let result = streamItemRecord instanceof _BoxedPromiseOrValue.BoxedPromiseOrValue ? streamItemRecord.value : streamItemRecord().value; - if ((0, _isPromise.isPromise)(result)) { - if (items.length > 0) { - this._enqueue({ - streamRecord, - result: - // TODO add additional test case or rework for coverage - errors.length > 0 /* c8 ignore start */ ? { - items, - errors - } /* c8 ignore stop */ : { - items - }, - incrementalDataRecords - }); - items = []; - errors = []; - incrementalDataRecords = []; - } - // eslint-disable-next-line no-await-in-loop - result = await result; - // wait an additional tick to coalesce resolving additional promises - // within the queue - // eslint-disable-next-line no-await-in-loop - await Promise.resolve(); - } - if (result.item === undefined) { - if (items.length > 0) { - this._enqueue({ - streamRecord, - result: errors.length > 0 ? { - items, - errors - } : { - items - }, - incrementalDataRecords - }); - } - this._enqueue(result.errors === undefined ? { - streamRecord - } : { - streamRecord, - errors: result.errors - }); - return; - } - items.push(result.item); - if (result.errors !== undefined) { - errors.push(...result.errors); - } - if (result.incrementalDataRecords !== undefined) { - incrementalDataRecords.push(...result.incrementalDataRecords); - } - } - } - *_yieldCurrentCompletedIncrementalData(first) { - yield first; - yield* this.currentCompletedBatch(); - } - _enqueue(completed) { - const next = this._nextQueue.shift(); - if (next !== undefined) { - next(this._yieldCurrentCompletedIncrementalData(completed)); - return; - } - this._completedQueue.push(completed); - } -} -exports.IncrementalGraph = IncrementalGraph; - -/***/ }), - -/***/ "../../../node_modules/graphql/execution/IncrementalPublisher.mjs": -/*!************************************************************************!*\ - !*** ../../../node_modules/graphql/execution/IncrementalPublisher.mjs ***! - \************************************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.buildIncrementalResponse = buildIncrementalResponse; -var _invariant = __webpack_require__(/*! ../jsutils/invariant.mjs */ "../../../node_modules/graphql/jsutils/invariant.mjs"); -var _Path = __webpack_require__(/*! ../jsutils/Path.mjs */ "../../../node_modules/graphql/jsutils/Path.mjs"); -var _IncrementalGraph = __webpack_require__(/*! ./IncrementalGraph.mjs */ "../../../node_modules/graphql/execution/IncrementalGraph.mjs"); -var _types = __webpack_require__(/*! ./types.mjs */ "../../../node_modules/graphql/execution/types.mjs"); -function buildIncrementalResponse(context, result, errors, incrementalDataRecords) { - const incrementalPublisher = new IncrementalPublisher(context); - return incrementalPublisher.buildResponse(result, errors, incrementalDataRecords); -} -/** - * This class is used to publish incremental results to the client, enabling semi-concurrent - * execution while preserving result order. - * - * @internal - */ -class IncrementalPublisher { - constructor(context) { - this._context = context; - this._nextId = 0; - this._incrementalGraph = new _IncrementalGraph.IncrementalGraph(); - } - buildResponse(data, errors, incrementalDataRecords) { - const newRootNodes = this._incrementalGraph.getNewRootNodes(incrementalDataRecords); - const pending = this._toPendingResults(newRootNodes); - const initialResult = errors === undefined ? { - data, - pending, - hasNext: true - } : { - errors, - data, - pending, - hasNext: true - }; - return { - initialResult, - subsequentResults: this._subscribe() - }; - } - _toPendingResults(newRootNodes) { - const pendingResults = []; - for (const node of newRootNodes) { - const id = String(this._getNextId()); - node.id = id; - const pendingResult = { - id, - path: (0, _Path.pathToArray)(node.path) - }; - if (node.label !== undefined) { - pendingResult.label = node.label; - } - pendingResults.push(pendingResult); - } - return pendingResults; - } - _getNextId() { - return String(this._nextId++); - } - _subscribe() { - let isDone = false; - const _next = async () => { - if (isDone) { - await this._returnAsyncIteratorsIgnoringErrors(); - return { - value: undefined, - done: true - }; - } - const context = { - pending: [], - incremental: [], - completed: [] - }; - let batch = this._incrementalGraph.currentCompletedBatch(); - do { - for (const completedResult of batch) { - this._handleCompletedIncrementalData(completedResult, context); - } - const { - incremental, - completed - } = context; - if (incremental.length > 0 || completed.length > 0) { - const hasNext = this._incrementalGraph.hasNext(); - if (!hasNext) { - isDone = true; - } - const subsequentIncrementalExecutionResult = { - hasNext - }; - const pending = context.pending; - if (pending.length > 0) { - subsequentIncrementalExecutionResult.pending = pending; - } - if (incremental.length > 0) { - subsequentIncrementalExecutionResult.incremental = incremental; - } - if (completed.length > 0) { - subsequentIncrementalExecutionResult.completed = completed; - } - return { - value: subsequentIncrementalExecutionResult, - done: false - }; - } - // eslint-disable-next-line no-await-in-loop - batch = await this._incrementalGraph.nextCompletedBatch(); - } while (batch !== undefined); - await this._returnAsyncIteratorsIgnoringErrors(); - return { - value: undefined, - done: true - }; - }; - const _return = async () => { - isDone = true; - this._incrementalGraph.abort(); - await this._returnAsyncIterators(); - return { - value: undefined, - done: true - }; - }; - const _throw = async error => { - isDone = true; - this._incrementalGraph.abort(); - await this._returnAsyncIterators(); - return Promise.reject(error); - }; - return { - [Symbol.asyncIterator]() { - return this; - }, - next: _next, - return: _return, - throw: _throw - }; - } - _handleCompletedIncrementalData(completedIncrementalData, context) { - if ((0, _types.isCompletedExecutionGroup)(completedIncrementalData)) { - this._handleCompletedExecutionGroup(completedIncrementalData, context); - } else { - this._handleCompletedStreamItems(completedIncrementalData, context); - } - } - _handleCompletedExecutionGroup(completedExecutionGroup, context) { - if ((0, _types.isFailedExecutionGroup)(completedExecutionGroup)) { - for (const deferredFragmentRecord of completedExecutionGroup.pendingExecutionGroup.deferredFragmentRecords) { - const id = deferredFragmentRecord.id; - if (!this._incrementalGraph.removeDeferredFragment(deferredFragmentRecord)) { - // This can occur if multiple deferred grouped field sets error for a fragment. - continue; - } - id !== undefined || (0, _invariant.invariant)(false); - context.completed.push({ - id, - errors: completedExecutionGroup.errors - }); - } - return; - } - this._incrementalGraph.addCompletedSuccessfulExecutionGroup(completedExecutionGroup); - for (const deferredFragmentRecord of completedExecutionGroup.pendingExecutionGroup.deferredFragmentRecords) { - const completion = this._incrementalGraph.completeDeferredFragment(deferredFragmentRecord); - if (completion === undefined) { - continue; - } - const id = deferredFragmentRecord.id; - id !== undefined || (0, _invariant.invariant)(false); - const incremental = context.incremental; - const { - newRootNodes, - successfulExecutionGroups - } = completion; - context.pending.push(...this._toPendingResults(newRootNodes)); - for (const successfulExecutionGroup of successfulExecutionGroups) { - const { - bestId, - subPath - } = this._getBestIdAndSubPath(id, deferredFragmentRecord, successfulExecutionGroup); - const incrementalEntry = { - ...successfulExecutionGroup.result, - id: bestId - }; - if (subPath !== undefined) { - incrementalEntry.subPath = subPath; - } - incremental.push(incrementalEntry); - } - context.completed.push({ - id - }); - } - } - _handleCompletedStreamItems(streamItemsResult, context) { - const streamRecord = streamItemsResult.streamRecord; - const id = streamRecord.id; - id !== undefined || (0, _invariant.invariant)(false); - if (streamItemsResult.errors !== undefined) { - context.completed.push({ - id, - errors: streamItemsResult.errors - }); - this._incrementalGraph.removeStream(streamRecord); - if ((0, _types.isCancellableStreamRecord)(streamRecord)) { - this._context.cancellableStreams !== undefined || (0, _invariant.invariant)(false); - this._context.cancellableStreams.delete(streamRecord); - streamRecord.earlyReturn().catch(() => { - /* c8 ignore next 1 */ - // ignore error - }); - } - } else if (streamItemsResult.result === undefined) { - context.completed.push({ - id - }); - this._incrementalGraph.removeStream(streamRecord); - if ((0, _types.isCancellableStreamRecord)(streamRecord)) { - this._context.cancellableStreams !== undefined || (0, _invariant.invariant)(false); - this._context.cancellableStreams.delete(streamRecord); - } - } else { - const incrementalEntry = { - id, - ...streamItemsResult.result - }; - context.incremental.push(incrementalEntry); - const incrementalDataRecords = streamItemsResult.incrementalDataRecords; - if (incrementalDataRecords !== undefined) { - const newRootNodes = this._incrementalGraph.getNewRootNodes(incrementalDataRecords); - context.pending.push(...this._toPendingResults(newRootNodes)); - } - } - } - _getBestIdAndSubPath(initialId, initialDeferredFragmentRecord, completedExecutionGroup) { - let maxLength = (0, _Path.pathToArray)(initialDeferredFragmentRecord.path).length; - let bestId = initialId; - for (const deferredFragmentRecord of completedExecutionGroup.pendingExecutionGroup.deferredFragmentRecords) { - if (deferredFragmentRecord === initialDeferredFragmentRecord) { - continue; - } - const id = deferredFragmentRecord.id; - // TODO: add test case for when an fragment has not been released, but might be processed for the shortest path. - /* c8 ignore next 3 */ - if (id === undefined) { - continue; - } - const fragmentPath = (0, _Path.pathToArray)(deferredFragmentRecord.path); - const length = fragmentPath.length; - if (length > maxLength) { - maxLength = length; - bestId = id; - } - } - const subPath = completedExecutionGroup.path.slice(maxLength); - return { - bestId, - subPath: subPath.length > 0 ? subPath : undefined - }; - } - async _returnAsyncIterators() { - const cancellableStreams = this._context.cancellableStreams; - if (cancellableStreams === undefined) { - return; - } - const promises = []; - for (const streamRecord of cancellableStreams) { - if (streamRecord.earlyReturn !== undefined) { - promises.push(streamRecord.earlyReturn()); - } - } - await Promise.all(promises); - } - async _returnAsyncIteratorsIgnoringErrors() { - await this._returnAsyncIterators().catch(() => { - // Ignore errors - }); - } -} - -/***/ }), - -/***/ "../../../node_modules/graphql/execution/buildExecutionPlan.mjs": -/*!**********************************************************************!*\ - !*** ../../../node_modules/graphql/execution/buildExecutionPlan.mjs ***! - \**********************************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.buildExecutionPlan = buildExecutionPlan; -var _getBySet = __webpack_require__(/*! ../jsutils/getBySet.mjs */ "../../../node_modules/graphql/jsutils/getBySet.mjs"); -var _isSameSet = __webpack_require__(/*! ../jsutils/isSameSet.mjs */ "../../../node_modules/graphql/jsutils/isSameSet.mjs"); -function buildExecutionPlan(originalGroupedFieldSet, parentDeferUsages = new Set()) { - const groupedFieldSet = new Map(); - const newGroupedFieldSets = new Map(); - for (const [responseKey, fieldGroup] of originalGroupedFieldSet) { - const filteredDeferUsageSet = getFilteredDeferUsageSet(fieldGroup); - if ((0, _isSameSet.isSameSet)(filteredDeferUsageSet, parentDeferUsages)) { - groupedFieldSet.set(responseKey, fieldGroup); - continue; - } - let newGroupedFieldSet = (0, _getBySet.getBySet)(newGroupedFieldSets, filteredDeferUsageSet); - if (newGroupedFieldSet === undefined) { - newGroupedFieldSet = new Map(); - newGroupedFieldSets.set(filteredDeferUsageSet, newGroupedFieldSet); - } - newGroupedFieldSet.set(responseKey, fieldGroup); - } - return { - groupedFieldSet, - newGroupedFieldSets - }; -} -function getFilteredDeferUsageSet(fieldGroup) { - const filteredDeferUsageSet = new Set(); - for (const fieldDetails of fieldGroup) { - const deferUsage = fieldDetails.deferUsage; - if (deferUsage === undefined) { - filteredDeferUsageSet.clear(); - return filteredDeferUsageSet; - } - filteredDeferUsageSet.add(deferUsage); - } - for (const deferUsage of filteredDeferUsageSet) { - let parentDeferUsage = deferUsage.parentDeferUsage; - while (parentDeferUsage !== undefined) { - if (filteredDeferUsageSet.has(parentDeferUsage)) { - filteredDeferUsageSet.delete(deferUsage); - break; - } - parentDeferUsage = parentDeferUsage.parentDeferUsage; - } - } - return filteredDeferUsageSet; -} - -/***/ }), - /***/ "../../../node_modules/graphql/execution/collectFields.mjs": /*!*****************************************************************!*\ !*** ../../../node_modules/graphql/execution/collectFields.mjs ***! @@ -23824,9 +23317,6 @@ Object.defineProperty(exports, "__esModule", ({ })); exports.collectFields = collectFields; exports.collectSubfields = collectSubfields; -var _AccumulatorMap = __webpack_require__(/*! ../jsutils/AccumulatorMap.mjs */ "../../../node_modules/graphql/jsutils/AccumulatorMap.mjs"); -var _invariant = __webpack_require__(/*! ../jsutils/invariant.mjs */ "../../../node_modules/graphql/jsutils/invariant.mjs"); -var _ast = __webpack_require__(/*! ../language/ast.mjs */ "../../../node_modules/graphql/language/ast.mjs"); var _kinds = __webpack_require__(/*! ../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); var _definition = __webpack_require__(/*! ../type/definition.mjs */ "../../../node_modules/graphql/type/definition.mjs"); var _directives = __webpack_require__(/*! ../type/directives.mjs */ "../../../node_modules/graphql/type/directives.mjs"); @@ -23841,22 +23331,11 @@ var _values = __webpack_require__(/*! ./values.mjs */ "../../../node_modules/gra * * @internal */ -function collectFields(schema, fragments, variableValues, runtimeType, operation) { - const groupedFieldSet = new _AccumulatorMap.AccumulatorMap(); - const newDeferUsages = []; - const context = { - schema, - fragments, - variableValues, - runtimeType, - operation, - visitedFragmentNames: new Set() - }; - collectFieldsImpl(context, operation.selectionSet, groupedFieldSet, newDeferUsages); - return { - groupedFieldSet, - newDeferUsages - }; + +function collectFields(schema, fragments, variableValues, runtimeType, selectionSet) { + const fields = new Map(); + collectFieldsImpl(schema, fragments, variableValues, runtimeType, selectionSet, fields, new Set()); + return fields; } /** * Given an array of field nodes, collects all of the subfields of the passed @@ -23868,38 +23347,18 @@ function collectFields(schema, fragments, variableValues, runtimeType, operation * * @internal */ -// eslint-disable-next-line max-params -function collectSubfields(schema, fragments, variableValues, operation, returnType, fieldGroup) { - const context = { - schema, - fragments, - variableValues, - runtimeType: returnType, - operation, - visitedFragmentNames: new Set() - }; - const subGroupedFieldSet = new _AccumulatorMap.AccumulatorMap(); - const newDeferUsages = []; - for (const fieldDetail of fieldGroup) { - const node = fieldDetail.node; + +function collectSubfields(schema, fragments, variableValues, returnType, fieldNodes) { + const subFieldNodes = new Map(); + const visitedFragmentNames = new Set(); + for (const node of fieldNodes) { if (node.selectionSet) { - collectFieldsImpl(context, node.selectionSet, subGroupedFieldSet, newDeferUsages, fieldDetail.deferUsage); + collectFieldsImpl(schema, fragments, variableValues, returnType, node.selectionSet, subFieldNodes, visitedFragmentNames); } } - return { - groupedFieldSet: subGroupedFieldSet, - newDeferUsages - }; + return subFieldNodes; } -function collectFieldsImpl(context, selectionSet, groupedFieldSet, newDeferUsages, deferUsage) { - const { - schema, - fragments, - variableValues, - runtimeType, - operation, - visitedFragmentNames - } = context; +function collectFieldsImpl(schema, fragments, variableValues, runtimeType, selectionSet, fields, visitedFragmentNames) { for (const selection of selectionSet.selections) { switch (selection.kind) { case _kinds.Kind.FIELD: @@ -23907,10 +23366,13 @@ function collectFieldsImpl(context, selectionSet, groupedFieldSet, newDeferUsage if (!shouldIncludeNode(variableValues, selection)) { continue; } - groupedFieldSet.add(getFieldEntryKey(selection), { - node: selection, - deferUsage - }); + const name = getFieldEntryKey(selection); + const fieldList = fields.get(name); + if (fieldList !== undefined) { + fieldList.push(selection); + } else { + fields.set(name, [selection]); + } break; } case _kinds.Kind.INLINE_FRAGMENT: @@ -23918,61 +23380,31 @@ function collectFieldsImpl(context, selectionSet, groupedFieldSet, newDeferUsage if (!shouldIncludeNode(variableValues, selection) || !doesFragmentConditionMatch(schema, selection, runtimeType)) { continue; } - const newDeferUsage = getDeferUsage(operation, variableValues, selection, deferUsage); - if (!newDeferUsage) { - collectFieldsImpl(context, selection.selectionSet, groupedFieldSet, newDeferUsages, deferUsage); - } else { - newDeferUsages.push(newDeferUsage); - collectFieldsImpl(context, selection.selectionSet, groupedFieldSet, newDeferUsages, newDeferUsage); - } + collectFieldsImpl(schema, fragments, variableValues, runtimeType, selection.selectionSet, fields, visitedFragmentNames); break; } case _kinds.Kind.FRAGMENT_SPREAD: { const fragName = selection.name.value; - const newDeferUsage = getDeferUsage(operation, variableValues, selection, deferUsage); - if (!newDeferUsage && (visitedFragmentNames.has(fragName) || !shouldIncludeNode(variableValues, selection))) { + if (visitedFragmentNames.has(fragName) || !shouldIncludeNode(variableValues, selection)) { continue; } + visitedFragmentNames.add(fragName); const fragment = fragments[fragName]; - if (fragment == null || !doesFragmentConditionMatch(schema, fragment, runtimeType)) { + if (!fragment || !doesFragmentConditionMatch(schema, fragment, runtimeType)) { continue; } - if (!newDeferUsage) { - visitedFragmentNames.add(fragName); - collectFieldsImpl(context, fragment.selectionSet, groupedFieldSet, newDeferUsages, deferUsage); - } else { - newDeferUsages.push(newDeferUsage); - collectFieldsImpl(context, fragment.selectionSet, groupedFieldSet, newDeferUsages, newDeferUsage); - } + collectFieldsImpl(schema, fragments, variableValues, runtimeType, fragment.selectionSet, fields, visitedFragmentNames); break; } } } } -/** - * Returns an object containing the `@defer` arguments if a field should be - * deferred based on the experimental flag, defer directive present and - * not disabled by the "if" argument. - */ -function getDeferUsage(operation, variableValues, node, parentDeferUsage) { - const defer = (0, _values.getDirectiveValues)(_directives.GraphQLDeferDirective, node, variableValues); - if (!defer) { - return; - } - if (defer.if === false) { - return; - } - operation.operation !== _ast.OperationTypeNode.SUBSCRIPTION || (0, _invariant.invariant)(false, '`@defer` directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.'); - return { - label: typeof defer.label === 'string' ? defer.label : undefined, - parentDeferUsage - }; -} /** * Determines if a field should be included based on the `@include` and `@skip` * directives, where `@skip` has higher precedence than `@include`. */ + function shouldIncludeNode(variableValues, node) { const skip = (0, _values.getDirectiveValues)(_directives.GraphQLSkipDirective, node, variableValues); if ((skip === null || skip === void 0 ? void 0 : skip.if) === true) { @@ -23987,6 +23419,7 @@ function shouldIncludeNode(variableValues, node) { /** * Determines if a fragment is applicable to the given type. */ + function doesFragmentConditionMatch(schema, fragment, type) { const typeConditionNode = fragment.typeCondition; if (!typeConditionNode) { @@ -24004,6 +23437,7 @@ function doesFragmentConditionMatch(schema, fragment, type) { /** * Implements the logic to compute the key of a given field's entry */ + function getFieldEntryKey(node) { return node.alias ? node.alias.value : node.name.value; } @@ -24021,18 +23455,16 @@ function getFieldEntryKey(node) { Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.assertValidExecutionArguments = assertValidExecutionArguments; exports.buildExecutionContext = buildExecutionContext; exports.buildResolveInfo = buildResolveInfo; -exports.createSourceEventStream = createSourceEventStream; exports.defaultTypeResolver = exports.defaultFieldResolver = void 0; exports.execute = execute; exports.executeSync = executeSync; -exports.experimentalExecuteIncrementally = experimentalExecuteIncrementally; -exports.subscribe = subscribe; -var _BoxedPromiseOrValue = __webpack_require__(/*! ../jsutils/BoxedPromiseOrValue.mjs */ "../../../node_modules/graphql/jsutils/BoxedPromiseOrValue.mjs"); +exports.getFieldDef = getFieldDef; +var _devAssert = __webpack_require__(/*! ../jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); var _invariant = __webpack_require__(/*! ../jsutils/invariant.mjs */ "../../../node_modules/graphql/jsutils/invariant.mjs"); -var _isAsyncIterable = __webpack_require__(/*! ../jsutils/isAsyncIterable.mjs */ "../../../node_modules/graphql/jsutils/isAsyncIterable.mjs"); var _isIterableObject = __webpack_require__(/*! ../jsutils/isIterableObject.mjs */ "../../../node_modules/graphql/jsutils/isIterableObject.mjs"); var _isObjectLike = __webpack_require__(/*! ../jsutils/isObjectLike.mjs */ "../../../node_modules/graphql/jsutils/isObjectLike.mjs"); var _isPromise = __webpack_require__(/*! ../jsutils/isPromise.mjs */ "../../../node_modules/graphql/jsutils/isPromise.mjs"); @@ -24045,25 +23477,44 @@ var _locatedError = __webpack_require__(/*! ../error/locatedError.mjs */ "../../ var _ast = __webpack_require__(/*! ../language/ast.mjs */ "../../../node_modules/graphql/language/ast.mjs"); var _kinds = __webpack_require__(/*! ../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); var _definition = __webpack_require__(/*! ../type/definition.mjs */ "../../../node_modules/graphql/type/definition.mjs"); -var _directives = __webpack_require__(/*! ../type/directives.mjs */ "../../../node_modules/graphql/type/directives.mjs"); +var _introspection = __webpack_require__(/*! ../type/introspection.mjs */ "../../../node_modules/graphql/type/introspection.mjs"); var _validate = __webpack_require__(/*! ../type/validate.mjs */ "../../../node_modules/graphql/type/validate.mjs"); -var _buildExecutionPlan = __webpack_require__(/*! ./buildExecutionPlan.mjs */ "../../../node_modules/graphql/execution/buildExecutionPlan.mjs"); var _collectFields = __webpack_require__(/*! ./collectFields.mjs */ "../../../node_modules/graphql/execution/collectFields.mjs"); -var _IncrementalPublisher = __webpack_require__(/*! ./IncrementalPublisher.mjs */ "../../../node_modules/graphql/execution/IncrementalPublisher.mjs"); -var _mapAsyncIterable = __webpack_require__(/*! ./mapAsyncIterable.mjs */ "../../../node_modules/graphql/execution/mapAsyncIterable.mjs"); -var _types = __webpack_require__(/*! ./types.mjs */ "../../../node_modules/graphql/execution/types.mjs"); var _values = __webpack_require__(/*! ./values.mjs */ "../../../node_modules/graphql/execution/values.mjs"); -/* eslint-disable max-params */ -// This file contains a lot of such errors but we plan to refactor it anyway -// so just disable it for entire file. /** * A memoized collection of relevant subfields with regard to the return * type. Memoizing ensures the subfields are not repeatedly calculated, which * saves overhead when resolving lists of values. */ -const collectSubfields = (0, _memoize.memoize3)((exeContext, returnType, fieldGroup) => (0, _collectFields.collectSubfields)(exeContext.schema, exeContext.fragments, exeContext.variableValues, exeContext.operation, returnType, fieldGroup)); -const UNEXPECTED_EXPERIMENTAL_DIRECTIVES = 'The provided schema unexpectedly contains experimental directives (@defer or @stream). These directives may only be utilized if experimental execution features are explicitly enabled.'; -const UNEXPECTED_MULTIPLE_PAYLOADS = 'Executing this GraphQL operation would unexpectedly produce multiple payloads (due to @defer or @stream directive)'; + +const collectSubfields = (0, _memoize.memoize3)((exeContext, returnType, fieldNodes) => (0, _collectFields.collectSubfields)(exeContext.schema, exeContext.fragments, exeContext.variableValues, returnType, fieldNodes)); +/** + * Terminology + * + * "Definitions" are the generic name for top-level statements in the document. + * Examples of this include: + * 1) Operations (such as a query) + * 2) Fragments + * + * "Operations" are a generic name for requests in the document. + * Examples of this include: + * 1) query, + * 2) mutation + * + * "Selections" are the definitions that can appear legally and at + * single level of the query. These include: + * 1) field references e.g `a` + * 2) fragment "spreads" e.g. `...c` + * 3) inline fragment "spreads" e.g. `...on Type { a }` + */ + +/** + * Data that must be available at all points during query execution. + * + * Namely, schema of the type system that is currently executing, + * and the fragments defined in the query document + */ + /** * Implements the "Executing requests" section of the GraphQL specification. * @@ -24073,177 +23524,105 @@ const UNEXPECTED_MULTIPLE_PAYLOADS = 'Executing this GraphQL operation would une * * If the arguments to this function do not result in a legal execution context, * a GraphQLError will be thrown immediately explaining the invalid input. - * - * This function does not support incremental delivery (`@defer` and `@stream`). - * If an operation which would defer or stream data is executed with this - * function, it will throw or return a rejected promise. - * Use `experimentalExecuteIncrementally` if you want to support incremental - * delivery. */ function execute(args) { - if (args.schema.getDirective('defer') || args.schema.getDirective('stream')) { - throw new Error(UNEXPECTED_EXPERIMENTAL_DIRECTIVES); - } - const result = experimentalExecuteIncrementally(args); - if (!(0, _isPromise.isPromise)(result)) { - if ('initialResult' in result) { - // This can happen if the operation contains @defer or @stream directives - // and is not validated prior to execution - throw new Error(UNEXPECTED_MULTIPLE_PAYLOADS); - } - return result; - } - return result.then(incrementalResult => { - if ('initialResult' in incrementalResult) { - // This can happen if the operation contains @defer or @stream directives - // and is not validated prior to execution - throw new Error(UNEXPECTED_MULTIPLE_PAYLOADS); - } - return incrementalResult; - }); -} -/** - * Implements the "Executing requests" section of the GraphQL specification, - * including `@defer` and `@stream` as proposed in - * https://github.com/graphql/graphql-spec/pull/742 - * - * This function returns a Promise of an ExperimentalIncrementalExecutionResults - * object. This object either consists of a single ExecutionResult, or an - * object containing an `initialResult` and a stream of `subsequentResults`. - * - * If the arguments to this function do not result in a legal execution context, - * a GraphQLError will be thrown immediately explaining the invalid input. - */ -function experimentalExecuteIncrementally(args) { - // If a valid execution context cannot be created due to incorrect arguments, + // Temporary for v15 to v16 migration. Remove in v17 + arguments.length < 2 || (0, _devAssert.devAssert)(false, 'graphql@16 dropped long-deprecated support for positional arguments, please pass an object instead.'); + const { + schema, + document, + variableValues, + rootValue + } = args; // If arguments are missing or incorrect, throw an error. + + assertValidExecutionArguments(schema, document, variableValues); // If a valid execution context cannot be created due to incorrect arguments, // a "Response" with only errors is returned. - const exeContext = buildExecutionContext(args); - // Return early errors if execution context failed. + + const exeContext = buildExecutionContext(args); // Return early errors if execution context failed. + if (!('schema' in exeContext)) { return { errors: exeContext }; - } - return executeOperation(exeContext); -} -/** - * Implements the "Executing operations" section of the spec. - * - * Returns a Promise that will eventually resolve to the data described by - * The "Response" section of the GraphQL specification. - * - * If errors are encountered while executing a GraphQL field, only that - * field and its descendants will be omitted, and sibling fields will still - * be executed. An execution which encounters errors will still result in a - * resolved Promise. - * - * Errors from sub-fields of a NonNull type may propagate to the top level, - * at which point we still log the error and null the parent field, which - * in this case is the entire response. - */ -function executeOperation(exeContext) { + } // Return a Promise that will eventually resolve to the data described by + // The "Response" section of the GraphQL specification. + // + // If errors are encountered while executing a GraphQL field, only that + // field and its descendants will be omitted, and sibling fields will still + // be executed. An execution which encounters errors will still result in a + // resolved Promise. + // + // Errors from sub-fields of a NonNull type may propagate to the top level, + // at which point we still log the error and null the parent field, which + // in this case is the entire response. + try { const { - operation, - schema, - fragments, - variableValues, - rootValue + operation } = exeContext; - const rootType = schema.getRootType(operation.operation); - if (rootType == null) { - throw new _GraphQLError.GraphQLError(`Schema is not configured to execute ${operation.operation} operation.`, { - nodes: operation + const result = executeOperation(exeContext, operation, rootValue); + if ((0, _isPromise.isPromise)(result)) { + return result.then(data => buildResponse(data, exeContext.errors), error => { + exeContext.errors.push(error); + return buildResponse(null, exeContext.errors); }); } - const collectedFields = (0, _collectFields.collectFields)(schema, fragments, variableValues, rootType, operation); - let groupedFieldSet = collectedFields.groupedFieldSet; - const newDeferUsages = collectedFields.newDeferUsages; - let graphqlWrappedResult; - if (newDeferUsages.length === 0) { - graphqlWrappedResult = executeRootGroupedFieldSet(exeContext, operation.operation, rootType, rootValue, groupedFieldSet, undefined); - } else { - const executionPlan = (0, _buildExecutionPlan.buildExecutionPlan)(groupedFieldSet); - groupedFieldSet = executionPlan.groupedFieldSet; - const newGroupedFieldSets = executionPlan.newGroupedFieldSets; - const newDeferMap = addNewDeferredFragments(newDeferUsages, new Map()); - graphqlWrappedResult = executeRootGroupedFieldSet(exeContext, operation.operation, rootType, rootValue, groupedFieldSet, newDeferMap); - if (newGroupedFieldSets.size > 0) { - const newPendingExecutionGroups = collectExecutionGroups(exeContext, rootType, rootValue, undefined, undefined, newGroupedFieldSets, newDeferMap); - graphqlWrappedResult = withNewExecutionGroups(graphqlWrappedResult, newPendingExecutionGroups); - } - } - if ((0, _isPromise.isPromise)(graphqlWrappedResult)) { - return graphqlWrappedResult.then(resolved => buildDataResponse(exeContext, resolved[0], resolved[1]), error => ({ - data: null, - errors: withError(exeContext.errors, error) - })); - } - return buildDataResponse(exeContext, graphqlWrappedResult[0], graphqlWrappedResult[1]); + return buildResponse(result, exeContext.errors); } catch (error) { - return { - data: null, - errors: withError(exeContext.errors, error) - }; + exeContext.errors.push(error); + return buildResponse(null, exeContext.errors); } } -function withNewExecutionGroups(result, newPendingExecutionGroups) { - if ((0, _isPromise.isPromise)(result)) { - return result.then(resolved => { - addIncrementalDataRecords(resolved, newPendingExecutionGroups); - return resolved; - }); - } - addIncrementalDataRecords(result, newPendingExecutionGroups); - return result; -} -function addIncrementalDataRecords(graphqlWrappedResult, incrementalDataRecords) { - if (incrementalDataRecords === undefined) { - return; - } - if (graphqlWrappedResult[1] === undefined) { - graphqlWrappedResult[1] = [...incrementalDataRecords]; - } else { - graphqlWrappedResult[1].push(...incrementalDataRecords); - } -} -function withError(errors, error) { - return errors === undefined ? [error] : [...errors, error]; -} -function buildDataResponse(exeContext, data, incrementalDataRecords) { - const errors = exeContext.errors; - if (incrementalDataRecords === undefined) { - return errors !== undefined ? { - errors, - data - } : { - data - }; - } - return (0, _IncrementalPublisher.buildIncrementalResponse)(exeContext, data, errors, incrementalDataRecords); -} /** * Also implements the "Executing requests" section of the GraphQL specification. * However, it guarantees to complete synchronously (or throw an error) assuming * that all field resolvers are also synchronous. */ + function executeSync(args) { - const result = experimentalExecuteIncrementally(args); - // Assert that the execution was synchronous. - if ((0, _isPromise.isPromise)(result) || 'initialResult' in result) { + const result = execute(args); // Assert that the execution was synchronous. + + if ((0, _isPromise.isPromise)(result)) { throw new Error('GraphQL execution failed to complete synchronously.'); } return result; } +/** + * Given a completed execution context and data, build the `{ errors, data }` + * response defined by the "Response" section of the GraphQL specification. + */ + +function buildResponse(data, errors) { + return errors.length === 0 ? { + data + } : { + errors, + data + }; +} +/** + * Essential assertions before executing to provide developer feedback for + * improper use of the GraphQL library. + * + * @internal + */ + +function assertValidExecutionArguments(schema, document, rawVariableValues) { + document || (0, _devAssert.devAssert)(false, 'Must provide document.'); // If the schema used for execution is invalid, throw an error. + + (0, _validate.assertValidSchema)(schema); // Variables, if provided, must be an object. + + rawVariableValues == null || (0, _isObjectLike.isObjectLike)(rawVariableValues) || (0, _devAssert.devAssert)(false, 'Variables must be provided as an Object where each property is a variable value. Perhaps look to see if an unparsed JSON string was provided.'); +} /** * Constructs a ExecutionContext object from the arguments passed to * execute, which we will pass throughout the other execution methods. * * Throws a GraphQLError if a valid execution context cannot be created. * - * TODO: consider no longer exporting this function * @internal */ + function buildExecutionContext(args) { var _definition$name, _operation$variableDe; const { @@ -24255,11 +23634,8 @@ function buildExecutionContext(args) { operationName, fieldResolver, typeResolver, - subscribeFieldResolver, - enableEarlyExecution + subscribeFieldResolver } = args; - // If the schema used for execution is invalid, throw an error. - (0, _validate.assertValidSchema)(schema); let operation; const fragments = Object.create(null); for (const definition of document.definitions) { @@ -24277,8 +23653,7 @@ function buildExecutionContext(args) { case _kinds.Kind.FRAGMENT_DEFINITION: fragments[definition.name.value] = definition; break; - default: - // ignore non-executable definitions + default: // ignore non-executable definitions } } if (!operation) { @@ -24286,9 +23661,10 @@ function buildExecutionContext(args) { return [new _GraphQLError.GraphQLError(`Unknown operation named "${operationName}".`)]; } return [new _GraphQLError.GraphQLError('Must provide an operation.')]; - } - // FIXME: https://github.com/graphql/graphql-js/issues/2203 + } // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + const variableDefinitions = (_operation$variableDe = operation.variableDefinitions) !== null && _operation$variableDe !== void 0 ? _operation$variableDe : []; const coercedVariableValues = (0, _values.getVariableValues)(schema, variableDefinitions, rawVariableValues !== null && rawVariableValues !== void 0 ? rawVariableValues : {}, { maxErrors: 50 @@ -24306,100 +23682,91 @@ function buildExecutionContext(args) { fieldResolver: fieldResolver !== null && fieldResolver !== void 0 ? fieldResolver : defaultFieldResolver, typeResolver: typeResolver !== null && typeResolver !== void 0 ? typeResolver : defaultTypeResolver, subscribeFieldResolver: subscribeFieldResolver !== null && subscribeFieldResolver !== void 0 ? subscribeFieldResolver : defaultFieldResolver, - enableEarlyExecution: enableEarlyExecution === true, - errors: undefined, - cancellableStreams: undefined + errors: [] }; } -function buildPerEventExecutionContext(exeContext, payload) { - return { - ...exeContext, - rootValue: payload, - errors: undefined - }; -} -function executeRootGroupedFieldSet(exeContext, operation, rootType, rootValue, groupedFieldSet, deferMap) { - switch (operation) { +/** + * Implements the "Executing operations" section of the spec. + */ + +function executeOperation(exeContext, operation, rootValue) { + const rootType = exeContext.schema.getRootType(operation.operation); + if (rootType == null) { + throw new _GraphQLError.GraphQLError(`Schema is not configured to execute ${operation.operation} operation.`, { + nodes: operation + }); + } + const rootFields = (0, _collectFields.collectFields)(exeContext.schema, exeContext.fragments, exeContext.variableValues, rootType, operation.selectionSet); + const path = undefined; + switch (operation.operation) { case _ast.OperationTypeNode.QUERY: - return executeFields(exeContext, rootType, rootValue, undefined, groupedFieldSet, undefined, deferMap); + return executeFields(exeContext, rootType, rootValue, path, rootFields); case _ast.OperationTypeNode.MUTATION: - return executeFieldsSerially(exeContext, rootType, rootValue, undefined, groupedFieldSet, undefined, deferMap); + return executeFieldsSerially(exeContext, rootType, rootValue, path, rootFields); case _ast.OperationTypeNode.SUBSCRIPTION: // TODO: deprecate `subscribe` and move all logic here // Temporary solution until we finish merging execute and subscribe together - return executeFields(exeContext, rootType, rootValue, undefined, groupedFieldSet, undefined, deferMap); + return executeFields(exeContext, rootType, rootValue, path, rootFields); } } /** * Implements the "Executing selection sets" section of the spec * for fields that must be executed serially. */ -function executeFieldsSerially(exeContext, parentType, sourceValue, path, groupedFieldSet, incrementalContext, deferMap) { - return (0, _promiseReduce.promiseReduce)(groupedFieldSet, (graphqlWrappedResult, [responseName, fieldGroup]) => { + +function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields) { + return (0, _promiseReduce.promiseReduce)(fields.entries(), (results, [responseName, fieldNodes]) => { const fieldPath = (0, _Path.addPath)(path, responseName, parentType.name); - const result = executeField(exeContext, parentType, sourceValue, fieldGroup, fieldPath, incrementalContext, deferMap); + const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath); if (result === undefined) { - return graphqlWrappedResult; + return results; } if ((0, _isPromise.isPromise)(result)) { - return result.then(resolved => { - graphqlWrappedResult[0][responseName] = resolved[0]; - addIncrementalDataRecords(graphqlWrappedResult, resolved[1]); - return graphqlWrappedResult; + return result.then(resolvedResult => { + results[responseName] = resolvedResult; + return results; }); } - graphqlWrappedResult[0][responseName] = result[0]; - addIncrementalDataRecords(graphqlWrappedResult, result[1]); - return graphqlWrappedResult; - }, [Object.create(null), undefined]); + results[responseName] = result; + return results; + }, Object.create(null)); } /** * Implements the "Executing selection sets" section of the spec * for fields that may be executed in parallel. */ -function executeFields(exeContext, parentType, sourceValue, path, groupedFieldSet, incrementalContext, deferMap) { + +function executeFields(exeContext, parentType, sourceValue, path, fields) { const results = Object.create(null); - const graphqlWrappedResult = [results, undefined]; let containsPromise = false; try { - for (const [responseName, fieldGroup] of groupedFieldSet) { + for (const [responseName, fieldNodes] of fields.entries()) { const fieldPath = (0, _Path.addPath)(path, responseName, parentType.name); - const result = executeField(exeContext, parentType, sourceValue, fieldGroup, fieldPath, incrementalContext, deferMap); + const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath); if (result !== undefined) { + results[responseName] = result; if ((0, _isPromise.isPromise)(result)) { - results[responseName] = result.then(resolved => { - addIncrementalDataRecords(graphqlWrappedResult, resolved[1]); - return resolved[0]; - }); containsPromise = true; - } else { - results[responseName] = result[0]; - addIncrementalDataRecords(graphqlWrappedResult, result[1]); } } } } catch (error) { if (containsPromise) { // Ensure that any promises returned by other fields are handled, as they may also reject. - return (0, _promiseForObject.promiseForObject)(results, () => { - /* noop */ - }).finally(() => { + return (0, _promiseForObject.promiseForObject)(results).finally(() => { throw error; }); } throw error; - } - // If there are no promises, we can just return the object and any incrementalDataRecords + } // If there are no promises, we can just return the object + if (!containsPromise) { - return graphqlWrappedResult; - } - // Otherwise, results is a map from field name to the result of resolving that + return results; + } // Otherwise, results is a map from field name to the result of resolving that // field, which is possibly a promise. Return a promise that will return this // same map, but with any promises replaced with the values they resolved to. - return (0, _promiseForObject.promiseForObject)(results, resolved => [resolved, graphqlWrappedResult[1]]); -} -function toNodes(fieldGroup) { - return fieldGroup.map(fieldDetails => fieldDetails.node); + + return (0, _promiseForObject.promiseForObject)(results); } /** * Implements the "Executing fields" section of the spec @@ -24407,49 +23774,51 @@ function toNodes(fieldGroup) { * calling its resolve function, then calls completeValue to complete promises, * serialize scalars, or execute the sub-selection-set for objects. */ -function executeField(exeContext, parentType, source, fieldGroup, path, incrementalContext, deferMap) { + +function executeField(exeContext, parentType, source, fieldNodes, path) { var _fieldDef$resolve; - const fieldName = fieldGroup[0].node.name.value; - const fieldDef = exeContext.schema.getField(parentType, fieldName); + const fieldDef = getFieldDef(exeContext.schema, parentType, fieldNodes[0]); if (!fieldDef) { return; } const returnType = fieldDef.type; const resolveFn = (_fieldDef$resolve = fieldDef.resolve) !== null && _fieldDef$resolve !== void 0 ? _fieldDef$resolve : exeContext.fieldResolver; - const info = buildResolveInfo(exeContext, fieldDef, toNodes(fieldGroup), parentType, path); - // Get the resolve function, regardless of if its result is normal or abrupt (error). + const info = buildResolveInfo(exeContext, fieldDef, fieldNodes, parentType, path); // Get the resolve function, regardless of if its result is normal or abrupt (error). + try { // Build a JS object of arguments from the field.arguments AST, using the // variables scope to fulfill any variable references. // TODO: find a way to memoize, in case this field is within a List type. - const args = (0, _values.getArgumentValues)(fieldDef, fieldGroup[0].node, exeContext.variableValues); - // The resolve function's optional third argument is a context value that + const args = (0, _values.getArgumentValues)(fieldDef, fieldNodes[0], exeContext.variableValues); // The resolve function's optional third argument is a context value that // is provided to every resolve function within an execution. It is commonly // used to represent an authenticated user, or request-specific caches. + const contextValue = exeContext.contextValue; const result = resolveFn(source, args, contextValue, info); + let completed; if ((0, _isPromise.isPromise)(result)) { - return completePromisedValue(exeContext, returnType, fieldGroup, info, path, result, incrementalContext, deferMap); + completed = result.then(resolved => completeValue(exeContext, returnType, fieldNodes, info, path, resolved)); + } else { + completed = completeValue(exeContext, returnType, fieldNodes, info, path, result); } - const completed = completeValue(exeContext, returnType, fieldGroup, info, path, result, incrementalContext, deferMap); if ((0, _isPromise.isPromise)(completed)) { // Note: we don't rely on a `catch` method, but we do expect "thenable" // to take a second callback for the error case. return completed.then(undefined, rawError => { - handleFieldError(rawError, exeContext, returnType, fieldGroup, path, incrementalContext); - return [null, undefined]; + const error = (0, _locatedError.locatedError)(rawError, fieldNodes, (0, _Path.pathToArray)(path)); + return handleFieldError(error, returnType, exeContext); }); } return completed; } catch (rawError) { - handleFieldError(rawError, exeContext, returnType, fieldGroup, path, incrementalContext); - return [null, undefined]; + const error = (0, _locatedError.locatedError)(rawError, fieldNodes, (0, _Path.pathToArray)(path)); + return handleFieldError(error, returnType, exeContext); } } /** - * TODO: consider no longer exporting this function * @internal */ + function buildResolveInfo(exeContext, fieldDef, fieldNodes, parentType, path) { // The resolve function's optional fourth argument is a collection of // information about the current execution state. @@ -24466,22 +23835,16 @@ function buildResolveInfo(exeContext, fieldDef, fieldNodes, parentType, path) { variableValues: exeContext.variableValues }; } -function handleFieldError(rawError, exeContext, returnType, fieldGroup, path, incrementalContext) { - const error = (0, _locatedError.locatedError)(rawError, toNodes(fieldGroup), (0, _Path.pathToArray)(path)); +function handleFieldError(error, returnType, exeContext) { // If the field type is non-nullable, then it is resolved without any // protection from errors, however it still properly locates the error. if ((0, _definition.isNonNullType)(returnType)) { throw error; - } - // Otherwise, error protection is applied, logging the error and resolving + } // Otherwise, error protection is applied, logging the error and resolving // a null value for this field if one is encountered. - const context = incrementalContext !== null && incrementalContext !== void 0 ? incrementalContext : exeContext; - let errors = context.errors; - if (errors === undefined) { - errors = []; - context.errors = errors; - } - errors.push(error); + + exeContext.errors.push(error); + return null; } /** * Implements the instructions for completeValue as defined in the @@ -24504,271 +23867,94 @@ function handleFieldError(rawError, exeContext, returnType, fieldGroup, path, in * Otherwise, the field type expects a sub-selection set, and will complete the * value by executing all sub-selections. */ -function completeValue(exeContext, returnType, fieldGroup, info, path, result, incrementalContext, deferMap) { + +function completeValue(exeContext, returnType, fieldNodes, info, path, result) { // If result is an Error, throw a located error. if (result instanceof Error) { throw result; - } - // If field type is NonNull, complete for inner type, and throw field error + } // If field type is NonNull, complete for inner type, and throw field error // if result is null. + if ((0, _definition.isNonNullType)(returnType)) { - const completed = completeValue(exeContext, returnType.ofType, fieldGroup, info, path, result, incrementalContext, deferMap); - if (completed[0] === null) { + const completed = completeValue(exeContext, returnType.ofType, fieldNodes, info, path, result); + if (completed === null) { throw new Error(`Cannot return null for non-nullable field ${info.parentType.name}.${info.fieldName}.`); } return completed; - } - // If result value is null or undefined then return null. + } // If result value is null or undefined then return null. + if (result == null) { - return [null, undefined]; - } - // If field type is List, complete each item in the list with the inner type + return null; + } // If field type is List, complete each item in the list with the inner type + if ((0, _definition.isListType)(returnType)) { - return completeListValue(exeContext, returnType, fieldGroup, info, path, result, incrementalContext, deferMap); - } - // If field type is a leaf type, Scalar or Enum, serialize to a valid value, + return completeListValue(exeContext, returnType, fieldNodes, info, path, result); + } // If field type is a leaf type, Scalar or Enum, serialize to a valid value, // returning null if serialization is not possible. + if ((0, _definition.isLeafType)(returnType)) { - return [completeLeafValue(returnType, result), undefined]; - } - // If field type is an abstract type, Interface or Union, determine the + return completeLeafValue(returnType, result); + } // If field type is an abstract type, Interface or Union, determine the // runtime Object type and complete for that type. + if ((0, _definition.isAbstractType)(returnType)) { - return completeAbstractValue(exeContext, returnType, fieldGroup, info, path, result, incrementalContext, deferMap); - } - // If field type is Object, execute and complete all sub-selections. + return completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result); + } // If field type is Object, execute and complete all sub-selections. + if ((0, _definition.isObjectType)(returnType)) { - return completeObjectValue(exeContext, returnType, fieldGroup, info, path, result, incrementalContext, deferMap); + return completeObjectValue(exeContext, returnType, fieldNodes, info, path, result); } /* c8 ignore next 6 */ // Not reachable, all possible output types have been considered. + false || (0, _invariant.invariant)(false, 'Cannot complete value of unexpected output type: ' + (0, _inspect.inspect)(returnType)); } -async function completePromisedValue(exeContext, returnType, fieldGroup, info, path, result, incrementalContext, deferMap) { - try { - const resolved = await result; - let completed = completeValue(exeContext, returnType, fieldGroup, info, path, resolved, incrementalContext, deferMap); - if ((0, _isPromise.isPromise)(completed)) { - completed = await completed; - } - return completed; - } catch (rawError) { - handleFieldError(rawError, exeContext, returnType, fieldGroup, path, incrementalContext); - return [null, undefined]; - } -} -/** - * Returns an object containing info for streaming if a field should be - * streamed based on the experimental flag, stream directive present and - * not disabled by the "if" argument. - */ -function getStreamUsage(exeContext, fieldGroup, path) { - // do not stream inner lists of multi-dimensional lists - if (typeof path.key === 'number') { - return; - } - // TODO: add test for this case (a streamed list nested under a list). - /* c8 ignore next 7 */ - if (fieldGroup._streamUsage !== undefined) { - return fieldGroup._streamUsage; - } - // validation only allows equivalent streams on multiple fields, so it is - // safe to only check the first fieldNode for the stream directive - const stream = (0, _values.getDirectiveValues)(_directives.GraphQLStreamDirective, fieldGroup[0].node, exeContext.variableValues); - if (!stream) { - return; - } - if (stream.if === false) { - return; - } - typeof stream.initialCount === 'number' || (0, _invariant.invariant)(false, 'initialCount must be a number'); - stream.initialCount >= 0 || (0, _invariant.invariant)(false, 'initialCount must be a positive integer'); - exeContext.operation.operation !== _ast.OperationTypeNode.SUBSCRIPTION || (0, _invariant.invariant)(false, '`@stream` directive not supported on subscription operations. Disable `@stream` by setting the `if` argument to `false`.'); - const streamedFieldGroup = fieldGroup.map(fieldDetails => ({ - node: fieldDetails.node, - deferUsage: undefined - })); - const streamUsage = { - initialCount: stream.initialCount, - label: typeof stream.label === 'string' ? stream.label : undefined, - fieldGroup: streamedFieldGroup - }; - fieldGroup._streamUsage = streamUsage; - return streamUsage; -} -/** - * Complete a async iterator value by completing the result and calling - * recursively until all the results are completed. - */ -async function completeAsyncIteratorValue(exeContext, itemType, fieldGroup, info, path, asyncIterator, incrementalContext, deferMap) { - let containsPromise = false; - const completedResults = []; - const graphqlWrappedResult = [completedResults, undefined]; - let index = 0; - const streamUsage = getStreamUsage(exeContext, fieldGroup, path); - const earlyReturn = asyncIterator.return === undefined ? undefined : asyncIterator.return.bind(asyncIterator); - try { - // eslint-disable-next-line no-constant-condition - while (true) { - if (streamUsage && index >= streamUsage.initialCount) { - const streamItemQueue = buildAsyncStreamItemQueue(index, path, asyncIterator, exeContext, streamUsage.fieldGroup, info, itemType); - let streamRecord; - if (earlyReturn === undefined) { - streamRecord = { - label: streamUsage.label, - path, - streamItemQueue - }; - } else { - streamRecord = { - label: streamUsage.label, - path, - earlyReturn, - streamItemQueue - }; - if (exeContext.cancellableStreams === undefined) { - exeContext.cancellableStreams = new Set(); - } - exeContext.cancellableStreams.add(streamRecord); - } - addIncrementalDataRecords(graphqlWrappedResult, [streamRecord]); - break; - } - const itemPath = (0, _Path.addPath)(path, index, undefined); - let iteration; - try { - // eslint-disable-next-line no-await-in-loop - iteration = await asyncIterator.next(); - } catch (rawError) { - throw (0, _locatedError.locatedError)(rawError, toNodes(fieldGroup), (0, _Path.pathToArray)(path)); - } - // TODO: add test case for stream returning done before initialCount - /* c8 ignore next 3 */ - if (iteration.done) { - break; - } - const item = iteration.value; - // TODO: add tests for stream backed by asyncIterator that returns a promise - /* c8 ignore start */ - if ((0, _isPromise.isPromise)(item)) { - completedResults.push(completePromisedListItemValue(item, graphqlWrappedResult, exeContext, itemType, fieldGroup, info, itemPath, incrementalContext, deferMap)); - containsPromise = true; - } else if ( /* c8 ignore stop */ - completeListItemValue(item, completedResults, graphqlWrappedResult, exeContext, itemType, fieldGroup, info, itemPath, incrementalContext, deferMap) - // TODO: add tests for stream backed by asyncIterator that completes to a promise - /* c8 ignore start */) { - containsPromise = true; - } - /* c8 ignore stop */ - index++; - } - } catch (error) { - if (earlyReturn !== undefined) { - earlyReturn().catch(() => { - /* c8 ignore next 1 */ - // ignore error - }); - } - throw error; - } - return containsPromise ? /* c8 ignore start */Promise.all(completedResults).then(resolved => [resolved, graphqlWrappedResult[1]]) : /* c8 ignore stop */graphqlWrappedResult; -} /** * Complete a list value by completing each item in the list with the * inner type */ -function completeListValue(exeContext, returnType, fieldGroup, info, path, result, incrementalContext, deferMap) { - const itemType = returnType.ofType; - if ((0, _isAsyncIterable.isAsyncIterable)(result)) { - const asyncIterator = result[Symbol.asyncIterator](); - return completeAsyncIteratorValue(exeContext, itemType, fieldGroup, info, path, asyncIterator, incrementalContext, deferMap); - } + +function completeListValue(exeContext, returnType, fieldNodes, info, path, result) { if (!(0, _isIterableObject.isIterableObject)(result)) { throw new _GraphQLError.GraphQLError(`Expected Iterable, but did not find one for field "${info.parentType.name}.${info.fieldName}".`); - } - return completeIterableValue(exeContext, itemType, fieldGroup, info, path, result, incrementalContext, deferMap); -} -function completeIterableValue(exeContext, itemType, fieldGroup, info, path, items, incrementalContext, deferMap) { - // This is specified as a simple map, however we're optimizing the path + } // This is specified as a simple map, however we're optimizing the path // where the list contains no Promises by avoiding creating another Promise. + + const itemType = returnType.ofType; let containsPromise = false; - const completedResults = []; - const graphqlWrappedResult = [completedResults, undefined]; - let index = 0; - const streamUsage = getStreamUsage(exeContext, fieldGroup, path); - const iterator = items[Symbol.iterator](); - let iteration = iterator.next(); - while (!iteration.done) { - const item = iteration.value; - if (streamUsage && index >= streamUsage.initialCount) { - const syncStreamRecord = { - label: streamUsage.label, - path, - streamItemQueue: buildSyncStreamItemQueue(item, index, path, iterator, exeContext, streamUsage.fieldGroup, info, itemType) - }; - addIncrementalDataRecords(graphqlWrappedResult, [syncStreamRecord]); - break; - } + const completedResults = Array.from(result, (item, index) => { // No need to modify the info object containing the path, // since from here on it is not ever accessed by resolver functions. const itemPath = (0, _Path.addPath)(path, index, undefined); - if ((0, _isPromise.isPromise)(item)) { - completedResults.push(completePromisedListItemValue(item, graphqlWrappedResult, exeContext, itemType, fieldGroup, info, itemPath, incrementalContext, deferMap)); - containsPromise = true; - } else if (completeListItemValue(item, completedResults, graphqlWrappedResult, exeContext, itemType, fieldGroup, info, itemPath, incrementalContext, deferMap)) { - containsPromise = true; + try { + let completedItem; + if ((0, _isPromise.isPromise)(item)) { + completedItem = item.then(resolved => completeValue(exeContext, itemType, fieldNodes, info, itemPath, resolved)); + } else { + completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item); + } + if ((0, _isPromise.isPromise)(completedItem)) { + containsPromise = true; // Note: we don't rely on a `catch` method, but we do expect "thenable" + // to take a second callback for the error case. + + return completedItem.then(undefined, rawError => { + const error = (0, _locatedError.locatedError)(rawError, fieldNodes, (0, _Path.pathToArray)(itemPath)); + return handleFieldError(error, itemType, exeContext); + }); + } + return completedItem; + } catch (rawError) { + const error = (0, _locatedError.locatedError)(rawError, fieldNodes, (0, _Path.pathToArray)(itemPath)); + return handleFieldError(error, itemType, exeContext); } - index++; - iteration = iterator.next(); - } - return containsPromise ? Promise.all(completedResults).then(resolved => [resolved, graphqlWrappedResult[1]]) : graphqlWrappedResult; -} -/** - * Complete a list item value by adding it to the completed results. - * - * Returns true if the value is a Promise. - */ -function completeListItemValue(item, completedResults, parent, exeContext, itemType, fieldGroup, info, itemPath, incrementalContext, deferMap) { - try { - const completedItem = completeValue(exeContext, itemType, fieldGroup, info, itemPath, item, incrementalContext, deferMap); - if ((0, _isPromise.isPromise)(completedItem)) { - // Note: we don't rely on a `catch` method, but we do expect "thenable" - // to take a second callback for the error case. - completedResults.push(completedItem.then(resolved => { - addIncrementalDataRecords(parent, resolved[1]); - return resolved[0]; - }, rawError => { - handleFieldError(rawError, exeContext, itemType, fieldGroup, itemPath, incrementalContext); - return null; - })); - return true; - } - completedResults.push(completedItem[0]); - addIncrementalDataRecords(parent, completedItem[1]); - } catch (rawError) { - handleFieldError(rawError, exeContext, itemType, fieldGroup, itemPath, incrementalContext); - completedResults.push(null); - } - return false; -} -async function completePromisedListItemValue(item, parent, exeContext, itemType, fieldGroup, info, itemPath, incrementalContext, deferMap) { - try { - const resolved = await item; - let completed = completeValue(exeContext, itemType, fieldGroup, info, itemPath, resolved, incrementalContext, deferMap); - if ((0, _isPromise.isPromise)(completed)) { - completed = await completed; - } - addIncrementalDataRecords(parent, completed[1]); - return completed[0]; - } catch (rawError) { - handleFieldError(rawError, exeContext, itemType, fieldGroup, itemPath, incrementalContext); - return null; - } + }); + return containsPromise ? Promise.all(completedResults) : completedResults; } /** * Complete a Scalar or Enum by serializing to a valid value, returning * null if serialization is not possible. */ + function completeLeafValue(returnType, result) { const serializedResult = returnType.serialize(result); if (serializedResult == null) { @@ -24780,24 +23966,23 @@ function completeLeafValue(returnType, result) { * Complete a value of an abstract type by determining the runtime object type * of that value, then complete the value for that type. */ -function completeAbstractValue(exeContext, returnType, fieldGroup, info, path, result, incrementalContext, deferMap) { + +function completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result) { var _returnType$resolveTy; const resolveTypeFn = (_returnType$resolveTy = returnType.resolveType) !== null && _returnType$resolveTy !== void 0 ? _returnType$resolveTy : exeContext.typeResolver; const contextValue = exeContext.contextValue; const runtimeType = resolveTypeFn(result, contextValue, info, returnType); if ((0, _isPromise.isPromise)(runtimeType)) { - return runtimeType.then(resolvedRuntimeType => completeObjectValue(exeContext, ensureValidRuntimeType(resolvedRuntimeType, exeContext, returnType, fieldGroup, info, result), fieldGroup, info, path, result, incrementalContext, deferMap)); + return runtimeType.then(resolvedRuntimeType => completeObjectValue(exeContext, ensureValidRuntimeType(resolvedRuntimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result)); } - return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldGroup, info, result), fieldGroup, info, path, result, incrementalContext, deferMap); + return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result); } -function ensureValidRuntimeType(runtimeTypeName, exeContext, returnType, fieldGroup, info, result) { +function ensureValidRuntimeType(runtimeTypeName, exeContext, returnType, fieldNodes, info, result) { if (runtimeTypeName == null) { - throw new _GraphQLError.GraphQLError(`Abstract type "${returnType.name}" must resolve to an Object type at runtime for field "${info.parentType.name}.${info.fieldName}". Either the "${returnType.name}" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.`, { - nodes: toNodes(fieldGroup) - }); - } - // releases before 16.0.0 supported returning `GraphQLObjectType` from `resolveType` + throw new _GraphQLError.GraphQLError(`Abstract type "${returnType.name}" must resolve to an Object type at runtime for field "${info.parentType.name}.${info.fieldName}". Either the "${returnType.name}" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.`, fieldNodes); + } // releases before 16.0.0 supported returning `GraphQLObjectType` from `resolveType` // TODO: remove in 17.0.0 release + if ((0, _definition.isObjectType)(runtimeTypeName)) { throw new _GraphQLError.GraphQLError('Support for returning GraphQLObjectType from resolveType was removed in graphql-js@16.0.0 please return type name instead.'); } @@ -24807,17 +23992,17 @@ function ensureValidRuntimeType(runtimeTypeName, exeContext, returnType, fieldGr const runtimeType = exeContext.schema.getType(runtimeTypeName); if (runtimeType == null) { throw new _GraphQLError.GraphQLError(`Abstract type "${returnType.name}" was resolved to a type "${runtimeTypeName}" that does not exist inside the schema.`, { - nodes: toNodes(fieldGroup) + nodes: fieldNodes }); } if (!(0, _definition.isObjectType)(runtimeType)) { throw new _GraphQLError.GraphQLError(`Abstract type "${returnType.name}" was resolved to a non-object type "${runtimeTypeName}".`, { - nodes: toNodes(fieldGroup) + nodes: fieldNodes }); } if (!exeContext.schema.isSubType(returnType, runtimeType)) { throw new _GraphQLError.GraphQLError(`Runtime Object type "${runtimeType.name}" is not a possible type for "${returnType.name}".`, { - nodes: toNodes(fieldGroup) + nodes: fieldNodes }); } return runtimeType; @@ -24825,92 +24010,34 @@ function ensureValidRuntimeType(runtimeTypeName, exeContext, returnType, fieldGr /** * Complete an Object value by executing all sub-selections. */ -function completeObjectValue(exeContext, returnType, fieldGroup, info, path, result, incrementalContext, deferMap) { - // If there is an isTypeOf predicate function, call it with the + +function completeObjectValue(exeContext, returnType, fieldNodes, info, path, result) { + // Collect sub-fields to execute to complete this value. + const subFieldNodes = collectSubfields(exeContext, returnType, fieldNodes); // If there is an isTypeOf predicate function, call it with the // current result. If isTypeOf returns false, then raise an error rather // than continuing execution. + if (returnType.isTypeOf) { const isTypeOf = returnType.isTypeOf(result, exeContext.contextValue, info); if ((0, _isPromise.isPromise)(isTypeOf)) { return isTypeOf.then(resolvedIsTypeOf => { if (!resolvedIsTypeOf) { - throw invalidReturnTypeError(returnType, result, fieldGroup); + throw invalidReturnTypeError(returnType, result, fieldNodes); } - return collectAndExecuteSubfields(exeContext, returnType, fieldGroup, path, result, incrementalContext, deferMap); + return executeFields(exeContext, returnType, result, path, subFieldNodes); }); } if (!isTypeOf) { - throw invalidReturnTypeError(returnType, result, fieldGroup); + throw invalidReturnTypeError(returnType, result, fieldNodes); } } - return collectAndExecuteSubfields(exeContext, returnType, fieldGroup, path, result, incrementalContext, deferMap); + return executeFields(exeContext, returnType, result, path, subFieldNodes); } -function invalidReturnTypeError(returnType, result, fieldGroup) { +function invalidReturnTypeError(returnType, result, fieldNodes) { return new _GraphQLError.GraphQLError(`Expected value of type "${returnType.name}" but got: ${(0, _inspect.inspect)(result)}.`, { - nodes: toNodes(fieldGroup) + nodes: fieldNodes }); } -/** - * Instantiates new DeferredFragmentRecords for the given path within an - * incremental data record, returning an updated map of DeferUsage - * objects to DeferredFragmentRecords. - * - * Note: As defer directives may be used with operations returning lists, - * a DeferUsage object may correspond to many DeferredFragmentRecords. - * - * DeferredFragmentRecord creation includes the following steps: - * 1. The new DeferredFragmentRecord is instantiated at the given path. - * 2. The parent result record is calculated from the given incremental data - * record. - * 3. The IncrementalPublisher is notified that a new DeferredFragmentRecord - * with the calculated parent has been added; the record will be released only - * after the parent has completed. - * - */ -function addNewDeferredFragments(newDeferUsages, newDeferMap, path) { - // For each new deferUsage object: - for (const newDeferUsage of newDeferUsages) { - const parentDeferUsage = newDeferUsage.parentDeferUsage; - const parent = parentDeferUsage === undefined ? undefined : deferredFragmentRecordFromDeferUsage(parentDeferUsage, newDeferMap); - // Instantiate the new record. - const deferredFragmentRecord = new _types.DeferredFragmentRecord(path, newDeferUsage.label, parent); - // Update the map. - newDeferMap.set(newDeferUsage, deferredFragmentRecord); - } - return newDeferMap; -} -function deferredFragmentRecordFromDeferUsage(deferUsage, deferMap) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return deferMap.get(deferUsage); -} -function collectAndExecuteSubfields(exeContext, returnType, fieldGroup, path, result, incrementalContext, deferMap) { - // Collect sub-fields to execute to complete this value. - const collectedSubfields = collectSubfields(exeContext, returnType, fieldGroup); - let groupedFieldSet = collectedSubfields.groupedFieldSet; - const newDeferUsages = collectedSubfields.newDeferUsages; - if (deferMap === undefined && newDeferUsages.length === 0) { - return executeFields(exeContext, returnType, result, path, groupedFieldSet, incrementalContext, undefined); - } - const subExecutionPlan = buildSubExecutionPlan(groupedFieldSet, incrementalContext === null || incrementalContext === void 0 ? void 0 : incrementalContext.deferUsageSet); - groupedFieldSet = subExecutionPlan.groupedFieldSet; - const newGroupedFieldSets = subExecutionPlan.newGroupedFieldSets; - const newDeferMap = addNewDeferredFragments(newDeferUsages, new Map(deferMap), path); - const subFields = executeFields(exeContext, returnType, result, path, groupedFieldSet, incrementalContext, newDeferMap); - if (newGroupedFieldSets.size > 0) { - const newPendingExecutionGroups = collectExecutionGroups(exeContext, returnType, result, path, incrementalContext === null || incrementalContext === void 0 ? void 0 : incrementalContext.deferUsageSet, newGroupedFieldSets, newDeferMap); - return withNewExecutionGroups(subFields, newPendingExecutionGroups); - } - return subFields; -} -function buildSubExecutionPlan(originalGroupedFieldSet, deferUsageSet) { - let executionPlan = originalGroupedFieldSet._executionPlan; - if (executionPlan !== undefined) { - return executionPlan; - } - executionPlan = (0, _buildExecutionPlan.buildExecutionPlan)(originalGroupedFieldSet, deferUsageSet); - originalGroupedFieldSet._executionPlan = executionPlan; - return executionPlan; -} /** * If a resolveType function is not given, then a default resolve behavior is * used which attempts two strategies: @@ -24921,12 +24048,13 @@ function buildSubExecutionPlan(originalGroupedFieldSet, deferUsageSet) { * Otherwise, test each possible type for the abstract type by calling * isTypeOf for the object being coerced, returning the first type that matches. */ + const defaultTypeResolver = function (value, contextValue, info, abstractType) { // First, look for `__typename`. if ((0, _isObjectLike.isObjectLike)(value) && typeof value.__typename === 'string') { return value.__typename; - } - // Otherwise, test each possible type. + } // Otherwise, test each possible type. + const possibleTypes = info.schema.getPossibleTypes(abstractType); const promisedIsTypeOfResults = []; for (let i = 0; i < possibleTypes.length; i++) { @@ -24968,340 +24096,27 @@ const defaultFieldResolver = function (source, args, contextValue, info) { } }; /** - * Implements the "Subscribe" algorithm described in the GraphQL specification. + * This method looks up the field on the given type definition. + * It has special casing for the three introspection fields, + * __schema, __type and __typename. __typename is special because + * it can always be queried as a field, even in situations where no + * other fields are allowed, like on a Union. __schema and __type + * could get automatically added to the query type, but that would + * require mutating type definitions, which would cause issues. * - * Returns a Promise which resolves to either an AsyncIterator (if successful) - * or an ExecutionResult (error). The promise will be rejected if the schema or - * other arguments to this function are invalid, or if the resolved event stream - * is not an async iterable. - * - * If the client-provided arguments to this function do not result in a - * compliant subscription, a GraphQL Response (ExecutionResult) with descriptive - * errors and no data will be returned. - * - * If the source stream could not be created due to faulty subscription resolver - * logic or underlying systems, the promise will resolve to a single - * ExecutionResult containing `errors` and no `data`. - * - * If the operation succeeded, the promise resolves to an AsyncIterator, which - * yields a stream of ExecutionResults representing the response stream. - * - * This function does not support incremental delivery (`@defer` and `@stream`). - * If an operation which would defer or stream data is executed with this - * function, a field error will be raised at the location of the `@defer` or - * `@stream` directive. - * - * Accepts an object with named arguments. + * @internal */ exports.defaultFieldResolver = defaultFieldResolver; -function subscribe(args) { - // If a valid execution context cannot be created due to incorrect arguments, - // a "Response" with only errors is returned. - const exeContext = buildExecutionContext(args); - // Return early errors if execution context failed. - if (!('schema' in exeContext)) { - return { - errors: exeContext - }; +function getFieldDef(schema, parentType, fieldNode) { + const fieldName = fieldNode.name.value; + if (fieldName === _introspection.SchemaMetaFieldDef.name && schema.getQueryType() === parentType) { + return _introspection.SchemaMetaFieldDef; + } else if (fieldName === _introspection.TypeMetaFieldDef.name && schema.getQueryType() === parentType) { + return _introspection.TypeMetaFieldDef; + } else if (fieldName === _introspection.TypeNameMetaFieldDef.name) { + return _introspection.TypeNameMetaFieldDef; } - const resultOrStream = createSourceEventStreamImpl(exeContext); - if ((0, _isPromise.isPromise)(resultOrStream)) { - return resultOrStream.then(resolvedResultOrStream => mapSourceToResponse(exeContext, resolvedResultOrStream)); - } - return mapSourceToResponse(exeContext, resultOrStream); -} -function mapSourceToResponse(exeContext, resultOrStream) { - if (!(0, _isAsyncIterable.isAsyncIterable)(resultOrStream)) { - return resultOrStream; - } - // For each payload yielded from a subscription, map it over the normal - // GraphQL `execute` function, with `payload` as the rootValue. - // This implements the "MapSourceToResponseEvent" algorithm described in - // the GraphQL specification. The `execute` function provides the - // "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the - // "ExecuteQuery" algorithm, for which `execute` is also used. - return (0, _mapAsyncIterable.mapAsyncIterable)(resultOrStream, payload => executeOperation(buildPerEventExecutionContext(exeContext, payload))); -} -/** - * Implements the "CreateSourceEventStream" algorithm described in the - * GraphQL specification, resolving the subscription source event stream. - * - * Returns a Promise which resolves to either an AsyncIterable (if successful) - * or an ExecutionResult (error). The promise will be rejected if the schema or - * other arguments to this function are invalid, or if the resolved event stream - * is not an async iterable. - * - * If the client-provided arguments to this function do not result in a - * compliant subscription, a GraphQL Response (ExecutionResult) with - * descriptive errors and no data will be returned. - * - * If the the source stream could not be created due to faulty subscription - * resolver logic or underlying systems, the promise will resolve to a single - * ExecutionResult containing `errors` and no `data`. - * - * If the operation succeeded, the promise resolves to the AsyncIterable for the - * event stream returned by the resolver. - * - * A Source Event Stream represents a sequence of events, each of which triggers - * a GraphQL execution for that event. - * - * This may be useful when hosting the stateful subscription service in a - * different process or machine than the stateless GraphQL execution engine, - * or otherwise separating these two steps. For more on this, see the - * "Supporting Subscriptions at Scale" information in the GraphQL specification. - */ -function createSourceEventStream(args) { - // If a valid execution context cannot be created due to incorrect arguments, - // a "Response" with only errors is returned. - const exeContext = buildExecutionContext(args); - // Return early errors if execution context failed. - if (!('schema' in exeContext)) { - return { - errors: exeContext - }; - } - return createSourceEventStreamImpl(exeContext); -} -function createSourceEventStreamImpl(exeContext) { - try { - const eventStream = executeSubscription(exeContext); - if ((0, _isPromise.isPromise)(eventStream)) { - return eventStream.then(undefined, error => ({ - errors: [error] - })); - } - return eventStream; - } catch (error) { - return { - errors: [error] - }; - } -} -function executeSubscription(exeContext) { - const { - schema, - fragments, - operation, - variableValues, - rootValue - } = exeContext; - const rootType = schema.getSubscriptionType(); - if (rootType == null) { - throw new _GraphQLError.GraphQLError('Schema is not configured to execute subscription operation.', { - nodes: operation - }); - } - const { - groupedFieldSet - } = (0, _collectFields.collectFields)(schema, fragments, variableValues, rootType, operation); - const firstRootField = groupedFieldSet.entries().next().value; - const [responseName, fieldGroup] = firstRootField; - const fieldName = fieldGroup[0].node.name.value; - const fieldDef = schema.getField(rootType, fieldName); - const fieldNodes = fieldGroup.map(fieldDetails => fieldDetails.node); - if (!fieldDef) { - throw new _GraphQLError.GraphQLError(`The subscription field "${fieldName}" is not defined.`, { - nodes: fieldNodes - }); - } - const path = (0, _Path.addPath)(undefined, responseName, rootType.name); - const info = buildResolveInfo(exeContext, fieldDef, fieldNodes, rootType, path); - try { - var _fieldDef$subscribe; - // Implements the "ResolveFieldEventStream" algorithm from GraphQL specification. - // It differs from "ResolveFieldValue" due to providing a different `resolveFn`. - // Build a JS object of arguments from the field.arguments AST, using the - // variables scope to fulfill any variable references. - const args = (0, _values.getArgumentValues)(fieldDef, fieldNodes[0], variableValues); - // The resolve function's optional third argument is a context value that - // is provided to every resolve function within an execution. It is commonly - // used to represent an authenticated user, or request-specific caches. - const contextValue = exeContext.contextValue; - // Call the `subscribe()` resolver or the default resolver to produce an - // AsyncIterable yielding raw payloads. - const resolveFn = (_fieldDef$subscribe = fieldDef.subscribe) !== null && _fieldDef$subscribe !== void 0 ? _fieldDef$subscribe : exeContext.subscribeFieldResolver; - const result = resolveFn(rootValue, args, contextValue, info); - if ((0, _isPromise.isPromise)(result)) { - return result.then(assertEventStream).then(undefined, error => { - throw (0, _locatedError.locatedError)(error, fieldNodes, (0, _Path.pathToArray)(path)); - }); - } - return assertEventStream(result); - } catch (error) { - throw (0, _locatedError.locatedError)(error, fieldNodes, (0, _Path.pathToArray)(path)); - } -} -function assertEventStream(result) { - if (result instanceof Error) { - throw result; - } - // Assert field returned an event stream, otherwise yield an error. - if (!(0, _isAsyncIterable.isAsyncIterable)(result)) { - throw new _GraphQLError.GraphQLError('Subscription field must return Async Iterable. ' + `Received: ${(0, _inspect.inspect)(result)}.`); - } - return result; -} -function collectExecutionGroups(exeContext, parentType, sourceValue, path, parentDeferUsages, newGroupedFieldSets, deferMap) { - const newPendingExecutionGroups = []; - for (const [deferUsageSet, groupedFieldSet] of newGroupedFieldSets) { - const deferredFragmentRecords = getDeferredFragmentRecords(deferUsageSet, deferMap); - const pendingExecutionGroup = { - deferredFragmentRecords, - result: undefined - }; - const executor = () => executeExecutionGroup(pendingExecutionGroup, exeContext, parentType, sourceValue, path, groupedFieldSet, { - errors: undefined, - deferUsageSet - }, deferMap); - if (exeContext.enableEarlyExecution) { - pendingExecutionGroup.result = new _BoxedPromiseOrValue.BoxedPromiseOrValue(shouldDefer(parentDeferUsages, deferUsageSet) ? Promise.resolve().then(executor) : executor()); - } else { - pendingExecutionGroup.result = () => new _BoxedPromiseOrValue.BoxedPromiseOrValue(executor()); - } - newPendingExecutionGroups.push(pendingExecutionGroup); - } - return newPendingExecutionGroups; -} -function shouldDefer(parentDeferUsages, deferUsages) { - // If we have a new child defer usage, defer. - // Otherwise, this defer usage was already deferred when it was initially - // encountered, and is now in the midst of executing early, so the new - // deferred grouped fields set can be executed immediately. - return parentDeferUsages === undefined || !Array.from(deferUsages).every(deferUsage => parentDeferUsages.has(deferUsage)); -} -function executeExecutionGroup(pendingExecutionGroup, exeContext, parentType, sourceValue, path, groupedFieldSet, incrementalContext, deferMap) { - let result; - try { - result = executeFields(exeContext, parentType, sourceValue, path, groupedFieldSet, incrementalContext, deferMap); - } catch (error) { - return { - pendingExecutionGroup, - path: (0, _Path.pathToArray)(path), - errors: withError(incrementalContext.errors, error) - }; - } - if ((0, _isPromise.isPromise)(result)) { - return result.then(resolved => buildCompletedExecutionGroup(incrementalContext.errors, pendingExecutionGroup, path, resolved), error => ({ - pendingExecutionGroup, - path: (0, _Path.pathToArray)(path), - errors: withError(incrementalContext.errors, error) - })); - } - return buildCompletedExecutionGroup(incrementalContext.errors, pendingExecutionGroup, path, result); -} -function buildCompletedExecutionGroup(errors, pendingExecutionGroup, path, result) { - return { - pendingExecutionGroup, - path: (0, _Path.pathToArray)(path), - result: errors === undefined ? { - data: result[0] - } : { - data: result[0], - errors - }, - incrementalDataRecords: result[1] - }; -} -function getDeferredFragmentRecords(deferUsages, deferMap) { - return Array.from(deferUsages).map(deferUsage => deferredFragmentRecordFromDeferUsage(deferUsage, deferMap)); -} -function buildSyncStreamItemQueue(initialItem, initialIndex, streamPath, iterator, exeContext, fieldGroup, info, itemType) { - const streamItemQueue = []; - const enableEarlyExecution = exeContext.enableEarlyExecution; - const firstExecutor = () => { - const initialPath = (0, _Path.addPath)(streamPath, initialIndex, undefined); - const firstStreamItem = new _BoxedPromiseOrValue.BoxedPromiseOrValue(completeStreamItem(initialPath, initialItem, exeContext, { - errors: undefined - }, fieldGroup, info, itemType)); - let iteration = iterator.next(); - let currentIndex = initialIndex + 1; - let currentStreamItem = firstStreamItem; - while (!iteration.done) { - // TODO: add test case for early sync termination - /* c8 ignore next 6 */ - if (currentStreamItem instanceof _BoxedPromiseOrValue.BoxedPromiseOrValue) { - const result = currentStreamItem.value; - if (!(0, _isPromise.isPromise)(result) && result.errors !== undefined) { - break; - } - } - const itemPath = (0, _Path.addPath)(streamPath, currentIndex, undefined); - const value = iteration.value; - const currentExecutor = () => completeStreamItem(itemPath, value, exeContext, { - errors: undefined - }, fieldGroup, info, itemType); - currentStreamItem = enableEarlyExecution ? new _BoxedPromiseOrValue.BoxedPromiseOrValue(currentExecutor()) : () => new _BoxedPromiseOrValue.BoxedPromiseOrValue(currentExecutor()); - streamItemQueue.push(currentStreamItem); - iteration = iterator.next(); - currentIndex = initialIndex + 1; - } - streamItemQueue.push(new _BoxedPromiseOrValue.BoxedPromiseOrValue({})); - return firstStreamItem.value; - }; - streamItemQueue.push(enableEarlyExecution ? new _BoxedPromiseOrValue.BoxedPromiseOrValue(Promise.resolve().then(firstExecutor)) : () => new _BoxedPromiseOrValue.BoxedPromiseOrValue(firstExecutor())); - return streamItemQueue; -} -function buildAsyncStreamItemQueue(initialIndex, streamPath, asyncIterator, exeContext, fieldGroup, info, itemType) { - const streamItemQueue = []; - const executor = () => getNextAsyncStreamItemResult(streamItemQueue, streamPath, initialIndex, asyncIterator, exeContext, fieldGroup, info, itemType); - streamItemQueue.push(exeContext.enableEarlyExecution ? new _BoxedPromiseOrValue.BoxedPromiseOrValue(executor()) : () => new _BoxedPromiseOrValue.BoxedPromiseOrValue(executor())); - return streamItemQueue; -} -async function getNextAsyncStreamItemResult(streamItemQueue, streamPath, index, asyncIterator, exeContext, fieldGroup, info, itemType) { - let iteration; - try { - iteration = await asyncIterator.next(); - } catch (error) { - return { - errors: [(0, _locatedError.locatedError)(error, toNodes(fieldGroup), (0, _Path.pathToArray)(streamPath))] - }; - } - if (iteration.done) { - return {}; - } - const itemPath = (0, _Path.addPath)(streamPath, index, undefined); - const result = completeStreamItem(itemPath, iteration.value, exeContext, { - errors: undefined - }, fieldGroup, info, itemType); - const executor = () => getNextAsyncStreamItemResult(streamItemQueue, streamPath, index + 1, asyncIterator, exeContext, fieldGroup, info, itemType); - streamItemQueue.push(exeContext.enableEarlyExecution ? new _BoxedPromiseOrValue.BoxedPromiseOrValue(executor()) : () => new _BoxedPromiseOrValue.BoxedPromiseOrValue(executor())); - return result; -} -function completeStreamItem(itemPath, item, exeContext, incrementalContext, fieldGroup, info, itemType) { - if ((0, _isPromise.isPromise)(item)) { - return completePromisedValue(exeContext, itemType, fieldGroup, info, itemPath, item, incrementalContext, new Map()).then(resolvedItem => buildStreamItemResult(incrementalContext.errors, resolvedItem), error => ({ - errors: withError(incrementalContext.errors, error) - })); - } - let result; - try { - try { - result = completeValue(exeContext, itemType, fieldGroup, info, itemPath, item, incrementalContext, new Map()); - } catch (rawError) { - handleFieldError(rawError, exeContext, itemType, fieldGroup, itemPath, incrementalContext); - result = [null, undefined]; - } - } catch (error) { - return { - errors: withError(incrementalContext.errors, error) - }; - } - if ((0, _isPromise.isPromise)(result)) { - return result.then(undefined, rawError => { - handleFieldError(rawError, exeContext, itemType, fieldGroup, itemPath, incrementalContext); - return [null, undefined]; - }).then(resolvedItem => buildStreamItemResult(incrementalContext.errors, resolvedItem), error => ({ - errors: withError(incrementalContext.errors, error) - })); - } - return buildStreamItemResult(incrementalContext.errors, result); -} -function buildStreamItemResult(errors, result) { - return { - item: result[0], - errors, - incrementalDataRecords: result[1] - }; + return parentType.getFields()[fieldName]; } /***/ }), @@ -25320,7 +24135,7 @@ Object.defineProperty(exports, "__esModule", ({ Object.defineProperty(exports, "createSourceEventStream", ({ enumerable: true, get: function () { - return _execute.createSourceEventStream; + return _subscribe.createSourceEventStream; } })); Object.defineProperty(exports, "defaultFieldResolver", ({ @@ -25347,12 +24162,6 @@ Object.defineProperty(exports, "executeSync", ({ return _execute.executeSync; } })); -Object.defineProperty(exports, "experimentalExecuteIncrementally", ({ - enumerable: true, - get: function () { - return _execute.experimentalExecuteIncrementally; - } -})); Object.defineProperty(exports, "getArgumentValues", ({ enumerable: true, get: function () { @@ -25380,18 +24189,19 @@ Object.defineProperty(exports, "responsePathAsArray", ({ Object.defineProperty(exports, "subscribe", ({ enumerable: true, get: function () { - return _execute.subscribe; + return _subscribe.subscribe; } })); var _Path = __webpack_require__(/*! ../jsutils/Path.mjs */ "../../../node_modules/graphql/jsutils/Path.mjs"); var _execute = __webpack_require__(/*! ./execute.mjs */ "../../../node_modules/graphql/execution/execute.mjs"); +var _subscribe = __webpack_require__(/*! ./subscribe.mjs */ "../../../node_modules/graphql/execution/subscribe.mjs"); var _values = __webpack_require__(/*! ./values.mjs */ "../../../node_modules/graphql/execution/values.mjs"); /***/ }), -/***/ "../../../node_modules/graphql/execution/mapAsyncIterable.mjs": +/***/ "../../../node_modules/graphql/execution/mapAsyncIterator.mjs": /*!********************************************************************!*\ - !*** ../../../node_modules/graphql/execution/mapAsyncIterable.mjs ***! + !*** ../../../node_modules/graphql/execution/mapAsyncIterator.mjs ***! \********************************************************************/ /***/ (function(__unused_webpack_module, exports) { @@ -25400,12 +24210,12 @@ var _values = __webpack_require__(/*! ./values.mjs */ "../../../node_modules/gra Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.mapAsyncIterable = mapAsyncIterable; +exports.mapAsyncIterator = mapAsyncIterator; /** * Given an AsyncIterable and a callback function, return an AsyncIterator * which produces values mapped via calling the callback function. */ -function mapAsyncIterable(iterable, callback) { +function mapAsyncIterator(iterable, callback) { const iterator = iterable[Symbol.asyncIterator](); async function mapResult(result) { if (result.done) { @@ -25455,49 +24265,201 @@ function mapAsyncIterable(iterable, callback) { /***/ }), -/***/ "../../../node_modules/graphql/execution/types.mjs": -/*!*********************************************************!*\ - !*** ../../../node_modules/graphql/execution/types.mjs ***! - \*********************************************************/ -/***/ (function(__unused_webpack_module, exports) { +/***/ "../../../node_modules/graphql/execution/subscribe.mjs": +/*!*************************************************************!*\ + !*** ../../../node_modules/graphql/execution/subscribe.mjs ***! + \*************************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DeferredFragmentRecord = void 0; -exports.isCancellableStreamRecord = isCancellableStreamRecord; -exports.isCompletedExecutionGroup = isCompletedExecutionGroup; -exports.isDeferredFragmentRecord = isDeferredFragmentRecord; -exports.isFailedExecutionGroup = isFailedExecutionGroup; -exports.isPendingExecutionGroup = isPendingExecutionGroup; -function isPendingExecutionGroup(incrementalDataRecord) { - return 'deferredFragmentRecords' in incrementalDataRecord; +exports.createSourceEventStream = createSourceEventStream; +exports.subscribe = subscribe; +var _devAssert = __webpack_require__(/*! ../jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); +var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); +var _isAsyncIterable = __webpack_require__(/*! ../jsutils/isAsyncIterable.mjs */ "../../../node_modules/graphql/jsutils/isAsyncIterable.mjs"); +var _Path = __webpack_require__(/*! ../jsutils/Path.mjs */ "../../../node_modules/graphql/jsutils/Path.mjs"); +var _GraphQLError = __webpack_require__(/*! ../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); +var _locatedError = __webpack_require__(/*! ../error/locatedError.mjs */ "../../../node_modules/graphql/error/locatedError.mjs"); +var _collectFields = __webpack_require__(/*! ./collectFields.mjs */ "../../../node_modules/graphql/execution/collectFields.mjs"); +var _execute = __webpack_require__(/*! ./execute.mjs */ "../../../node_modules/graphql/execution/execute.mjs"); +var _mapAsyncIterator = __webpack_require__(/*! ./mapAsyncIterator.mjs */ "../../../node_modules/graphql/execution/mapAsyncIterator.mjs"); +var _values = __webpack_require__(/*! ./values.mjs */ "../../../node_modules/graphql/execution/values.mjs"); +/** + * Implements the "Subscribe" algorithm described in the GraphQL specification. + * + * Returns a Promise which resolves to either an AsyncIterator (if successful) + * or an ExecutionResult (error). The promise will be rejected if the schema or + * other arguments to this function are invalid, or if the resolved event stream + * is not an async iterable. + * + * If the client-provided arguments to this function do not result in a + * compliant subscription, a GraphQL Response (ExecutionResult) with + * descriptive errors and no data will be returned. + * + * If the source stream could not be created due to faulty subscription + * resolver logic or underlying systems, the promise will resolve to a single + * ExecutionResult containing `errors` and no `data`. + * + * If the operation succeeded, the promise resolves to an AsyncIterator, which + * yields a stream of ExecutionResults representing the response stream. + * + * Accepts either an object with named arguments, or individual arguments. + */ + +async function subscribe(args) { + // Temporary for v15 to v16 migration. Remove in v17 + arguments.length < 2 || (0, _devAssert.devAssert)(false, 'graphql@16 dropped long-deprecated support for positional arguments, please pass an object instead.'); + const resultOrStream = await createSourceEventStream(args); + if (!(0, _isAsyncIterable.isAsyncIterable)(resultOrStream)) { + return resultOrStream; + } // For each payload yielded from a subscription, map it over the normal + // GraphQL `execute` function, with `payload` as the rootValue. + // This implements the "MapSourceToResponseEvent" algorithm described in + // the GraphQL specification. The `execute` function provides the + // "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the + // "ExecuteQuery" algorithm, for which `execute` is also used. + + const mapSourceToResponse = payload => (0, _execute.execute)({ + ...args, + rootValue: payload + }); // Map every source value to a ExecutionResult value as described above. + + return (0, _mapAsyncIterator.mapAsyncIterator)(resultOrStream, mapSourceToResponse); } -function isCompletedExecutionGroup(subsequentResult) { - return 'pendingExecutionGroup' in subsequentResult; +function toNormalizedArgs(args) { + const firstArg = args[0]; + if (firstArg && 'document' in firstArg) { + return firstArg; + } + return { + schema: firstArg, + // FIXME: when underlying TS bug fixed, see https://github.com/microsoft/TypeScript/issues/31613 + document: args[1], + rootValue: args[2], + contextValue: args[3], + variableValues: args[4], + operationName: args[5], + subscribeFieldResolver: args[6] + }; } -function isFailedExecutionGroup(completedExecutionGroup) { - return completedExecutionGroup.errors !== undefined; -} -/** @internal */ -class DeferredFragmentRecord { - constructor(path, label, parent) { - this.path = path; - this.label = label; - this.parent = parent; - this.pendingExecutionGroups = new Set(); - this.successfulExecutionGroups = new Set(); - this.children = new Set(); +/** + * Implements the "CreateSourceEventStream" algorithm described in the + * GraphQL specification, resolving the subscription source event stream. + * + * Returns a Promise which resolves to either an AsyncIterable (if successful) + * or an ExecutionResult (error). The promise will be rejected if the schema or + * other arguments to this function are invalid, or if the resolved event stream + * is not an async iterable. + * + * If the client-provided arguments to this function do not result in a + * compliant subscription, a GraphQL Response (ExecutionResult) with + * descriptive errors and no data will be returned. + * + * If the the source stream could not be created due to faulty subscription + * resolver logic or underlying systems, the promise will resolve to a single + * ExecutionResult containing `errors` and no `data`. + * + * If the operation succeeded, the promise resolves to the AsyncIterable for the + * event stream returned by the resolver. + * + * A Source Event Stream represents a sequence of events, each of which triggers + * a GraphQL execution for that event. + * + * This may be useful when hosting the stateful subscription service in a + * different process or machine than the stateless GraphQL execution engine, + * or otherwise separating these two steps. For more on this, see the + * "Supporting Subscriptions at Scale" information in the GraphQL specification. + */ + +async function createSourceEventStream(...rawArgs) { + const args = toNormalizedArgs(rawArgs); + const { + schema, + document, + variableValues + } = args; // If arguments are missing or incorrectly typed, this is an internal + // developer mistake which should throw an early error. + + (0, _execute.assertValidExecutionArguments)(schema, document, variableValues); // If a valid execution context cannot be created due to incorrect arguments, + // a "Response" with only errors is returned. + + const exeContext = (0, _execute.buildExecutionContext)(args); // Return early errors if execution context failed. + + if (!('schema' in exeContext)) { + return { + errors: exeContext + }; + } + try { + const eventStream = await executeSubscription(exeContext); // Assert field returned an event stream, otherwise yield an error. + + if (!(0, _isAsyncIterable.isAsyncIterable)(eventStream)) { + throw new Error('Subscription field must return Async Iterable. ' + `Received: ${(0, _inspect.inspect)(eventStream)}.`); + } + return eventStream; + } catch (error) { + // If it GraphQLError, report it as an ExecutionResult, containing only errors and no data. + // Otherwise treat the error as a system-class error and re-throw it. + if (error instanceof _GraphQLError.GraphQLError) { + return { + errors: [error] + }; + } + throw error; } } -exports.DeferredFragmentRecord = DeferredFragmentRecord; -function isDeferredFragmentRecord(deliveryGroup) { - return deliveryGroup instanceof DeferredFragmentRecord; -} -function isCancellableStreamRecord(deliveryGroup) { - return 'earlyReturn' in deliveryGroup; +async function executeSubscription(exeContext) { + const { + schema, + fragments, + operation, + variableValues, + rootValue + } = exeContext; + const rootType = schema.getSubscriptionType(); + if (rootType == null) { + throw new _GraphQLError.GraphQLError('Schema is not configured to execute subscription operation.', { + nodes: operation + }); + } + const rootFields = (0, _collectFields.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet); + const [responseName, fieldNodes] = [...rootFields.entries()][0]; + const fieldDef = (0, _execute.getFieldDef)(schema, rootType, fieldNodes[0]); + if (!fieldDef) { + const fieldName = fieldNodes[0].name.value; + throw new _GraphQLError.GraphQLError(`The subscription field "${fieldName}" is not defined.`, { + nodes: fieldNodes + }); + } + const path = (0, _Path.addPath)(undefined, responseName, rootType.name); + const info = (0, _execute.buildResolveInfo)(exeContext, fieldDef, fieldNodes, rootType, path); + try { + var _fieldDef$subscribe; + + // Implements the "ResolveFieldEventStream" algorithm from GraphQL specification. + // It differs from "ResolveFieldValue" due to providing a different `resolveFn`. + // Build a JS object of arguments from the field.arguments AST, using the + // variables scope to fulfill any variable references. + const args = (0, _values.getArgumentValues)(fieldDef, fieldNodes[0], variableValues); // The resolve function's optional third argument is a context value that + // is provided to every resolve function within an execution. It is commonly + // used to represent an authenticated user, or request-specific caches. + + const contextValue = exeContext.contextValue; // Call the `subscribe()` resolver or the default resolver to produce an + // AsyncIterable yielding raw payloads. + + const resolveFn = (_fieldDef$subscribe = fieldDef.subscribe) !== null && _fieldDef$subscribe !== void 0 ? _fieldDef$subscribe : exeContext.subscribeFieldResolver; + const eventStream = await resolveFn(rootValue, args, contextValue, info); + if (eventStream instanceof Error) { + throw eventStream; + } + return eventStream; + } catch (error) { + throw (0, _locatedError.locatedError)(error, fieldNodes, (0, _Path.pathToArray)(path)); + } } /***/ }), @@ -25517,6 +24479,7 @@ exports.getArgumentValues = getArgumentValues; exports.getDirectiveValues = getDirectiveValues; exports.getVariableValues = getVariableValues; var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); +var _keyMap = __webpack_require__(/*! ../jsutils/keyMap.mjs */ "../../../node_modules/graphql/jsutils/keyMap.mjs"); var _printPathArray = __webpack_require__(/*! ../jsutils/printPathArray.mjs */ "../../../node_modules/graphql/jsutils/printPathArray.mjs"); var _GraphQLError = __webpack_require__(/*! ../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); var _kinds = __webpack_require__(/*! ../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); @@ -25570,7 +24533,7 @@ function coerceVariableValues(schema, varDefNodes, inputs, onError) { })); continue; } - if (!Object.hasOwn(inputs, varName)) { + if (!hasOwnProperty(inputs, varName)) { if (varDefNode.defaultValue) { coercedValues[varName] = (0, _valueFromAST.valueFromAST)(varDefNode.defaultValue, varType); } else if ((0, _definition.isNonNullType)(varType)) { @@ -25610,18 +24573,20 @@ function coerceVariableValues(schema, varDefNodes, inputs, onError) { * exposed to user code. Care should be taken to not pull values from the * Object prototype. */ + function getArgumentValues(def, node, variableValues) { var _node$arguments; - const coercedValues = {}; - // FIXME: https://github.com/graphql/graphql-js/issues/2203 + const coercedValues = {}; // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + const argumentNodes = (_node$arguments = node.arguments) !== null && _node$arguments !== void 0 ? _node$arguments : []; - const argNodeMap = new Map(argumentNodes.map(arg => [arg.name.value, arg])); + const argNodeMap = (0, _keyMap.keyMap)(argumentNodes, arg => arg.name.value); for (const argDef of def.args) { const name = argDef.name; const argType = argDef.type; - const argumentNode = argNodeMap.get(name); - if (argumentNode == null) { + const argumentNode = argNodeMap[name]; + if (!argumentNode) { if (argDef.defaultValue !== undefined) { coercedValues[name] = argDef.defaultValue; } else if ((0, _definition.isNonNullType)(argType)) { @@ -25635,7 +24600,7 @@ function getArgumentValues(def, node, variableValues) { let isNull = valueNode.kind === _kinds.Kind.NULL; if (valueNode.kind === _kinds.Kind.VARIABLE) { const variableName = valueNode.name.value; - if (variableValues == null || !Object.hasOwn(variableValues, variableName)) { + if (variableValues == null || !hasOwnProperty(variableValues, variableName)) { if (argDef.defaultValue !== undefined) { coercedValues[name] = argDef.defaultValue; } else if ((0, _definition.isNonNullType)(argType)) { @@ -25676,6 +24641,7 @@ function getArgumentValues(def, node, variableValues) { * exposed to user code. Care should be taken to not pull values from the * Object prototype. */ + function getDirectiveValues(directiveDef, node, variableValues) { var _node$directives; const directiveNode = (_node$directives = node.directives) === null || _node$directives === void 0 ? void 0 : _node$directives.find(directive => directive.name.value === directiveDef.name); @@ -25683,6 +24649,9 @@ function getDirectiveValues(directiveDef, node, variableValues) { return getArgumentValues(directiveDef, directiveNode, variableValues); } } +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} /***/ }), @@ -25699,11 +24668,52 @@ Object.defineProperty(exports, "__esModule", ({ })); exports.graphql = graphql; exports.graphqlSync = graphqlSync; +var _devAssert = __webpack_require__(/*! ./jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); var _isPromise = __webpack_require__(/*! ./jsutils/isPromise.mjs */ "../../../node_modules/graphql/jsutils/isPromise.mjs"); var _parser = __webpack_require__(/*! ./language/parser.mjs */ "../../../node_modules/graphql/language/parser.mjs"); var _validate = __webpack_require__(/*! ./type/validate.mjs */ "../../../node_modules/graphql/type/validate.mjs"); var _validate2 = __webpack_require__(/*! ./validation/validate.mjs */ "../../../node_modules/graphql/validation/validate.mjs"); var _execute = __webpack_require__(/*! ./execution/execute.mjs */ "../../../node_modules/graphql/execution/execute.mjs"); +/** + * This is the primary entry point function for fulfilling GraphQL operations + * by parsing, validating, and executing a GraphQL document along side a + * GraphQL schema. + * + * More sophisticated GraphQL servers, such as those which persist queries, + * may wish to separate the validation and execution phases to a static time + * tooling step, and a server runtime step. + * + * Accepts either an object with named arguments, or individual arguments: + * + * schema: + * The GraphQL type system to use when validating and executing a query. + * source: + * A GraphQL language formatted string representing the requested operation. + * rootValue: + * The value provided as the first argument to resolver functions on the top + * level type (e.g. the query object type). + * contextValue: + * The context value is provided as an argument to resolver functions after + * field arguments. It is used to pass shared information useful at any point + * during executing this query, for example the currently logged in user and + * connections to databases or other services. + * variableValues: + * A mapping of variable name to runtime value to use for all variables + * defined in the requestString. + * operationName: + * The name of the operation to use if requestString contains multiple + * possible operations. Can be omitted if requestString contains only + * one operation. + * fieldResolver: + * A resolver function to use when one is not provided by the schema. + * If not provided, the default field resolver is used (which looks for a + * value or method on the source value with the field's name). + * typeResolver: + * A type resolver function to use when none is provided by the schema. + * If not provided, the default type resolver is used (which looks for a + * `__typename` field or alternatively calls the `isTypeOf` method). + */ + function graphql(args) { // Always return a Promise for a consistent API. return new Promise(resolve => resolve(graphqlImpl(args))); @@ -25714,15 +24724,18 @@ function graphql(args) { * However, it guarantees to complete synchronously (or throw an error) assuming * that all field resolvers are also synchronous. */ + function graphqlSync(args) { - const result = graphqlImpl(args); - // Assert that the execution was synchronous. + const result = graphqlImpl(args); // Assert that the execution was synchronous. + if ((0, _isPromise.isPromise)(result)) { throw new Error('GraphQL execution failed to complete synchronously.'); } return result; } function graphqlImpl(args) { + // Temporary for v15 to v16 migration. Remove in v17 + arguments.length < 2 || (0, _devAssert.devAssert)(false, 'graphql@16 dropped long-deprecated support for positional arguments, please pass an object instead.'); const { schema, source, @@ -25732,15 +24745,15 @@ function graphqlImpl(args) { operationName, fieldResolver, typeResolver - } = args; - // Validate Schema + } = args; // Validate Schema + const schemaValidationErrors = (0, _validate.validateSchema)(schema); if (schemaValidationErrors.length > 0) { return { errors: schemaValidationErrors }; - } - // Parse + } // Parse + let document; try { document = (0, _parser.parse)(source); @@ -25748,15 +24761,15 @@ function graphqlImpl(args) { return { errors: [syntaxError] }; - } - // Validate + } // Validate + const validationErrors = (0, _validate2.validate)(schema, document); if (validationErrors.length > 0) { return { errors: validationErrors }; - } - // Execute + } // Execute + return (0, _execute.execute)({ schema, document, @@ -25848,12 +24861,6 @@ Object.defineProperty(exports, "GraphQLBoolean", ({ return _index.GraphQLBoolean; } })); -Object.defineProperty(exports, "GraphQLDeferDirective", ({ - enumerable: true, - get: function () { - return _index.GraphQLDeferDirective; - } -})); Object.defineProperty(exports, "GraphQLDeprecatedDirective", ({ enumerable: true, get: function () { @@ -25962,12 +24969,6 @@ Object.defineProperty(exports, "GraphQLSpecifiedByDirective", ({ return _index.GraphQLSpecifiedByDirective; } })); -Object.defineProperty(exports, "GraphQLStreamDirective", ({ - enumerable: true, - get: function () { - return _index.GraphQLStreamDirective; - } -})); Object.defineProperty(exports, "GraphQLString", ({ enumerable: true, get: function () { @@ -26430,6 +25431,12 @@ Object.defineProperty(exports, "assertUnionType", ({ return _index.assertUnionType; } })); +Object.defineProperty(exports, "assertValidName", ({ + enumerable: true, + get: function () { + return _index6.assertValidName; + } +})); Object.defineProperty(exports, "assertValidSchema", ({ enumerable: true, get: function () { @@ -26514,12 +25521,6 @@ Object.defineProperty(exports, "executeSync", ({ return _index3.executeSync; } })); -Object.defineProperty(exports, "experimentalExecuteIncrementally", ({ - enumerable: true, - get: function () { - return _index3.experimentalExecuteIncrementally; - } -})); Object.defineProperty(exports, "extendSchema", ({ enumerable: true, get: function () { @@ -26538,6 +25539,12 @@ Object.defineProperty(exports, "findDangerousChanges", ({ return _index6.findDangerousChanges; } })); +Object.defineProperty(exports, "formatError", ({ + enumerable: true, + get: function () { + return _index5.formatError; + } +})); Object.defineProperty(exports, "getArgumentValues", ({ enumerable: true, get: function () { @@ -26586,12 +25593,24 @@ Object.defineProperty(exports, "getOperationAST", ({ return _index6.getOperationAST; } })); +Object.defineProperty(exports, "getOperationRootType", ({ + enumerable: true, + get: function () { + return _index6.getOperationRootType; + } +})); Object.defineProperty(exports, "getVariableValues", ({ enumerable: true, get: function () { return _index3.getVariableValues; } })); +Object.defineProperty(exports, "getVisitFn", ({ + enumerable: true, + get: function () { + return _index2.getVisitFn; + } +})); Object.defineProperty(exports, "graphql", ({ enumerable: true, get: function () { @@ -26712,12 +25731,6 @@ Object.defineProperty(exports, "isNonNullType", ({ return _index.isNonNullType; } })); -Object.defineProperty(exports, "isNullabilityAssertionNode", ({ - enumerable: true, - get: function () { - return _index2.isNullabilityAssertionNode; - } -})); Object.defineProperty(exports, "isNullableType", ({ enumerable: true, get: function () { @@ -26826,6 +25839,12 @@ Object.defineProperty(exports, "isUnionType", ({ return _index.isUnionType; } })); +Object.defineProperty(exports, "isValidNameError", ({ + enumerable: true, + get: function () { + return _index6.isValidNameError; + } +})); Object.defineProperty(exports, "isValueNode", ({ enumerable: true, get: function () { @@ -26880,10 +25899,10 @@ Object.defineProperty(exports, "print", ({ return _index2.print; } })); -Object.defineProperty(exports, "printDirective", ({ +Object.defineProperty(exports, "printError", ({ enumerable: true, get: function () { - return _index6.printDirective; + return _index5.printError; } })); Object.defineProperty(exports, "printIntrospectionSchema", ({ @@ -27053,76 +26072,6 @@ var _index6 = __webpack_require__(/*! ./utilities/index.mjs */ "../../../node_mo /***/ }), -/***/ "../../../node_modules/graphql/jsutils/AccumulatorMap.mjs": -/*!****************************************************************!*\ - !*** ../../../node_modules/graphql/jsutils/AccumulatorMap.mjs ***! - \****************************************************************/ -/***/ (function(__unused_webpack_module, exports) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.AccumulatorMap = void 0; -/** - * ES6 Map with additional `add` method to accumulate items. - */ -class AccumulatorMap extends Map { - get [Symbol.toStringTag]() { - return 'AccumulatorMap'; - } - add(key, item) { - const group = this.get(key); - if (group === undefined) { - this.set(key, [item]); - } else { - group.push(item); - } - } -} -exports.AccumulatorMap = AccumulatorMap; - -/***/ }), - -/***/ "../../../node_modules/graphql/jsutils/BoxedPromiseOrValue.mjs": -/*!*********************************************************************!*\ - !*** ../../../node_modules/graphql/jsutils/BoxedPromiseOrValue.mjs ***! - \*********************************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.BoxedPromiseOrValue = void 0; -var _isPromise = __webpack_require__(/*! ./isPromise.mjs */ "../../../node_modules/graphql/jsutils/isPromise.mjs"); -/** - * A BoxedPromiseOrValue is a container for a value or promise where the value - * will be updated when the promise resolves. - * - * A BoxedPromiseOrValue may only be used with promises whose possible - * rejection has already been handled, otherwise this will lead to unhandled - * promise rejections. - * - * @internal - * */ -class BoxedPromiseOrValue { - constructor(value) { - this.value = value; - if ((0, _isPromise.isPromise)(value)) { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - value.then(resolved => { - this.value = resolved; - }); - } - } -} -exports.BoxedPromiseOrValue = BoxedPromiseOrValue; - -/***/ }), - /***/ "../../../node_modules/graphql/jsutils/Path.mjs": /*!******************************************************!*\ !*** ../../../node_modules/graphql/jsutils/Path.mjs ***! @@ -27149,6 +26098,7 @@ function addPath(prev, key, typename) { /** * Given a Path, return an Array of the path keys. */ + function pathToArray(path) { const flattened = []; let curr = path; @@ -27161,27 +26111,6 @@ function pathToArray(path) { /***/ }), -/***/ "../../../node_modules/graphql/jsutils/capitalize.mjs": -/*!************************************************************!*\ - !*** ../../../node_modules/graphql/jsutils/capitalize.mjs ***! - \************************************************************/ -/***/ (function(__unused_webpack_module, exports) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.capitalize = capitalize; -/** - * Converts the first character of string to upper case and the remaining to lower case. - */ -function capitalize(str) { - return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); -} - -/***/ }), - /***/ "../../../node_modules/graphql/jsutils/devAssert.mjs": /*!***********************************************************!*\ !*** ../../../node_modules/graphql/jsutils/devAssert.mjs ***! @@ -27195,7 +26124,8 @@ Object.defineProperty(exports, "__esModule", ({ })); exports.devAssert = devAssert; function devAssert(condition, message) { - if (!condition) { + const booleanCondition = Boolean(condition); + if (!booleanCondition) { throw new Error(message); } } @@ -27206,7 +26136,7 @@ function devAssert(condition, message) { /*!************************************************************!*\ !*** ../../../node_modules/graphql/jsutils/didYouMean.mjs ***! \************************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { +/***/ (function(__unused_webpack_module, exports) { @@ -27214,84 +26144,29 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.didYouMean = didYouMean; -var _formatList = __webpack_require__(/*! ./formatList.mjs */ "../../../node_modules/graphql/jsutils/formatList.mjs"); const MAX_SUGGESTIONS = 5; +/** + * Given [ A, B, C ] return ' Did you mean A, B, or C?'. + */ + function didYouMean(firstArg, secondArg) { - const [subMessage, suggestions] = secondArg ? [firstArg, secondArg] : [undefined, firstArg]; - if (suggestions.length === 0) { - return ''; - } + const [subMessage, suggestionsArg] = secondArg ? [firstArg, secondArg] : [undefined, firstArg]; let message = ' Did you mean '; - if (subMessage != null) { + if (subMessage) { message += subMessage + ' '; } - const suggestionList = (0, _formatList.orList)(suggestions.slice(0, MAX_SUGGESTIONS).map(x => `"${x}"`)); - return message + suggestionList + '?'; -} - -/***/ }), - -/***/ "../../../node_modules/graphql/jsutils/formatList.mjs": -/*!************************************************************!*\ - !*** ../../../node_modules/graphql/jsutils/formatList.mjs ***! - \************************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.andList = andList; -exports.orList = orList; -var _invariant = __webpack_require__(/*! ./invariant.mjs */ "../../../node_modules/graphql/jsutils/invariant.mjs"); -/** - * Given [ A, B, C ] return 'A, B, or C'. - */ -function orList(items) { - return formatList('or', items); -} -/** - * Given [ A, B, C ] return 'A, B, and C'. - */ -function andList(items) { - return formatList('and', items); -} -function formatList(conjunction, items) { - items.length !== 0 || (0, _invariant.invariant)(false); - switch (items.length) { + const suggestions = suggestionsArg.map(x => `"${x}"`); + switch (suggestions.length) { + case 0: + return ''; case 1: - return items[0]; + return message + suggestions[0] + '?'; case 2: - return items[0] + ' ' + conjunction + ' ' + items[1]; + return message + suggestions[0] + ' or ' + suggestions[1] + '?'; } - const allButLast = items.slice(0, -1); - const lastItem = items.at(-1); - return allButLast.join(', ') + ', ' + conjunction + ' ' + lastItem; -} - -/***/ }), - -/***/ "../../../node_modules/graphql/jsutils/getBySet.mjs": -/*!**********************************************************!*\ - !*** ../../../node_modules/graphql/jsutils/getBySet.mjs ***! - \**********************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.getBySet = getBySet; -var _isSameSet = __webpack_require__(/*! ./isSameSet.mjs */ "../../../node_modules/graphql/jsutils/isSameSet.mjs"); -function getBySet(map, setToMatch) { - for (const set of map.keys()) { - if ((0, _isSameSet.isSameSet)(set, setToMatch)) { - return map.get(set); - } - } - return undefined; + const selected = suggestions.slice(0, MAX_SUGGESTIONS); + const lastItem = selected.pop(); + return message + selected.join(', ') + ', or ' + lastItem + '?'; } /***/ }), @@ -27300,7 +26175,7 @@ function getBySet(map, setToMatch) { /*!*********************************************************!*\ !*** ../../../node_modules/graphql/jsutils/groupBy.mjs ***! \*********************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { +/***/ (function(__unused_webpack_module, exports) { @@ -27308,14 +26183,19 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.groupBy = groupBy; -var _AccumulatorMap = __webpack_require__(/*! ./AccumulatorMap.mjs */ "../../../node_modules/graphql/jsutils/AccumulatorMap.mjs"); /** * Groups array items into a Map, given a function to produce grouping key. */ function groupBy(list, keyFn) { - const result = new _AccumulatorMap.AccumulatorMap(); + const result = new Map(); for (const item of list) { - result.add(keyFn(item), item); + const key = keyFn(item); + const group = result.get(key); + if (group === undefined) { + result.set(key, [item]); + } else { + group.push(item); + } } return result; } @@ -27360,6 +26240,7 @@ const MAX_RECURSIVE_DEPTH = 2; /** * Used to print values in error messages. */ + function inspect(value) { return formatValue(value, []); } @@ -27384,8 +26265,8 @@ function formatObjectValue(value, previouslySeenValues) { } const seenValues = [...previouslySeenValues, value]; if (isJSONable(value)) { - const jsonValue = value.toJSON(); - // check for infinite recursion + const jsonValue = value.toJSON(); // check for infinite recursion + if (jsonValue !== value) { return typeof jsonValue === 'string' ? jsonValue : formatValue(jsonValue, seenValues); } @@ -27454,15 +26335,21 @@ Object.defineProperty(exports, "__esModule", ({ })); exports.instanceOf = void 0; var _inspect = __webpack_require__(/*! ./inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); +/* c8 ignore next 3 */ + +const isProduction = globalThis.process && +// eslint-disable-next-line no-undef +"development" === 'production'; /** * A replacement for instanceof which includes an error warning when multi-realm * constructors are detected. * See: https://expressjs.com/en/advanced/best-practice-performance.html#set-node_env-to-production * See: https://webpack.js.org/guides/production/ */ + const instanceOf = exports.instanceOf = /* c8 ignore next 6 */ // FIXME: https://github.com/graphql/graphql-js/issues/2317 -globalThis.process != null && globalThis.process.env.NODE_ENV === 'production' ? function instanceOf(value, constructor) { +isProduction ? function instanceOf(value, constructor) { return value instanceof constructor; } : function instanceOf(value, constructor) { if (value instanceof constructor) { @@ -27470,11 +26357,13 @@ globalThis.process != null && globalThis.process.env.NODE_ENV === 'production' ? } if (typeof value === 'object' && value !== null) { var _value$constructor; + // Prefer Symbol.toStringTag since it is immune to minification. const className = constructor.prototype[Symbol.toStringTag]; const valueClassName = // We still need to support constructor's name to detect conflicts with older versions of this library. - Symbol.toStringTag in value ? value[Symbol.toStringTag] : (_value$constructor = value.constructor) === null || _value$constructor === void 0 ? void 0 : _value$constructor.name; + Symbol.toStringTag in value // @ts-expect-error TS bug see, https://github.com/microsoft/TypeScript/issues/38009 + ? value[Symbol.toStringTag] : (_value$constructor = value.constructor) === null || _value$constructor === void 0 ? void 0 : _value$constructor.name; if (className === valueClassName) { const stringifiedValue = (0, _inspect.inspect)(value); throw new Error(`Cannot use ${className} "${stringifiedValue}" from another module or realm. @@ -27509,7 +26398,8 @@ Object.defineProperty(exports, "__esModule", ({ })); exports.invariant = invariant; function invariant(condition, message) { - if (!condition) { + const booleanCondition = Boolean(condition); + if (!booleanCondition) { throw new Error(message != null ? message : 'Unexpected invariant triggered.'); } } @@ -27617,32 +26507,6 @@ function isPromise(value) { /***/ }), -/***/ "../../../node_modules/graphql/jsutils/isSameSet.mjs": -/*!***********************************************************!*\ - !*** ../../../node_modules/graphql/jsutils/isSameSet.mjs ***! - \***********************************************************/ -/***/ (function(__unused_webpack_module, exports) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.isSameSet = isSameSet; -function isSameSet(setA, setB) { - if (setA.size !== setB.size) { - return false; - } - for (const item of setA) { - if (!setB.has(item)) { - return false; - } - } - return true; -} - -/***/ }), - /***/ "../../../node_modules/graphql/jsutils/keyMap.mjs": /*!********************************************************!*\ !*** ../../../node_modules/graphql/jsutils/keyMap.mjs ***! @@ -27904,15 +26768,14 @@ exports.promiseForObject = promiseForObject; * This is akin to bluebird's `Promise.props`, but implemented only using * `Promise.all` so it will work with any implementation of ES6 promises. */ -async function promiseForObject(object, callback) { - const keys = Object.keys(object); - const values = Object.values(object); - const resolvedValues = await Promise.all(values); - const resolvedObject = Object.create(null); - for (let i = 0; i < keys.length; ++i) { - resolvedObject[keys[i]] = resolvedValues[i]; - } - return callback(resolvedObject); +function promiseForObject(object) { + return Promise.all(Object.values(object)).then(resolvedValues => { + const resolvedObject = Object.create(null); + for (const [i, key] of Object.keys(object).entries()) { + resolvedObject[key] = resolvedValues[i]; + } + return resolvedObject; + }); } /***/ }), @@ -27947,39 +26810,6 @@ function promiseReduce(values, callbackFn, initialValue) { /***/ }), -/***/ "../../../node_modules/graphql/jsutils/promiseWithResolvers.mjs": -/*!**********************************************************************!*\ - !*** ../../../node_modules/graphql/jsutils/promiseWithResolvers.mjs ***! - \**********************************************************************/ -/***/ (function(__unused_webpack_module, exports) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.promiseWithResolvers = promiseWithResolvers; -/** - * Based on Promise.withResolvers proposal - * https://github.com/tc39/proposal-promise-with-resolvers - */ -function promiseWithResolvers() { - // these are assigned synchronously within the Promise constructor - let resolve; - let reject; - const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - return { - promise, - resolve, - reject - }; -} - -/***/ }), - /***/ "../../../node_modules/graphql/jsutils/suggestionList.mjs": /*!****************************************************************!*\ !*** ../../../node_modules/graphql/jsutils/suggestionList.mjs ***! @@ -27997,6 +26827,7 @@ var _naturalCompare = __webpack_require__(/*! ./naturalCompare.mjs */ "../../../ * Given an invalid input string and a list of valid options, returns a filtered * list of valid options sorted based on their similarity with the input. */ + function suggestionList(input, options) { const optionsByDistance = Object.create(null); const lexicalDistance = new LexicalDistance(input); @@ -28026,6 +26857,7 @@ function suggestionList(input, options) { * * This distance can be useful for detecting typos in input or sorting */ + class LexicalDistance { constructor(input) { this._input = input; @@ -28037,8 +26869,8 @@ class LexicalDistance { if (this._input === option) { return 0; } - const optionLowerCase = option.toLowerCase(); - // Any case change counts as a single edit + const optionLowerCase = option.toLowerCase(); // Any case change counts as a single edit + if (this._inputLowerCase === optionLowerCase) { return 1; } @@ -28068,7 +26900,8 @@ class LexicalDistance { // delete currentRow[j - 1] + 1, // insert - upRow[j - 1] + cost); + upRow[j - 1] + cost // substitute + ); if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) { // transposition const doubleDiagonalCell = rows[(i - 2) % 3][j - 2]; @@ -28078,8 +26911,8 @@ class LexicalDistance { smallestCell = currentCell; } currentRow[j] = currentCell; - } - // Early exit, since distance can't go smaller than smallest element of the previous row. + } // Early exit, since distance can't go smaller than smallest element of the previous row. + if (smallestCell > threshold) { return undefined; } @@ -28115,6 +26948,7 @@ var _inspect = __webpack_require__(/*! ./inspect.mjs */ "../../../node_modules/g /** * Sometimes a non-error is thrown, wrap it as an Error instance to ensure a consistent Error interface. */ + function toError(thrownValue) { return thrownValue instanceof Error ? thrownValue : new NonErrorThrown(thrownValue); } @@ -28174,6 +27008,25 @@ exports.isNode = isNode; * identify the region of the source from which the AST derived. */ class Location { + /** + * The character offset at which this Node begins. + */ + + /** + * The character offset at which this Node ends. + */ + + /** + * The Token at which this Node begins. + */ + + /** + * The Token at which this Node ends. + */ + + /** + * The Source document the AST represents. + */ constructor(startToken, endToken, source) { this.start = startToken.start; this.end = endToken.end; @@ -28197,14 +27050,45 @@ class Location { */ exports.Location = Location; class Token { - // eslint-disable-next-line max-params + /** + * The kind of Token. + */ + + /** + * The character offset at which this Node begins. + */ + + /** + * The character offset at which this Node ends. + */ + + /** + * The 1-indexed line number on which this Token appears. + */ + + /** + * The 1-indexed column number at which this Token begins. + */ + + /** + * For non-punctuation tokens, represents the interpreted value of the token. + * + * Note: is undefined for punctuation tokens, but typed as string for + * convenience in the parser. + */ + + /** + * Tokens exist as nodes in a double-linked-list amongst all tokens + * including ignored tokens. is always the first node and + * the last. + */ constructor(kind, start, end, line, column, value) { this.kind = kind; this.start = start; this.end = end; this.line = line; - this.column = column; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.column = column; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.value = value; this.prev = null; this.next = null; @@ -28221,6 +27105,10 @@ class Token { }; } } +/** + * The list of all possible AST node types. + */ + /** * @internal */ @@ -28232,16 +27120,8 @@ const QueryDocumentKeys = exports.QueryDocumentKeys = { VariableDefinition: ['variable', 'type', 'defaultValue', 'directives'], Variable: ['name'], SelectionSet: ['selections'], - Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet', - // Note: Client Controlled Nullability is experimental and may be changed - // or removed in the future. - 'nullabilityAssertion'], + Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet'], Argument: ['name', 'value'], - // Note: Client Controlled Nullability is experimental and may be changed - // or removed in the future. - ListNullabilityOperator: ['nullabilityAssertion'], - NonNullAssertion: ['nullabilityAssertion'], - ErrorBoundary: ['nullabilityAssertion'], FragmentSpread: ['name', 'directives'], InlineFragment: ['typeCondition', 'directives', 'selectionSet'], FragmentDefinition: ['name', @@ -28284,10 +27164,13 @@ const kindValues = new Set(Object.keys(QueryDocumentKeys)); /** * @internal */ + function isNode(maybeNode) { const maybeKind = maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.kind; return typeof maybeKind === 'string' && kindValues.has(maybeKind); } +/** Name */ + var OperationTypeNode; (function (OperationTypeNode) { OperationTypeNode['QUERY'] = 'query'; @@ -28320,28 +27203,28 @@ var _characterClasses = __webpack_require__(/*! ./characterClasses.mjs */ "../.. * * @internal */ + function dedentBlockStringLines(lines) { - var _firstNonEmptyLine; + var _firstNonEmptyLine2; let commonIndent = Number.MAX_SAFE_INTEGER; let firstNonEmptyLine = null; let lastNonEmptyLine = -1; for (let i = 0; i < lines.length; ++i) { + var _firstNonEmptyLine; const line = lines[i]; const indent = leadingWhitespace(line); if (indent === line.length) { continue; // skip empty lines } - firstNonEmptyLine ??= i; + firstNonEmptyLine = (_firstNonEmptyLine = firstNonEmptyLine) !== null && _firstNonEmptyLine !== void 0 ? _firstNonEmptyLine : i; lastNonEmptyLine = i; if (i !== 0 && indent < commonIndent) { commonIndent = indent; } } - return lines - // Remove common indentation from all lines but first. - .map((line, i) => i === 0 ? line : line.slice(commonIndent)) - // Remove leading and trailing blank lines. - .slice((_firstNonEmptyLine = firstNonEmptyLine) !== null && _firstNonEmptyLine !== void 0 ? _firstNonEmptyLine : 0, lastNonEmptyLine + 1); + return lines // Remove common indentation from all lines but first. + .map((line, i) => i === 0 ? line : line.slice(commonIndent)) // Remove leading and trailing blank lines. + .slice((_firstNonEmptyLine2 = firstNonEmptyLine) !== null && _firstNonEmptyLine2 !== void 0 ? _firstNonEmptyLine2 : 0, lastNonEmptyLine + 1); } function leadingWhitespace(str) { let i = 0; @@ -28353,6 +27236,7 @@ function leadingWhitespace(str) { /** * @internal */ + function isPrintableAsBlockString(value) { if (value === '') { return true; // empty string is printable @@ -28378,10 +27262,12 @@ function isPrintableAsBlockString(value) { case 0x000f: return false; // Has non-printable characters + case 0x000d: // \r return false; // Has \r or \r\n which will be replaced as \n + case 10: // \n if (isEmptyLine && !seenNonEmptyLine) { @@ -28392,12 +27278,13 @@ function isPrintableAsBlockString(value) { hasIndent = false; break; case 9: // \t + case 32: // - hasIndent ||= isEmptyLine; + hasIndent || (hasIndent = isEmptyLine); break; default: - hasCommonIndent &&= hasIndent; + hasCommonIndent && (hasCommonIndent = hasIndent); isEmptyLine = false; } } @@ -28416,24 +27303,25 @@ function isPrintableAsBlockString(value) { * * @internal */ + function printBlockString(value, options) { - const escapedValue = value.replaceAll('"""', '\\"""'); - // Expand a block string's raw value into independent lines. + const escapedValue = value.replace(/"""/g, '\\"""'); // Expand a block string's raw value into independent lines. + const lines = escapedValue.split(/\r\n|[\n\r]/g); - const isSingleLine = lines.length === 1; - // If common indentation is found we can fix some of those cases by adding leading new line - const forceLeadingNewLine = lines.length > 1 && lines.slice(1).every(line => line.length === 0 || (0, _characterClasses.isWhiteSpace)(line.charCodeAt(0))); - // Trailing triple quotes just looks confusing but doesn't force trailing new line - const hasTrailingTripleQuotes = escapedValue.endsWith('\\"""'); - // Trailing quote (single or double) or slash forces trailing new line + const isSingleLine = lines.length === 1; // If common indentation is found we can fix some of those cases by adding leading new line + + const forceLeadingNewLine = lines.length > 1 && lines.slice(1).every(line => line.length === 0 || (0, _characterClasses.isWhiteSpace)(line.charCodeAt(0))); // Trailing triple quotes just looks confusing but doesn't force trailing new line + + const hasTrailingTripleQuotes = escapedValue.endsWith('\\"""'); // Trailing quote (single or double) or slash forces trailing new line + const hasTrailingQuote = value.endsWith('"') && !hasTrailingTripleQuotes; const hasTrailingSlash = value.endsWith('\\'); const forceTrailingNewline = hasTrailingQuote || hasTrailingSlash; const printAsMultipleLines = !(options !== null && options !== void 0 && options.minimize) && ( // add leading and trailing new lines only if it improves readability !isSingleLine || value.length > 70 || forceTrailingNewline || forceLeadingNewLine || hasTrailingTripleQuotes); - let result = ''; - // Format a multi-line block quote to account for leading space. + let result = ''; // Format a multi-line block quote to account for leading space. + const skipLeadingNewLine = isSingleLine && (0, _characterClasses.isWhiteSpace)(value.charCodeAt(0)); if (printAsMultipleLines && !skipLeadingNewLine || forceLeadingNewLine) { result += '\n'; @@ -28481,6 +27369,7 @@ function isWhiteSpace(code) { * ``` * @internal */ + function isDigit(code) { return code >= 0x0030 && code <= 0x0039; } @@ -28494,6 +27383,7 @@ function isDigit(code) { * ``` * @internal */ + function isLetter(code) { return code >= 0x0061 && code <= 0x007a || // A-Z @@ -28508,6 +27398,7 @@ function isLetter(code) { * ``` * @internal */ + function isNameStart(code) { return isLetter(code) || code === 0x005f; } @@ -28520,6 +27411,7 @@ function isNameStart(code) { * ``` * @internal */ + function isNameContinue(code) { return isLetter(code) || isDigit(code) || code === 0x005f; } @@ -28543,7 +27435,6 @@ exports.DirectiveLocation = void 0; */ var DirectiveLocation; (function (DirectiveLocation) { - /** Request Definitions */ DirectiveLocation['QUERY'] = 'QUERY'; DirectiveLocation['MUTATION'] = 'MUTATION'; DirectiveLocation['SUBSCRIPTION'] = 'SUBSCRIPTION'; @@ -28552,7 +27443,6 @@ var DirectiveLocation; DirectiveLocation['FRAGMENT_SPREAD'] = 'FRAGMENT_SPREAD'; DirectiveLocation['INLINE_FRAGMENT'] = 'INLINE_FRAGMENT'; DirectiveLocation['VARIABLE_DEFINITION'] = 'VARIABLE_DEFINITION'; - /** Type System Definitions */ DirectiveLocation['SCHEMA'] = 'SCHEMA'; DirectiveLocation['SCALAR'] = 'SCALAR'; DirectiveLocation['OBJECT'] = 'OBJECT'; @@ -28566,6 +27456,12 @@ var DirectiveLocation; DirectiveLocation['INPUT_FIELD_DEFINITION'] = 'INPUT_FIELD_DEFINITION'; })(DirectiveLocation || (exports.DirectiveLocation = DirectiveLocation = {})); +/** + * The enum type representing the directive location values. + * + * @deprecated Please use `DirectiveLocation`. Will be remove in v17. + */ + /***/ }), /***/ "../../../node_modules/graphql/language/index.mjs": @@ -28645,6 +27541,12 @@ Object.defineProperty(exports, "getLocation", ({ return _location.getLocation; } })); +Object.defineProperty(exports, "getVisitFn", ({ + enumerable: true, + get: function () { + return _visitor.getVisitFn; + } +})); Object.defineProperty(exports, "isConstValueNode", ({ enumerable: true, get: function () { @@ -28663,12 +27565,6 @@ Object.defineProperty(exports, "isExecutableDefinitionNode", ({ return _predicates.isExecutableDefinitionNode; } })); -Object.defineProperty(exports, "isNullabilityAssertionNode", ({ - enumerable: true, - get: function () { - return _predicates.isNullabilityAssertionNode; - } -})); Object.defineProperty(exports, "isSelectionNode", ({ enumerable: true, get: function () { @@ -28797,24 +27693,16 @@ exports.Kind = void 0; */ var Kind; (function (Kind) { - /** Name */ Kind['NAME'] = 'Name'; - /** Document */ Kind['DOCUMENT'] = 'Document'; Kind['OPERATION_DEFINITION'] = 'OperationDefinition'; Kind['VARIABLE_DEFINITION'] = 'VariableDefinition'; Kind['SELECTION_SET'] = 'SelectionSet'; Kind['FIELD'] = 'Field'; Kind['ARGUMENT'] = 'Argument'; - /** Nullability Modifiers */ - Kind['LIST_NULLABILITY_OPERATOR'] = 'ListNullabilityOperator'; - Kind['NON_NULL_ASSERTION'] = 'NonNullAssertion'; - Kind['ERROR_BOUNDARY'] = 'ErrorBoundary'; - /** Fragments */ Kind['FRAGMENT_SPREAD'] = 'FragmentSpread'; Kind['INLINE_FRAGMENT'] = 'InlineFragment'; Kind['FRAGMENT_DEFINITION'] = 'FragmentDefinition'; - /** Values */ Kind['VARIABLE'] = 'Variable'; Kind['INT'] = 'IntValue'; Kind['FLOAT'] = 'FloatValue'; @@ -28825,16 +27713,12 @@ var Kind; Kind['LIST'] = 'ListValue'; Kind['OBJECT'] = 'ObjectValue'; Kind['OBJECT_FIELD'] = 'ObjectField'; - /** Directives */ Kind['DIRECTIVE'] = 'Directive'; - /** Types */ Kind['NAMED_TYPE'] = 'NamedType'; Kind['LIST_TYPE'] = 'ListType'; Kind['NON_NULL_TYPE'] = 'NonNullType'; - /** Type System Definitions */ Kind['SCHEMA_DEFINITION'] = 'SchemaDefinition'; Kind['OPERATION_TYPE_DEFINITION'] = 'OperationTypeDefinition'; - /** Type Definitions */ Kind['SCALAR_TYPE_DEFINITION'] = 'ScalarTypeDefinition'; Kind['OBJECT_TYPE_DEFINITION'] = 'ObjectTypeDefinition'; Kind['FIELD_DEFINITION'] = 'FieldDefinition'; @@ -28844,11 +27728,8 @@ var Kind; Kind['ENUM_TYPE_DEFINITION'] = 'EnumTypeDefinition'; Kind['ENUM_VALUE_DEFINITION'] = 'EnumValueDefinition'; Kind['INPUT_OBJECT_TYPE_DEFINITION'] = 'InputObjectTypeDefinition'; - /** Directive Definitions */ Kind['DIRECTIVE_DEFINITION'] = 'DirectiveDefinition'; - /** Type System Extensions */ Kind['SCHEMA_EXTENSION'] = 'SchemaExtension'; - /** Type Extensions */ Kind['SCALAR_TYPE_EXTENSION'] = 'ScalarTypeExtension'; Kind['OBJECT_TYPE_EXTENSION'] = 'ObjectTypeExtension'; Kind['INTERFACE_TYPE_EXTENSION'] = 'InterfaceTypeExtension'; @@ -28857,6 +27738,12 @@ var Kind; Kind['INPUT_OBJECT_TYPE_EXTENSION'] = 'InputObjectTypeExtension'; })(Kind || (exports.Kind = Kind = {})); +/** + * The enum type representing the possible kind values of AST nodes. + * + * @deprecated Please use `Kind`. Will be remove in v17. + */ + /***/ }), /***/ "../../../node_modules/graphql/language/lexer.mjs": @@ -28885,7 +27772,23 @@ var _tokenKind = __webpack_require__(/*! ./tokenKind.mjs */ "../../../node_modul * EOF, after which the lexer will repeatedly return the same EOF token * whenever called. */ + class Lexer { + /** + * The previously focused non-ignored token. + */ + + /** + * The currently focused non-ignored token. + */ + + /** + * The (1-indexed) line containing the current token. + */ + + /** + * The character offset at which the current line begins. + */ constructor(source) { const startOfFileToken = new _ast.Token(_tokenKind.TokenKind.SOF, 0, 0, 0, 0); this.source = source; @@ -28900,6 +27803,7 @@ class Lexer { /** * Advances the token stream to the next non-ignored token. */ + advance() { this.lastToken = this.token; const token = this.token = this.lookahead(); @@ -28909,6 +27813,7 @@ class Lexer { * Looks ahead and returns the next non-ignored token, but does not change * the state of Lexer. */ + lookahead() { let token = this.token; if (token.kind !== _tokenKind.TokenKind.EOF) { @@ -28917,10 +27822,10 @@ class Lexer { token = token.next; } else { // Read the next token and form a link in the token linked-list. - const nextToken = readNextToken(this, token.end); - // @ts-expect-error next is only mutable during parsing. - token.next = nextToken; - // @ts-expect-error prev is only mutable during parsing. + const nextToken = readNextToken(this, token.end); // @ts-expect-error next is only mutable during parsing. + + token.next = nextToken; // @ts-expect-error prev is only mutable during parsing. + nextToken.prev = token; token = nextToken; } @@ -28934,7 +27839,7 @@ class Lexer { */ exports.Lexer = Lexer; function isPunctuatorTokenKind(kind) { - return kind === _tokenKind.TokenKind.BANG || kind === _tokenKind.TokenKind.QUESTION_MARK || kind === _tokenKind.TokenKind.DOLLAR || kind === _tokenKind.TokenKind.AMP || kind === _tokenKind.TokenKind.PAREN_L || kind === _tokenKind.TokenKind.PAREN_R || kind === _tokenKind.TokenKind.SPREAD || kind === _tokenKind.TokenKind.COLON || kind === _tokenKind.TokenKind.EQUALS || kind === _tokenKind.TokenKind.AT || kind === _tokenKind.TokenKind.BRACKET_L || kind === _tokenKind.TokenKind.BRACKET_R || kind === _tokenKind.TokenKind.BRACE_L || kind === _tokenKind.TokenKind.PIPE || kind === _tokenKind.TokenKind.BRACE_R; + return kind === _tokenKind.TokenKind.BANG || kind === _tokenKind.TokenKind.DOLLAR || kind === _tokenKind.TokenKind.AMP || kind === _tokenKind.TokenKind.PAREN_L || kind === _tokenKind.TokenKind.PAREN_R || kind === _tokenKind.TokenKind.SPREAD || kind === _tokenKind.TokenKind.COLON || kind === _tokenKind.TokenKind.EQUALS || kind === _tokenKind.TokenKind.AT || kind === _tokenKind.TokenKind.BRACKET_L || kind === _tokenKind.TokenKind.BRACKET_R || kind === _tokenKind.TokenKind.BRACE_L || kind === _tokenKind.TokenKind.PIPE || kind === _tokenKind.TokenKind.BRACE_R; } /** * A Unicode scalar value is any Unicode code point except surrogate code @@ -28944,6 +27849,7 @@ function isPunctuatorTokenKind(kind) { * SourceCharacter :: * - "Any Unicode scalar value" */ + function isUnicodeScalarValue(code) { return code >= 0x0000 && code <= 0xd7ff || code >= 0xe000 && code <= 0x10ffff; } @@ -28955,6 +27861,7 @@ function isUnicodeScalarValue(code) { * encodes a supplementary code point (above U+FFFF), but unpaired surrogate * code points are not valid source characters. */ + function isSupplementaryCodePoint(body, location) { return isLeadingSurrogate(body.charCodeAt(location)) && isTrailingSurrogate(body.charCodeAt(location + 1)); } @@ -28971,6 +27878,7 @@ function isTrailingSurrogate(code) { * Printable ASCII is printed quoted, while other points are printed in Unicode * code point form (ie. U+1234). */ + function printCodePointAt(lexer, location) { const code = lexer.source.body.codePointAt(location); if (code === undefined) { @@ -28979,13 +27887,14 @@ function printCodePointAt(lexer, location) { // Printable ASCII const char = String.fromCodePoint(code); return char === '"' ? "'\"'" : `"${char}"`; - } - // Unicode code point + } // Unicode code point + return 'U+' + code.toString(16).toUpperCase().padStart(4, '0'); } /** * Create a token with line and column location information. */ + function createToken(lexer, kind, start, end, value) { const line = lexer.line; const col = 1 + start - lexer.lineStart; @@ -28998,13 +27907,14 @@ function createToken(lexer, kind, start, end, value) { * punctuators immediately or calls the appropriate helper function for more * complicated tokens. */ + function readNextToken(lexer, start) { const body = lexer.source.body; const bodyLength = body.length; let position = start; while (position < bodyLength) { - const code = body.charCodeAt(position); - // SourceCharacter + const code = body.charCodeAt(position); // SourceCharacter + switch (code) { // Ignored :: // - UnicodeBOM @@ -29021,8 +27931,11 @@ function readNextToken(lexer, start) { // // Comma :: , case 0xfeff: // + case 0x0009: // \t + case 0x0020: // + case 0x002c: // , ++position; @@ -29031,6 +27944,7 @@ function readNextToken(lexer, start) { // - "New Line (U+000A)" // - "Carriage Return (U+000D)" [lookahead != "New Line (U+000A)"] // - "Carriage Return (U+000D)" "New Line (U+000A)" + case 0x000a: // \n ++position; @@ -29048,6 +27962,7 @@ function readNextToken(lexer, start) { lexer.lineStart = position; continue; // Comment + case 0x0023: // # return readComment(lexer, position); @@ -29059,6 +27974,7 @@ function readNextToken(lexer, start) { // - StringValue // // Punctuator :: one of ! $ & ( ) ... : = @ [ ] { | } + case 0x0021: // ! return createToken(lexer, _tokenKind.TokenKind.BANG, position, position + 1); @@ -29104,22 +28020,20 @@ function readNextToken(lexer, start) { case 0x007d: // } return createToken(lexer, _tokenKind.TokenKind.BRACE_R, position, position + 1); - case 0x003f: - // ? - return createToken(lexer, _tokenKind.TokenKind.QUESTION_MARK, position, position + 1); // StringValue + case 0x0022: // " if (body.charCodeAt(position + 1) === 0x0022 && body.charCodeAt(position + 2) === 0x0022) { return readBlockString(lexer, position); } return readString(lexer, position); - } - // IntValue | FloatValue (Digit | -) + } // IntValue | FloatValue (Digit | -) + if ((0, _characterClasses.isDigit)(code) || code === 0x002d) { return readNumber(lexer, position, code); - } - // Name + } // Name + if ((0, _characterClasses.isNameStart)(code)) { return readName(lexer, position); } @@ -29136,17 +28050,18 @@ function readNextToken(lexer, start) { * CommentChar :: SourceCharacter but not LineTerminator * ``` */ + function readComment(lexer, start) { const body = lexer.source.body; const bodyLength = body.length; let position = start + 1; while (position < bodyLength) { - const code = body.charCodeAt(position); - // LineTerminator (\n | \r) + const code = body.charCodeAt(position); // LineTerminator (\n | \r) + if (code === 0x000a || code === 0x000d) { break; - } - // SourceCharacter + } // SourceCharacter + if (isUnicodeScalarValue(code)) { ++position; } else if (isSupplementaryCodePoint(body, position)) { @@ -29186,16 +28101,17 @@ function readComment(lexer, start) { * Sign :: one of + - * ``` */ + function readNumber(lexer, start, firstCode) { const body = lexer.source.body; let position = start; let code = firstCode; - let isFloat = false; - // NegativeSign (-) + let isFloat = false; // NegativeSign (-) + if (code === 0x002d) { code = body.charCodeAt(++position); - } - // Zero (0) + } // Zero (0) + if (code === 0x0030) { code = body.charCodeAt(++position); if ((0, _characterClasses.isDigit)(code)) { @@ -29204,26 +28120,26 @@ function readNumber(lexer, start, firstCode) { } else { position = readDigits(lexer, position, code); code = body.charCodeAt(position); - } - // Full stop (.) + } // Full stop (.) + if (code === 0x002e) { isFloat = true; code = body.charCodeAt(++position); position = readDigits(lexer, position, code); code = body.charCodeAt(position); - } - // E e + } // E e + if (code === 0x0045 || code === 0x0065) { isFloat = true; - code = body.charCodeAt(++position); - // + - + code = body.charCodeAt(++position); // + - + if (code === 0x002b || code === 0x002d) { code = body.charCodeAt(++position); } position = readDigits(lexer, position, code); code = body.charCodeAt(position); - } - // Numbers cannot be followed by . or NameStart + } // Numbers cannot be followed by . or NameStart + if (code === 0x002e || (0, _characterClasses.isNameStart)(code)) { throw (0, _syntaxError.syntaxError)(lexer.source, position, `Invalid number, expected digit but got: ${printCodePointAt(lexer, position)}.`); } @@ -29232,12 +28148,14 @@ function readNumber(lexer, start, firstCode) { /** * Returns the new position in the source after reading one or more digits. */ + function readDigits(lexer, start, firstCode) { if (!(0, _characterClasses.isDigit)(firstCode)) { throw (0, _syntaxError.syntaxError)(lexer.source, start, `Invalid number, expected digit but got: ${printCodePointAt(lexer, start)}.`); } const body = lexer.source.body; let position = start + 1; // +1 to skip first firstCode + while ((0, _characterClasses.isDigit)(body.charCodeAt(position))) { ++position; } @@ -29263,6 +28181,7 @@ function readDigits(lexer, start, firstCode) { * EscapedCharacter :: one of `"` `\` `/` `b` `f` `n` `r` `t` * ``` */ + function readString(lexer, start) { const body = lexer.source.body; const bodyLength = body.length; @@ -29270,13 +28189,13 @@ function readString(lexer, start) { let chunkStart = position; let value = ''; while (position < bodyLength) { - const code = body.charCodeAt(position); - // Closing Quote (") + const code = body.charCodeAt(position); // Closing Quote (") + if (code === 0x0022) { value += body.slice(chunkStart, position); return createToken(lexer, _tokenKind.TokenKind.STRING, start, position + 1, value); - } - // Escape Sequence (\) + } // Escape Sequence (\) + if (code === 0x005c) { value += body.slice(chunkStart, position); const escape = body.charCodeAt(position + 1) === 0x0075 // u @@ -29286,12 +28205,12 @@ function readString(lexer, start) { position += escape.size; chunkStart = position; continue; - } - // LineTerminator (\n | \r) + } // LineTerminator (\n | \r) + if (code === 0x000a || code === 0x000d) { break; - } - // SourceCharacter + } // SourceCharacter + if (isUnicodeScalarValue(code)) { ++position; } else if (isSupplementaryCodePoint(body, position)) { @@ -29301,15 +28220,16 @@ function readString(lexer, start) { } } throw (0, _syntaxError.syntaxError)(lexer.source, position, 'Unterminated string.'); -} +} // The string value and lexed size of an escape sequence. + function readEscapedUnicodeVariableWidth(lexer, position) { const body = lexer.source.body; let point = 0; - let size = 3; - // Cannot be larger than 12 chars (\u{00000000}). + let size = 3; // Cannot be larger than 12 chars (\u{00000000}). + while (size < 12) { - const code = body.charCodeAt(position + size++); - // Closing Brace (}) + const code = body.charCodeAt(position + size++); // Closing Brace (}) + if (code === 0x007d) { // Must be at least 5 chars (\u{0}) and encode a Unicode scalar value. if (size < 5 || !isUnicodeScalarValue(point)) { @@ -29319,8 +28239,8 @@ function readEscapedUnicodeVariableWidth(lexer, position) { value: String.fromCodePoint(point), size }; - } - // Append this hex digit to the code point. + } // Append this hex digit to the code point. + point = point << 4 | readHexDigit(code); if (point < 0) { break; @@ -29336,9 +28256,9 @@ function readEscapedUnicodeFixedWidth(lexer, position) { value: String.fromCodePoint(code), size: 6 }; - } - // GraphQL allows JSON-style surrogate pair escape sequences, but only when + } // GraphQL allows JSON-style surrogate pair escape sequences, but only when // a valid pair is formed. + if (isLeadingSurrogate(code)) { // \u if (body.charCodeAt(position + 6) === 0x005c && body.charCodeAt(position + 7) === 0x0075) { @@ -29366,6 +28286,7 @@ function readEscapedUnicodeFixedWidth(lexer, position) { * * Returns a negative number if any char was not a valid hexadecimal digit. */ + function read16BitHexCode(body, position) { // readHexDigit() returns -1 on error. ORing a negative value with any other // value always produces a negative value. @@ -29385,6 +28306,7 @@ function read16BitHexCode(body, position) { * - `A` `B` `C` `D` `E` `F` * - `a` `b` `c` `d` `e` `f` */ + function readHexDigit(code) { return code >= 0x0030 && code <= 0x0039 // 0-9 ? code - 0x0030 : code >= 0x0041 && code <= 0x0046 // A-F @@ -29403,6 +28325,7 @@ function readHexDigit(code) { * | `r` | U+000D | carriage return | * | `t` | U+0009 | horizontal tab | */ + function readEscapedCharacter(lexer, position) { const body = lexer.source.body; const code = body.charCodeAt(position + 1); @@ -29470,6 +28393,7 @@ function readEscapedCharacter(lexer, position) { * - `\"""` * ``` */ + function readBlockString(lexer, start) { const body = lexer.source.body; const bodyLength = body.length; @@ -29479,8 +28403,8 @@ function readBlockString(lexer, start) { let currentLine = ''; const blockLines = []; while (position < bodyLength) { - const code = body.charCodeAt(position); - // Closing Triple-Quote (""") + const code = body.charCodeAt(position); // Closing Triple-Quote (""") + if (code === 0x0022 && body.charCodeAt(position + 1) === 0x0022 && body.charCodeAt(position + 2) === 0x0022) { currentLine += body.slice(chunkStart, position); blockLines.push(currentLine); @@ -29490,15 +28414,16 @@ function readBlockString(lexer, start) { lexer.line += blockLines.length - 1; lexer.lineStart = lineStart; return token; - } - // Escaped Triple-Quote (\""") + } // Escaped Triple-Quote (\""") + if (code === 0x005c && body.charCodeAt(position + 1) === 0x0022 && body.charCodeAt(position + 2) === 0x0022 && body.charCodeAt(position + 3) === 0x0022) { currentLine += body.slice(chunkStart, position); chunkStart = position + 1; // skip only slash + position += 4; continue; - } - // LineTerminator + } // LineTerminator + if (code === 0x000a || code === 0x000d) { currentLine += body.slice(chunkStart, position); blockLines.push(currentLine); @@ -29511,8 +28436,8 @@ function readBlockString(lexer, start) { chunkStart = position; lineStart = position; continue; - } - // SourceCharacter + } // SourceCharacter + if (isUnicodeScalarValue(code)) { ++position; } else if (isSupplementaryCodePoint(body, position)) { @@ -29531,6 +28456,7 @@ function readBlockString(lexer, start) { * - NameStart NameContinue* [lookahead != NameContinue] * ``` */ + function readName(lexer, start) { const body = lexer.source.body; const bodyLength = body.length; @@ -29562,6 +28488,10 @@ Object.defineProperty(exports, "__esModule", ({ exports.getLocation = getLocation; var _invariant = __webpack_require__(/*! ../jsutils/invariant.mjs */ "../../../node_modules/graphql/jsutils/invariant.mjs"); const LineRegExp = /\r\n|[\n\r]/g; +/** + * Represents a location in a Source. + */ + /** * Takes a Source and a UTF-8 character offset, and returns the corresponding * line and column as a SourceLocation. @@ -29608,6 +28538,10 @@ var _kinds = __webpack_require__(/*! ./kinds.mjs */ "../../../node_modules/graph var _lexer = __webpack_require__(/*! ./lexer.mjs */ "../../../node_modules/graphql/language/lexer.mjs"); var _source = __webpack_require__(/*! ./source.mjs */ "../../../node_modules/graphql/language/source.mjs"); var _tokenKind = __webpack_require__(/*! ./tokenKind.mjs */ "../../../node_modules/graphql/language/tokenKind.mjs"); +/** + * Configuration options to control parser behavior + */ + /** * Given a GraphQL source, parses it into a Document. * Throws GraphQLError if a syntax error is encountered. @@ -29626,6 +28560,7 @@ function parse(source, options) { * * Consider providing the results to the utility function: valueFromAST(). */ + function parseValue(source, options) { const parser = new Parser(source, options); parser.expectToken(_tokenKind.TokenKind.SOF); @@ -29637,6 +28572,7 @@ function parseValue(source, options) { * Similar to parseValue(), but raises a parse error if it encounters a * variable. The return type will be a constant value. */ + function parseConstValue(source, options) { const parser = new Parser(source, options); parser.expectToken(_tokenKind.TokenKind.SOF); @@ -29654,6 +28590,7 @@ function parseConstValue(source, options) { * * Consider providing the results to the utility function: typeFromAST(). */ + function parseType(source, options) { const parser = new Parser(source, options); parser.expectToken(_tokenKind.TokenKind.SOF); @@ -29672,6 +28609,7 @@ function parseType(source, options) { * * @internal */ + class Parser { constructor(source, options = {}) { const sourceObj = (0, _source.isSource)(source) ? source : new _source.Source(source); @@ -29682,17 +28620,19 @@ class Parser { /** * Converts a name lex token into a name parse node. */ + parseName() { const token = this.expectToken(_tokenKind.TokenKind.NAME); return this.node(token, { kind: _kinds.Kind.NAME, value: token.value }); - } - // Implements the parsing rules in the Document section. + } // Implements the parsing rules in the Document section. + /** * Document : Definition+ */ + parseDocument() { return this.node(this._lexer.token, { kind: _kinds.Kind.DOCUMENT, @@ -29722,11 +28662,12 @@ class Parser { * - EnumTypeDefinition * - InputObjectTypeDefinition */ + parseDefinition() { if (this.peek(_tokenKind.TokenKind.BRACE_L)) { return this.parseOperationDefinition(); - } - // Many definitions begin with a description and require a lookahead. + } // Many definitions begin with a description and require a lookahead. + const hasDescription = this.peekDescription(); const keywordToken = hasDescription ? this._lexer.lookahead() : this._lexer.token; if (keywordToken.kind === _tokenKind.TokenKind.NAME) { @@ -29763,13 +28704,14 @@ class Parser { } } throw this.unexpected(keywordToken); - } - // Implements the parsing rules in the Operations section. + } // Implements the parsing rules in the Operations section. + /** * OperationDefinition : * - SelectionSet * - OperationType Name? VariableDefinitions? Directives? SelectionSet */ + parseOperationDefinition() { const start = this._lexer.token; if (this.peek(_tokenKind.TokenKind.BRACE_L)) { @@ -29799,6 +28741,7 @@ class Parser { /** * OperationType : one of query mutation subscription */ + parseOperationType() { const operationToken = this.expectToken(_tokenKind.TokenKind.NAME); switch (operationToken.value) { @@ -29814,12 +28757,14 @@ class Parser { /** * VariableDefinitions : ( VariableDefinition+ ) */ + parseVariableDefinitions() { return this.optionalMany(_tokenKind.TokenKind.PAREN_L, this.parseVariableDefinition, _tokenKind.TokenKind.PAREN_R); } /** * VariableDefinition : Variable : Type DefaultValue? Directives[Const]? */ + parseVariableDefinition() { return this.node(this._lexer.token, { kind: _kinds.Kind.VARIABLE_DEFINITION, @@ -29832,6 +28777,7 @@ class Parser { /** * Variable : $ Name */ + parseVariable() { const start = this._lexer.token; this.expectToken(_tokenKind.TokenKind.DOLLAR); @@ -29845,6 +28791,7 @@ class Parser { * SelectionSet : { Selection+ } * ``` */ + parseSelectionSet() { return this.node(this._lexer.token, { kind: _kinds.Kind.SELECTION_SET, @@ -29857,6 +28804,7 @@ class Parser { * - FragmentSpread * - InlineFragment */ + parseSelection() { return this.peek(_tokenKind.TokenKind.SPREAD) ? this.parseFragment() : this.parseField(); } @@ -29865,6 +28813,7 @@ class Parser { * * Alias : Name : */ + parseField() { const start = this._lexer.token; const nameOrAlias = this.parseName(); @@ -29881,47 +28830,22 @@ class Parser { alias, name, arguments: this.parseArguments(false), - // Experimental support for Client Controlled Nullability changes - // the grammar of Field: - nullabilityAssertion: this.parseNullabilityAssertion(), directives: this.parseDirectives(false), selectionSet: this.peek(_tokenKind.TokenKind.BRACE_L) ? this.parseSelectionSet() : undefined }); } - // TODO: add grammar comment after it finalizes - parseNullabilityAssertion() { - // Note: Client Controlled Nullability is experimental and may be changed or - // removed in the future. - if (this._options.experimentalClientControlledNullability !== true) { - return undefined; - } - const start = this._lexer.token; - let nullabilityAssertion; - if (this.expectOptionalToken(_tokenKind.TokenKind.BRACKET_L)) { - const innerModifier = this.parseNullabilityAssertion(); - this.expectToken(_tokenKind.TokenKind.BRACKET_R); - nullabilityAssertion = this.node(start, { - kind: _kinds.Kind.LIST_NULLABILITY_OPERATOR, - nullabilityAssertion: innerModifier - }); - } - if (this.expectOptionalToken(_tokenKind.TokenKind.BANG)) { - nullabilityAssertion = this.node(start, { - kind: _kinds.Kind.NON_NULL_ASSERTION, - nullabilityAssertion - }); - } else if (this.expectOptionalToken(_tokenKind.TokenKind.QUESTION_MARK)) { - nullabilityAssertion = this.node(start, { - kind: _kinds.Kind.ERROR_BOUNDARY, - nullabilityAssertion - }); - } - return nullabilityAssertion; - } + /** + * Arguments[Const] : ( Argument[?Const]+ ) + */ + parseArguments(isConst) { const item = isConst ? this.parseConstArgument : this.parseArgument; return this.optionalMany(_tokenKind.TokenKind.PAREN_L, item, _tokenKind.TokenKind.PAREN_R); } + /** + * Argument[Const] : Name : Value[?Const] + */ + parseArgument(isConst = false) { const start = this._lexer.token; const name = this.parseName(); @@ -29934,8 +28858,8 @@ class Parser { } parseConstArgument() { return this.parseArgument(true); - } - // Implements the parsing rules in the Fragments section. + } // Implements the parsing rules in the Fragments section. + /** * Corresponds to both FragmentSpread and InlineFragment in the spec. * @@ -29943,6 +28867,7 @@ class Parser { * * InlineFragment : ... TypeCondition? Directives? SelectionSet */ + parseFragment() { const start = this._lexer.token; this.expectToken(_tokenKind.TokenKind.SPREAD); @@ -29967,12 +28892,13 @@ class Parser { * * TypeCondition : NamedType */ + parseFragmentDefinition() { const start = this._lexer.token; - this.expectKeyword('fragment'); - // Legacy support for defining variables within fragments changes + this.expectKeyword('fragment'); // Legacy support for defining variables within fragments changes // the grammar of FragmentDefinition: // - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet + if (this._options.allowLegacyFragmentVariables === true) { return this.node(start, { kind: _kinds.Kind.FRAGMENT_DEFINITION, @@ -29994,12 +28920,33 @@ class Parser { /** * FragmentName : Name but not `on` */ + parseFragmentName() { if (this._lexer.token.value === 'on') { throw this.unexpected(); } return this.parseName(); - } + } // Implements the parsing rules in the Values section. + + /** + * Value[Const] : + * - [~Const] Variable + * - IntValue + * - FloatValue + * - StringValue + * - BooleanValue + * - NullValue + * - EnumValue + * - ListValue[?Const] + * - ObjectValue[?Const] + * + * BooleanValue : one of `true` `false` + * + * NullValue : `null` + * + * EnumValue : Name but not `true`, `false` or `null` + */ + parseValueLiteral(isConst) { const token = this._lexer.token; switch (token.kind) { @@ -30072,6 +29019,12 @@ class Parser { block: token.kind === _tokenKind.TokenKind.BLOCK_STRING }); } + /** + * ListValue[Const] : + * - [ ] + * - [ Value[?Const]+ ] + */ + parseList(isConst) { const item = () => this.parseValueLiteral(isConst); return this.node(this._lexer.token, { @@ -30079,6 +29032,14 @@ class Parser { values: this.any(_tokenKind.TokenKind.BRACKET_L, item, _tokenKind.TokenKind.BRACKET_R) }); } + /** + * ``` + * ObjectValue[Const] : + * - { } + * - { ObjectField[?Const]+ } + * ``` + */ + parseObject(isConst) { const item = () => this.parseObjectField(isConst); return this.node(this._lexer.token, { @@ -30086,6 +29047,10 @@ class Parser { fields: this.any(_tokenKind.TokenKind.BRACE_L, item, _tokenKind.TokenKind.BRACE_R) }); } + /** + * ObjectField[Const] : Name : Value[?Const] + */ + parseObjectField(isConst) { const start = this._lexer.token; const name = this.parseName(); @@ -30095,7 +29060,12 @@ class Parser { name, value: this.parseValueLiteral(isConst) }); - } + } // Implements the parsing rules in the Directives section. + + /** + * Directives[Const] : Directive[?Const]+ + */ + parseDirectives(isConst) { const directives = []; while (this.peek(_tokenKind.TokenKind.AT)) { @@ -30106,6 +29076,12 @@ class Parser { parseConstDirectives() { return this.parseDirectives(true); } + /** + * ``` + * Directive[Const] : @ Name Arguments[?Const]? + * ``` + */ + parseDirective(isConst) { const start = this._lexer.token; this.expectToken(_tokenKind.TokenKind.AT); @@ -30114,14 +29090,15 @@ class Parser { name: this.parseName(), arguments: this.parseArguments(isConst) }); - } - // Implements the parsing rules in the Types section. + } // Implements the parsing rules in the Types section. + /** * Type : * - NamedType * - ListType * - NonNullType */ + parseTypeReference() { const start = this._lexer.token; let type; @@ -30146,19 +29123,21 @@ class Parser { /** * NamedType : Name */ + parseNamedType() { return this.node(this._lexer.token, { kind: _kinds.Kind.NAMED_TYPE, name: this.parseName() }); - } - // Implements the parsing rules in the Type Definition section. + } // Implements the parsing rules in the Type Definition section. + peekDescription() { return this.peek(_tokenKind.TokenKind.STRING) || this.peek(_tokenKind.TokenKind.BLOCK_STRING); } /** * Description : StringValue */ + parseDescription() { if (this.peekDescription()) { return this.parseStringLiteral(); @@ -30169,6 +29148,7 @@ class Parser { * SchemaDefinition : Description? schema Directives[Const]? { OperationTypeDefinition+ } * ``` */ + parseSchemaDefinition() { const start = this._lexer.token; const description = this.parseDescription(); @@ -30185,6 +29165,7 @@ class Parser { /** * OperationTypeDefinition : OperationType : NamedType */ + parseOperationTypeDefinition() { const start = this._lexer.token; const operation = this.parseOperationType(); @@ -30199,6 +29180,7 @@ class Parser { /** * ScalarTypeDefinition : Description? scalar Name Directives[Const]? */ + parseScalarTypeDefinition() { const start = this._lexer.token; const description = this.parseDescription(); @@ -30217,6 +29199,7 @@ class Parser { * Description? * type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition? */ + parseObjectTypeDefinition() { const start = this._lexer.token; const description = this.parseDescription(); @@ -30239,6 +29222,7 @@ class Parser { * - implements `&`? NamedType * - ImplementsInterfaces & NamedType */ + parseImplementsInterfaces() { return this.expectOptionalKeyword('implements') ? this.delimitedMany(_tokenKind.TokenKind.AMP, this.parseNamedType) : []; } @@ -30247,6 +29231,7 @@ class Parser { * FieldsDefinition : { FieldDefinition+ } * ``` */ + parseFieldsDefinition() { return this.optionalMany(_tokenKind.TokenKind.BRACE_L, this.parseFieldDefinition, _tokenKind.TokenKind.BRACE_R); } @@ -30254,6 +29239,7 @@ class Parser { * FieldDefinition : * - Description? Name ArgumentsDefinition? : Type Directives[Const]? */ + parseFieldDefinition() { const start = this._lexer.token; const description = this.parseDescription(); @@ -30274,6 +29260,7 @@ class Parser { /** * ArgumentsDefinition : ( InputValueDefinition+ ) */ + parseArgumentDefs() { return this.optionalMany(_tokenKind.TokenKind.PAREN_L, this.parseInputValueDef, _tokenKind.TokenKind.PAREN_R); } @@ -30281,6 +29268,7 @@ class Parser { * InputValueDefinition : * - Description? Name : Type DefaultValue? Directives[Const]? */ + parseInputValueDef() { const start = this._lexer.token; const description = this.parseDescription(); @@ -30305,6 +29293,7 @@ class Parser { * InterfaceTypeDefinition : * - Description? interface Name Directives[Const]? FieldsDefinition? */ + parseInterfaceTypeDefinition() { const start = this._lexer.token; const description = this.parseDescription(); @@ -30326,6 +29315,7 @@ class Parser { * UnionTypeDefinition : * - Description? union Name Directives[Const]? UnionMemberTypes? */ + parseUnionTypeDefinition() { const start = this._lexer.token; const description = this.parseDescription(); @@ -30346,6 +29336,7 @@ class Parser { * - = `|`? NamedType * - UnionMemberTypes | NamedType */ + parseUnionMemberTypes() { return this.expectOptionalToken(_tokenKind.TokenKind.EQUALS) ? this.delimitedMany(_tokenKind.TokenKind.PIPE, this.parseNamedType) : []; } @@ -30353,6 +29344,7 @@ class Parser { * EnumTypeDefinition : * - Description? enum Name Directives[Const]? EnumValuesDefinition? */ + parseEnumTypeDefinition() { const start = this._lexer.token; const description = this.parseDescription(); @@ -30373,12 +29365,14 @@ class Parser { * EnumValuesDefinition : { EnumValueDefinition+ } * ``` */ + parseEnumValuesDefinition() { return this.optionalMany(_tokenKind.TokenKind.BRACE_L, this.parseEnumValueDefinition, _tokenKind.TokenKind.BRACE_R); } /** * EnumValueDefinition : Description? EnumValue Directives[Const]? */ + parseEnumValueDefinition() { const start = this._lexer.token; const description = this.parseDescription(); @@ -30394,6 +29388,7 @@ class Parser { /** * EnumValue : Name but not `true`, `false` or `null` */ + parseEnumValueName() { if (this._lexer.token.value === 'true' || this._lexer.token.value === 'false' || this._lexer.token.value === 'null') { throw (0, _syntaxError.syntaxError)(this._lexer.source, this._lexer.token.start, `${getTokenDesc(this._lexer.token)} is reserved and cannot be used for an enum value.`); @@ -30404,6 +29399,7 @@ class Parser { * InputObjectTypeDefinition : * - Description? input Name Directives[Const]? InputFieldsDefinition? */ + parseInputObjectTypeDefinition() { const start = this._lexer.token; const description = this.parseDescription(); @@ -30424,6 +29420,7 @@ class Parser { * InputFieldsDefinition : { InputValueDefinition+ } * ``` */ + parseInputFieldsDefinition() { return this.optionalMany(_tokenKind.TokenKind.BRACE_L, this.parseInputValueDef, _tokenKind.TokenKind.BRACE_R); } @@ -30440,6 +29437,7 @@ class Parser { * - EnumTypeExtension * - InputObjectTypeDefinition */ + parseTypeSystemExtension() { const keywordToken = this._lexer.lookahead(); if (keywordToken.kind === _tokenKind.TokenKind.NAME) { @@ -30469,6 +29467,7 @@ class Parser { * - extend schema Directives[Const] * ``` */ + parseSchemaExtension() { const start = this._lexer.token; this.expectKeyword('extend'); @@ -30488,6 +29487,7 @@ class Parser { * ScalarTypeExtension : * - extend scalar Name Directives[Const] */ + parseScalarTypeExtension() { const start = this._lexer.token; this.expectKeyword('extend'); @@ -30509,6 +29509,7 @@ class Parser { * - extend type Name ImplementsInterfaces? Directives[Const] * - extend type Name ImplementsInterfaces */ + parseObjectTypeExtension() { const start = this._lexer.token; this.expectKeyword('extend'); @@ -30534,6 +29535,7 @@ class Parser { * - extend interface Name ImplementsInterfaces? Directives[Const] * - extend interface Name ImplementsInterfaces */ + parseInterfaceTypeExtension() { const start = this._lexer.token; this.expectKeyword('extend'); @@ -30558,6 +29560,7 @@ class Parser { * - extend union Name Directives[Const]? UnionMemberTypes * - extend union Name Directives[Const] */ + parseUnionTypeExtension() { const start = this._lexer.token; this.expectKeyword('extend'); @@ -30580,6 +29583,7 @@ class Parser { * - extend enum Name Directives[Const]? EnumValuesDefinition * - extend enum Name Directives[Const] */ + parseEnumTypeExtension() { const start = this._lexer.token; this.expectKeyword('extend'); @@ -30602,6 +29606,7 @@ class Parser { * - extend input Name Directives[Const]? InputFieldsDefinition * - extend input Name Directives[Const] */ + parseInputObjectTypeExtension() { const start = this._lexer.token; this.expectKeyword('extend'); @@ -30625,6 +29630,7 @@ class Parser { * - Description? directive @ Name ArgumentsDefinition? `repeatable`? on DirectiveLocations * ``` */ + parseDirectiveDefinition() { const start = this._lexer.token; const description = this.parseDescription(); @@ -30649,6 +29655,7 @@ class Parser { * - `|`? DirectiveLocation * - DirectiveLocations | DirectiveLocation */ + parseDirectiveLocations() { return this.delimitedMany(_tokenKind.TokenKind.PIPE, this.parseDirectiveLocation); } @@ -30679,20 +29686,22 @@ class Parser { * `INPUT_OBJECT` * `INPUT_FIELD_DEFINITION` */ + parseDirectiveLocation() { const start = this._lexer.token; const name = this.parseName(); - if (Object.hasOwn(_directiveLocation.DirectiveLocation, name.value)) { + if (Object.prototype.hasOwnProperty.call(_directiveLocation.DirectiveLocation, name.value)) { return name; } throw this.unexpected(start); - } - // Core parsing utility functions + } // Core parsing utility functions + /** * Returns a node that, if configured to do so, sets a "loc" field as a * location object, used to identify the place in the source that created a * given parsed object. */ + node(startToken, node) { if (this._options.noLocation !== true) { node.loc = new _ast.Location(startToken, this._lexer.lastToken, this._lexer.source); @@ -30702,6 +29711,7 @@ class Parser { /** * Determines if the next token is of a given kind */ + peek(kind) { return this._lexer.token.kind === kind; } @@ -30709,6 +29719,7 @@ class Parser { * If the next token is of the given kind, return that token after advancing the lexer. * Otherwise, do not change the parser state and throw an error. */ + expectToken(kind) { const token = this._lexer.token; if (token.kind === kind) { @@ -30721,6 +29732,7 @@ class Parser { * If the next token is of the given kind, return "true" after advancing the lexer. * Otherwise, do not change the parser state and return "false". */ + expectOptionalToken(kind) { const token = this._lexer.token; if (token.kind === kind) { @@ -30733,6 +29745,7 @@ class Parser { * If the next token is a given keyword, advance the lexer. * Otherwise, do not change the parser state and throw an error. */ + expectKeyword(value) { const token = this._lexer.token; if (token.kind === _tokenKind.TokenKind.NAME && token.value === value) { @@ -30745,6 +29758,7 @@ class Parser { * If the next token is a given keyword, return "true" after advancing the lexer. * Otherwise, do not change the parser state and return "false". */ + expectOptionalKeyword(value) { const token = this._lexer.token; if (token.kind === _tokenKind.TokenKind.NAME && token.value === value) { @@ -30756,6 +29770,7 @@ class Parser { /** * Helper function for creating an error when an unexpected lexed token is encountered. */ + unexpected(atToken) { const token = atToken !== null && atToken !== void 0 ? atToken : this._lexer.token; return (0, _syntaxError.syntaxError)(this._lexer.source, token.start, `Unexpected ${getTokenDesc(token)}.`); @@ -30765,6 +29780,7 @@ class Parser { * This list begins with a lex token of openKind and ends with a lex token of closeKind. * Advances the parser to the next lex token after the closing token. */ + any(openKind, parseFn, closeKind) { this.expectToken(openKind); const nodes = []; @@ -30779,6 +29795,7 @@ class Parser { * that begins with a lex token of openKind and ends with a lex token of closeKind. * Advances the parser to the next lex token after the closing token. */ + optionalMany(openKind, parseFn, closeKind) { if (this.expectOptionalToken(openKind)) { const nodes = []; @@ -30794,6 +29811,7 @@ class Parser { * This list begins with a lex token of openKind and ends with a lex token of closeKind. * Advances the parser to the next lex token after the closing token. */ + many(openKind, parseFn, closeKind) { this.expectToken(openKind); const nodes = []; @@ -30807,6 +29825,7 @@ class Parser { * This list may begin with a lex token of delimiterKind followed by items separated by lex tokens of tokenKind. * Advances the parser to the next lex token after last item in the list. */ + delimitedMany(delimiterKind, parseFn) { this.expectOptionalToken(delimiterKind); const nodes = []; @@ -30823,7 +29842,7 @@ class Parser { if (maxTokens !== undefined && token.kind !== _tokenKind.TokenKind.EOF) { ++this._tokenCounter; if (this._tokenCounter > maxTokens) { - throw (0, _syntaxError.syntaxError)(this._lexer.source, token.start, `Document contains more than ${maxTokens} tokens. Parsing aborted.`); + throw (0, _syntaxError.syntaxError)(this._lexer.source, token.start, `Document contains more that ${maxTokens} tokens. Parsing aborted.`); } } } @@ -30839,6 +29858,7 @@ function getTokenDesc(token) { /** * A helper function to describe a token kind as a string for debugging. */ + function getTokenKindDesc(kind) { return (0, _lexer.isPunctuatorTokenKind)(kind) ? `"${kind}"` : kind; } @@ -30859,7 +29879,6 @@ Object.defineProperty(exports, "__esModule", ({ exports.isConstValueNode = isConstValueNode; exports.isDefinitionNode = isDefinitionNode; exports.isExecutableDefinitionNode = isExecutableDefinitionNode; -exports.isNullabilityAssertionNode = isNullabilityAssertionNode; exports.isSelectionNode = isSelectionNode; exports.isTypeDefinitionNode = isTypeDefinitionNode; exports.isTypeExtensionNode = isTypeExtensionNode; @@ -30877,9 +29896,6 @@ function isExecutableDefinitionNode(node) { function isSelectionNode(node) { return node.kind === _kinds.Kind.FIELD || node.kind === _kinds.Kind.FRAGMENT_SPREAD || node.kind === _kinds.Kind.INLINE_FRAGMENT; } -function isNullabilityAssertionNode(node) { - return node.kind === _kinds.Kind.LIST_NULLABILITY_OPERATOR || node.kind === _kinds.Kind.NON_NULL_ASSERTION || node.kind === _kinds.Kind.ERROR_BOUNDARY; -} function isValueNode(node) { return node.kind === _kinds.Kind.VARIABLE || node.kind === _kinds.Kind.INT || node.kind === _kinds.Kind.FLOAT || node.kind === _kinds.Kind.STRING || node.kind === _kinds.Kind.BOOLEAN || node.kind === _kinds.Kind.NULL || node.kind === _kinds.Kind.ENUM || node.kind === _kinds.Kind.LIST || node.kind === _kinds.Kind.OBJECT; } @@ -30927,6 +29943,7 @@ function printLocation(location) { /** * Render a helpful description of the location in the GraphQL Source document. */ + function printSourceLocation(source, sourceLocation) { const firstLineColumnOffset = source.locationOffset.column - 1; const body = ''.padStart(firstLineColumnOffset) + source.body; @@ -30937,8 +29954,8 @@ function printSourceLocation(source, sourceLocation) { const columnNum = sourceLocation.column + columnOffset; const locationStr = `${source.name}:${lineNum}:${columnNum}\n`; const lines = body.split(/\r\n|[\n\r]/g); - const locationLine = lines[lineIndex]; - // Special case for minified documents + const locationLine = lines[lineIndex]; // Special case for minified documents + if (locationLine.length > 120) { const subLineIndex = Math.floor(columnNum / 80); const subLineColumnNum = columnNum % 80; @@ -30978,14 +29995,24 @@ exports.printString = printString; */ function printString(str) { return `"${str.replace(escapedRegExp, escapedReplacer)}"`; -} -// eslint-disable-next-line no-control-regex +} // eslint-disable-next-line no-control-regex + const escapedRegExp = /[\x00-\x1f\x22\x5c\x7f-\x9f]/g; function escapedReplacer(str) { return escapeSequences[str.charCodeAt(0)]; -} -// prettier-ignore -const escapeSequences = ['\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004', '\\u0005', '\\u0006', '\\u0007', '\\b', '\\t', '\\n', '\\u000B', '\\f', '\\r', '\\u000E', '\\u000F', '\\u0010', '\\u0011', '\\u0012', '\\u0013', '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018', '\\u0019', '\\u001A', '\\u001B', '\\u001C', '\\u001D', '\\u001E', '\\u001F', '', '', '\\"', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '\\\\', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '\\u007F', '\\u0080', '\\u0081', '\\u0082', '\\u0083', '\\u0084', '\\u0085', '\\u0086', '\\u0087', '\\u0088', '\\u0089', '\\u008A', '\\u008B', '\\u008C', '\\u008D', '\\u008E', '\\u008F', '\\u0090', '\\u0091', '\\u0092', '\\u0093', '\\u0094', '\\u0095', '\\u0096', '\\u0097', '\\u0098', '\\u0099', '\\u009A', '\\u009B', '\\u009C', '\\u009D', '\\u009E', '\\u009F']; +} // prettier-ignore + +const escapeSequences = ['\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004', '\\u0005', '\\u0006', '\\u0007', '\\b', '\\t', '\\n', '\\u000B', '\\f', '\\r', '\\u000E', '\\u000F', '\\u0010', '\\u0011', '\\u0012', '\\u0013', '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018', '\\u0019', '\\u001A', '\\u001B', '\\u001C', '\\u001D', '\\u001E', '\\u001F', '', '', '\\"', '', '', '', '', '', '', '', '', '', '', '', '', '', +// 2F +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +// 3F +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +// 4F +'', '', '', '', '', '', '', '', '', '', '', '', '\\\\', '', '', '', +// 5F +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +// 6F +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '\\u007F', '\\u0080', '\\u0081', '\\u0082', '\\u0083', '\\u0084', '\\u0085', '\\u0086', '\\u0087', '\\u0088', '\\u0089', '\\u008A', '\\u008B', '\\u008C', '\\u008D', '\\u008E', '\\u008F', '\\u0090', '\\u0091', '\\u0092', '\\u0093', '\\u0094', '\\u0095', '\\u0096', '\\u0097', '\\u0098', '\\u0099', '\\u009A', '\\u009B', '\\u009C', '\\u009D', '\\u009E', '\\u009F']; /***/ }), @@ -31008,6 +30035,7 @@ var _visitor = __webpack_require__(/*! ./visitor.mjs */ "../../../node_modules/g * Converts an AST into a string, using one set of reasonable * formatting rules. */ + function print(ast) { return (0, _visitor.visit)(ast, printDocASTReducer); } @@ -31026,9 +30054,9 @@ const printDocASTReducer = { OperationDefinition: { leave(node) { const varDefs = wrap('(', join(node.variableDefinitions, ', '), ')'); - const prefix = join([node.operation, join([node.name, varDefs]), join(node.directives, ' ')], ' '); - // Anonymous queries with no directives or variable definitions can use + const prefix = join([node.operation, join([node.name, varDefs]), join(node.directives, ' ')], ' '); // Anonymous queries with no directives or variable definitions can use // the query short form. + return (prefix === 'query' ? '' : prefix + ' ') + node.selectionSet; } }, @@ -31050,19 +30078,15 @@ const printDocASTReducer = { alias, name, arguments: args, - nullabilityAssertion, directives, selectionSet }) { - const prefix = join([wrap('', alias, ': '), name], ''); + const prefix = wrap('', alias, ': ') + name; let argsLine = prefix + wrap('(', join(args, ', '), ')'); if (argsLine.length > MAX_LINE_LENGTH) { argsLine = prefix + wrap('(\n', indent(join(args, '\n')), '\n)'); } - return join([argsLine, - // Note: Client Controlled Nullability is experimental and may be - // changed or removed in the future. - nullabilityAssertion, wrap(' ', join(directives, ' ')), wrap(' ', selectionSet)]); + return join([argsLine, join(directives, ' '), selectionSet], ' '); } }, Argument: { @@ -31071,28 +30095,6 @@ const printDocASTReducer = { value }) => name + ': ' + value }, - // Nullability Modifiers - ListNullabilityOperator: { - leave({ - nullabilityAssertion - }) { - return join(['[', nullabilityAssertion, ']']); - } - }, - NonNullAssertion: { - leave({ - nullabilityAssertion - }) { - return join([nullabilityAssertion, '!']); - } - }, - ErrorBoundary: { - leave({ - nullabilityAssertion - }) { - return join([nullabilityAssertion, '?']); - } - }, // Fragments FragmentSpread: { leave: ({ @@ -31114,8 +30116,8 @@ const printDocASTReducer = { variableDefinitions, directives, selectionSet - }) => - // Note: fragment variable definitions are experimental and may be changed + } // Note: fragment variable definitions are experimental and may be changed + ) => // or removed in the future. `fragment ${name}${wrap('(', join(variableDefinitions, ', '), ')')} ` + `on ${typeCondition} ${wrap('', join(directives, ' '), ' ')}` + selectionSet }, @@ -31134,7 +30136,7 @@ const printDocASTReducer = { leave: ({ value, block: isBlockString - }) => isBlockString === true ? (0, _blockString.printBlockString)(value) : (0, _printString.printString)(value) + }) => isBlockString ? (0, _blockString.printBlockString)(value) : (0, _printString.printString)(value) }, BooleanValue: { leave: ({ @@ -31152,21 +30154,12 @@ const printDocASTReducer = { ListValue: { leave: ({ values - }) => { - const valuesLine = '[' + join(values, ', ') + ']'; - if (valuesLine.length > MAX_LINE_LENGTH) { - return '[\n' + indent(join(values, '\n')) + '\n]'; - } - return valuesLine; - } + }) => '[' + join(values, ', ') + ']' }, ObjectValue: { leave: ({ fields - }) => { - const fieldsLine = '{ ' + join(fields, ', ') + ' }'; - return fieldsLine.length > MAX_LINE_LENGTH ? block(fields) : fieldsLine; - } + }) => '{' + join(fields, ', ') + '}' }, ObjectField: { leave: ({ @@ -31348,6 +30341,7 @@ const printDocASTReducer = { * Given maybeArray, print an empty string if it is null or empty, otherwise * print all items together separated by separator if provided */ + function join(maybeArray, separator = '') { var _maybeArray$filter$jo; return (_maybeArray$filter$jo = maybeArray === null || maybeArray === void 0 ? void 0 : maybeArray.filter(x => x).join(separator)) !== null && _maybeArray$filter$jo !== void 0 ? _maybeArray$filter$jo : ''; @@ -31355,21 +30349,25 @@ function join(maybeArray, separator = '') { /** * Given array, print each item on its own line, wrapped in an indented `{ }` block. */ + function block(array) { return wrap('{\n', indent(join(array, '\n')), '\n}'); } /** * If maybeString is not null or empty, then wrap with start and end, otherwise print an empty string. */ + function wrap(start, maybeString, end = '') { return maybeString != null && maybeString !== '' ? start + maybeString + end : ''; } function indent(str) { - return wrap(' ', str.replaceAll('\n', '\n ')); + return wrap(' ', str.replace(/\n/g, '\n ')); } function hasMultilineItems(maybeArray) { var _maybeArray$some; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ return (_maybeArray$some = maybeArray === null || maybeArray === void 0 ? void 0 : maybeArray.some(str => str.includes('\n'))) !== null && _maybeArray$some !== void 0 ? _maybeArray$some : false; } @@ -31390,6 +30388,7 @@ Object.defineProperty(exports, "__esModule", ({ exports.Source = void 0; exports.isSource = isSource; var _devAssert = __webpack_require__(/*! ../jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); +var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); var _instanceOf = __webpack_require__(/*! ../jsutils/instanceOf.mjs */ "../../../node_modules/graphql/jsutils/instanceOf.mjs"); /** * A representation of source input to GraphQL. The `name` and `locationOffset` parameters are @@ -31403,6 +30402,7 @@ class Source { line: 1, column: 1 }) { + typeof body === 'string' || (0, _devAssert.devAssert)(false, `Body must be a string. Received: ${(0, _inspect.inspect)(body)}.`); this.body = body; this.name = name; this.locationOffset = locationOffset; @@ -31446,7 +30446,6 @@ var TokenKind; TokenKind['SOF'] = ''; TokenKind['EOF'] = ''; TokenKind['BANG'] = '!'; - TokenKind['QUESTION_MARK'] = '?'; TokenKind['DOLLAR'] = '$'; TokenKind['AMP'] = '&'; TokenKind['PAREN_L'] = '('; @@ -31468,6 +30467,12 @@ var TokenKind; TokenKind['COMMENT'] = 'Comment'; })(TokenKind || (exports.TokenKind = TokenKind = {})); +/** + * The enum type representing the token kinds values. + * + * @deprecated Please use `TokenKind`. Will be remove in v17. + */ + /***/ }), /***/ "../../../node_modules/graphql/language/visitor.mjs": @@ -31483,19 +30488,105 @@ Object.defineProperty(exports, "__esModule", ({ })); exports.BREAK = void 0; exports.getEnterLeaveForKind = getEnterLeaveForKind; +exports.getVisitFn = getVisitFn; exports.visit = visit; exports.visitInParallel = visitInParallel; var _devAssert = __webpack_require__(/*! ../jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); var _ast = __webpack_require__(/*! ./ast.mjs */ "../../../node_modules/graphql/language/ast.mjs"); var _kinds = __webpack_require__(/*! ./kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); +/** + * A visitor is provided to visit, it contains the collection of + * relevant functions to be called during the visitor's traversal. + */ + const BREAK = exports.BREAK = Object.freeze({}); +/** + * visit() will walk through an AST using a depth-first traversal, calling + * the visitor's enter function at each node in the traversal, and calling the + * leave function after visiting that node and all of its child nodes. + * + * By returning different values from the enter and leave functions, the + * behavior of the visitor can be altered, including skipping over a sub-tree of + * the AST (by returning false), editing the AST by returning a value or null + * to remove the value, or to stop the whole traversal by returning BREAK. + * + * When using visit() to edit an AST, the original AST will not be modified, and + * a new version of the AST with the changes applied will be returned from the + * visit function. + * + * ```ts + * const editedAST = visit(ast, { + * enter(node, key, parent, path, ancestors) { + * // @return + * // undefined: no action + * // false: skip visiting this node + * // visitor.BREAK: stop visiting altogether + * // null: delete this node + * // any value: replace this node with the returned value + * }, + * leave(node, key, parent, path, ancestors) { + * // @return + * // undefined: no action + * // false: no action + * // visitor.BREAK: stop visiting altogether + * // null: delete this node + * // any value: replace this node with the returned value + * } + * }); + * ``` + * + * Alternatively to providing enter() and leave() functions, a visitor can + * instead provide functions named the same as the kinds of AST nodes, or + * enter/leave visitors at a named key, leading to three permutations of the + * visitor API: + * + * 1) Named visitors triggered when entering a node of a specific kind. + * + * ```ts + * visit(ast, { + * Kind(node) { + * // enter the "Kind" node + * } + * }) + * ``` + * + * 2) Named visitors that trigger upon entering and leaving a node of a specific kind. + * + * ```ts + * visit(ast, { + * Kind: { + * enter(node) { + * // enter the "Kind" node + * } + * leave(node) { + * // leave the "Kind" node + * } + * } + * }) + * ``` + * + * 3) Generic visitors that trigger upon entering and leaving any node. + * + * ```ts + * visit(ast, { + * enter(node) { + * // enter any node + * }, + * leave(node) { + * // leave any node + * } + * }) + * ``` + */ + function visit(root, visitor, visitorKeys = _ast.QueryDocumentKeys) { const enterLeaveMap = new Map(); for (const kind of Object.values(_kinds.Kind)) { enterLeaveMap.set(kind, getEnterLeaveForKind(visitor, kind)); } /* eslint-disable no-undef-init */ + let stack = undefined; let inArray = Array.isArray(root); let keys = [root]; @@ -31507,6 +30598,7 @@ function visit(root, visitor, visitorKeys = _ast.QueryDocumentKeys) { const path = []; const ancestors = []; /* eslint-enable no-undef-init */ + do { index++; const isLeaving = index === keys.length; @@ -31540,7 +30632,7 @@ function visit(root, visitor, visitorKeys = _ast.QueryDocumentKeys) { edits = stack.edits; inArray = stack.inArray; stack = stack.prev; - } else if (parent != null) { + } else if (parent) { key = inArray ? index : keys[index]; node = parent[key]; if (node === null || node === undefined) { @@ -31580,7 +30672,7 @@ function visit(root, visitor, visitorKeys = _ast.QueryDocumentKeys) { if (isLeaving) { path.pop(); } else { - var _visitorKeys$node$kin; + var _node$kind; stack = { inArray, index, @@ -31589,10 +30681,10 @@ function visit(root, visitor, visitorKeys = _ast.QueryDocumentKeys) { prev: stack }; inArray = Array.isArray(node); - keys = inArray ? node : (_visitorKeys$node$kin = visitorKeys[node.kind]) !== null && _visitorKeys$node$kin !== void 0 ? _visitorKeys$node$kin : []; + keys = inArray ? node : (_node$kind = visitorKeys[node.kind]) !== null && _node$kind !== void 0 ? _node$kind : []; index = -1; edits = []; - if (parent != null) { + if (parent) { ancestors.push(parent); } parent = node; @@ -31600,7 +30692,7 @@ function visit(root, visitor, visitorKeys = _ast.QueryDocumentKeys) { } while (stack !== undefined); if (edits.length !== 0) { // New root - return edits.at(-1)[1]; + return edits[edits.length - 1][1]; } return root; } @@ -31610,6 +30702,7 @@ function visit(root, visitor, visitorKeys = _ast.QueryDocumentKeys) { * * If a prior visitor edits a node, no following visitors will see that node. */ + function visitInParallel(visitors) { const skipping = new Array(visitors.length).fill(null); const mergedVisitor = Object.create(null); @@ -31622,7 +30715,7 @@ function visitInParallel(visitors) { enter, leave } = getEnterLeaveForKind(visitors[i], kind); - hasVisitor ||= enter != null || leave != null; + hasVisitor || (hasVisitor = enter != null || leave != null); enterList[i] = enter; leaveList[i] = leave; } @@ -31670,6 +30763,7 @@ function visitInParallel(visitors) { /** * Given a visitor instance and a node kind, return EnterLeaveVisitor for that kind. */ + function getEnterLeaveForKind(visitor, kind) { const kindVisitor = visitor[kind]; if (typeof kindVisitor === 'object') { @@ -31681,13 +30775,29 @@ function getEnterLeaveForKind(visitor, kind) { enter: kindVisitor, leave: undefined }; - } - // { enter() {}, leave() {} } + } // { enter() {}, leave() {} } + return { enter: visitor.enter, leave: visitor.leave }; } +/** + * Given a visitor instance, if it is leaving or not, and a node kind, return + * the function the visitor runtime should call. + * + * @deprecated Please use `getEnterLeaveForKind` instead. Will be removed in v17 + */ + +/* c8 ignore next 8 */ + +function getVisitFn(visitor, kind, isLeaving) { + const { + enter, + leave + } = getEnterLeaveForKind(visitor, kind); + return isLeaving ? leave : enter; +} /***/ }), @@ -31704,12 +30814,16 @@ Object.defineProperty(exports, "__esModule", ({ })); exports.assertEnumValueName = assertEnumValueName; exports.assertName = assertName; +var _devAssert = __webpack_require__(/*! ../jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); var _GraphQLError = __webpack_require__(/*! ../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); var _characterClasses = __webpack_require__(/*! ../language/characterClasses.mjs */ "../../../node_modules/graphql/language/characterClasses.mjs"); /** * Upholds the spec rules about naming. */ + function assertName(name) { + name != null || (0, _devAssert.devAssert)(false, 'Must provide name.'); + typeof name === 'string' || (0, _devAssert.devAssert)(false, 'Expected name to be a string.'); if (name.length === 0) { throw new _GraphQLError.GraphQLError('Expected name to be a non-empty string.'); } @@ -31728,6 +30842,7 @@ function assertName(name) { * * @internal */ + function assertEnumValueName(name) { if (name === 'true' || name === 'false' || name === 'null') { throw new _GraphQLError.GraphQLError(`Enum values cannot be named: ${name}`); @@ -31796,6 +30911,7 @@ var _didYouMean = __webpack_require__(/*! ../jsutils/didYouMean.mjs */ "../../.. var _identityFunc = __webpack_require__(/*! ../jsutils/identityFunc.mjs */ "../../../node_modules/graphql/jsutils/identityFunc.mjs"); var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); var _instanceOf = __webpack_require__(/*! ../jsutils/instanceOf.mjs */ "../../../node_modules/graphql/jsutils/instanceOf.mjs"); +var _isObjectLike = __webpack_require__(/*! ../jsutils/isObjectLike.mjs */ "../../../node_modules/graphql/jsutils/isObjectLike.mjs"); var _keyMap = __webpack_require__(/*! ../jsutils/keyMap.mjs */ "../../../node_modules/graphql/jsutils/keyMap.mjs"); var _keyValMap = __webpack_require__(/*! ../jsutils/keyValMap.mjs */ "../../../node_modules/graphql/jsutils/keyValMap.mjs"); var _mapValue = __webpack_require__(/*! ../jsutils/mapValue.mjs */ "../../../node_modules/graphql/jsutils/mapValue.mjs"); @@ -31818,6 +30934,7 @@ function assertType(type) { /** * There are predicates for each kind of GraphQL type. */ + function isScalarType(type) { return (0, _instanceOf.instanceOf)(type, GraphQLScalarType); } @@ -31890,6 +31007,10 @@ function assertNonNullType(type) { } return type; } +/** + * These types may be used as input types for arguments and directives. + */ + function isInputType(type) { return isScalarType(type) || isEnumType(type) || isInputObjectType(type) || isWrappingType(type) && isInputType(type.ofType); } @@ -31899,6 +31020,10 @@ function assertInputType(type) { } return type; } +/** + * These types may be used as output types as the result of fields. + */ + function isOutputType(type) { return isScalarType(type) || isObjectType(type) || isInterfaceType(type) || isUnionType(type) || isEnumType(type) || isWrappingType(type) && isOutputType(type.ofType); } @@ -31908,6 +31033,10 @@ function assertOutputType(type) { } return type; } +/** + * These types may describe types which may be leaf values. + */ + function isLeafType(type) { return isScalarType(type) || isEnumType(type); } @@ -31917,6 +31046,10 @@ function assertLeafType(type) { } return type; } +/** + * These types may describe the parent context of a selection set. + */ + function isCompositeType(type) { return isObjectType(type) || isInterfaceType(type) || isUnionType(type); } @@ -31926,6 +31059,10 @@ function assertCompositeType(type) { } return type; } +/** + * These types may describe the parent context of a selection set. + */ + function isAbstractType(type) { return isInterfaceType(type) || isUnionType(type); } @@ -31954,8 +31091,10 @@ function assertAbstractType(type) { * }) * ``` */ + class GraphQLList { constructor(ofType) { + isType(ofType) || (0, _devAssert.devAssert)(false, `Expected ${(0, _inspect.inspect)(ofType)} to be a GraphQL type.`); this.ofType = ofType; } get [Symbol.toStringTag]() { @@ -31992,6 +31131,7 @@ class GraphQLList { exports.GraphQLList = GraphQLList; class GraphQLNonNull { constructor(ofType) { + isNullableType(ofType) || (0, _devAssert.devAssert)(false, `Expected ${(0, _inspect.inspect)(ofType)} to be a GraphQL nullable type.`); this.ofType = ofType; } get [Symbol.toStringTag]() { @@ -32004,6 +31144,9 @@ class GraphQLNonNull { return this.toString(); } } +/** + * These types wrap and modify other types + */ exports.GraphQLNonNull = GraphQLNonNull; function isWrappingType(type) { return isListType(type) || isNonNullType(type); @@ -32014,6 +31157,10 @@ function assertWrappingType(type) { } return type; } +/** + * These types can all accept null as a value. + */ + function isNullableType(type) { return isType(type) && !isNonNullType(type); } @@ -32028,6 +31175,10 @@ function getNullableType(type) { return isNonNullType(type) ? type.ofType : type; } } +/** + * These named types do not include modifiers like List or NonNull. + */ + function isNamedType(type) { return isScalarType(type) || isObjectType(type) || isInterfaceType(type) || isUnionType(type) || isEnumType(type) || isInputObjectType(type); } @@ -32046,12 +31197,27 @@ function getNamedType(type) { return unwrappedType; } } +/** + * Used while defining GraphQL types to allow for circular references in + * otherwise immutable type definitions. + */ + function resolveReadonlyArrayThunk(thunk) { return typeof thunk === 'function' ? thunk() : thunk; } function resolveObjMapThunk(thunk) { return typeof thunk === 'function' ? thunk() : thunk; } +/** + * Custom extensions + * + * @remarks + * Use a unique identifier name for your extension, for example the name of + * your library or project. Do not use a shortened identifier as this increases + * the risk of conflicts. We recommend you add at most one extension field, + * an object which can contain all the values you need. + */ + /** * Scalar Type Definition * @@ -32096,6 +31262,8 @@ class GraphQLScalarType { this.extensions = (0, _toObjMap.toObjMap)(config.extensions); this.astNode = config.astNode; this.extensionASTNodes = (_config$extensionASTN = config.extensionASTNodes) !== null && _config$extensionASTN !== void 0 ? _config$extensionASTN : []; + config.specifiedByURL == null || typeof config.specifiedByURL === 'string' || (0, _devAssert.devAssert)(false, `${this.name} must provide "specifiedByURL" as a string, ` + `but got: ${(0, _inspect.inspect)(config.specifiedByURL)}.`); + config.serialize == null || typeof config.serialize === 'function' || (0, _devAssert.devAssert)(false, `${this.name} must provide "serialize" function. If this custom Scalar is also used as an input type, ensure "parseValue" and "parseLiteral" functions are also provided.`); if (config.parseLiteral) { typeof config.parseValue === 'function' && typeof config.parseLiteral === 'function' || (0, _devAssert.devAssert)(false, `${this.name} must provide both "parseValue" and "parseLiteral" functions.`); } @@ -32123,6 +31291,7 @@ class GraphQLScalarType { return this.toString(); } } + /** * Object Type Definition * @@ -32173,10 +31342,9 @@ class GraphQLObjectType { this.extensions = (0, _toObjMap.toObjMap)(config.extensions); this.astNode = config.astNode; this.extensionASTNodes = (_config$extensionASTN2 = config.extensionASTNodes) !== null && _config$extensionASTN2 !== void 0 ? _config$extensionASTN2 : []; - // prettier-ignore - // FIXME: blocked by https://github.com/prettier/prettier/issues/14625 - this._fields = defineFieldMap.bind(undefined, config.fields); - this._interfaces = defineInterfaces.bind(undefined, config.interfaces); + this._fields = () => defineFieldMap(config); + this._interfaces = () => defineInterfaces(config); + config.isTypeOf == null || typeof config.isTypeOf === 'function' || (0, _devAssert.devAssert)(false, `${this.name} must provide "isTypeOf" as a function, ` + `but got: ${(0, _inspect.inspect)(config.isTypeOf)}.`); } get [Symbol.toStringTag]() { return 'GraphQLObjectType'; @@ -32213,14 +31381,21 @@ class GraphQLObjectType { } } exports.GraphQLObjectType = GraphQLObjectType; -function defineInterfaces(interfaces) { - return resolveReadonlyArrayThunk(interfaces !== null && interfaces !== void 0 ? interfaces : []); +function defineInterfaces(config) { + var _config$interfaces; + const interfaces = resolveReadonlyArrayThunk((_config$interfaces = config.interfaces) !== null && _config$interfaces !== void 0 ? _config$interfaces : []); + Array.isArray(interfaces) || (0, _devAssert.devAssert)(false, `${config.name} interfaces must be an Array or a function which returns an Array.`); + return interfaces; } -function defineFieldMap(fields) { - const fieldMap = resolveObjMapThunk(fields); +function defineFieldMap(config) { + const fieldMap = resolveObjMapThunk(config.fields); + isPlainObj(fieldMap) || (0, _devAssert.devAssert)(false, `${config.name} fields must be an object with field names as keys or a function which returns such an object.`); return (0, _mapValue.mapValue)(fieldMap, (fieldConfig, fieldName) => { var _fieldConfig$args; + isPlainObj(fieldConfig) || (0, _devAssert.devAssert)(false, `${config.name}.${fieldName} field config must be an object.`); + fieldConfig.resolve == null || typeof fieldConfig.resolve === 'function' || (0, _devAssert.devAssert)(false, `${config.name}.${fieldName} field resolver must be a function if ` + `provided, but got: ${(0, _inspect.inspect)(fieldConfig.resolve)}.`); const argsConfig = (_fieldConfig$args = fieldConfig.args) !== null && _fieldConfig$args !== void 0 ? _fieldConfig$args : {}; + isPlainObj(argsConfig) || (0, _devAssert.devAssert)(false, `${config.name}.${fieldName} args must be an object with argument names as keys.`); return { name: (0, _assertName.assertName)(fieldName), description: fieldConfig.description, @@ -32234,8 +31409,8 @@ function defineFieldMap(fields) { }; }); } -function defineArguments(args) { - return Object.entries(args).map(([argName, argConfig]) => ({ +function defineArguments(config) { + return Object.entries(config).map(([argName, argConfig]) => ({ name: (0, _assertName.assertName)(argName), description: argConfig.description, type: argConfig.type, @@ -32245,6 +31420,9 @@ function defineArguments(args) { astNode: argConfig.astNode })); } +function isPlainObj(obj) { + return (0, _isObjectLike.isObjectLike)(obj) && !Array.isArray(obj); +} function fieldsToFieldsConfig(fields) { return (0, _mapValue.mapValue)(fields, field => ({ description: field.description, @@ -32260,6 +31438,7 @@ function fieldsToFieldsConfig(fields) { /** * @internal */ + function argsToArgsConfig(args) { return (0, _keyValMap.keyValMap)(args, arg => arg.name, arg => ({ description: arg.description, @@ -32273,6 +31452,7 @@ function argsToArgsConfig(args) { function isRequiredArgument(arg) { return isNonNullType(arg.type) && arg.defaultValue === undefined; } + /** * Interface Type Definition * @@ -32301,10 +31481,9 @@ class GraphQLInterfaceType { this.extensions = (0, _toObjMap.toObjMap)(config.extensions); this.astNode = config.astNode; this.extensionASTNodes = (_config$extensionASTN3 = config.extensionASTNodes) !== null && _config$extensionASTN3 !== void 0 ? _config$extensionASTN3 : []; - // prettier-ignore - // FIXME: blocked by https://github.com/prettier/prettier/issues/14625 - this._fields = defineFieldMap.bind(undefined, config.fields); - this._interfaces = defineInterfaces.bind(undefined, config.interfaces); + this._fields = defineFieldMap.bind(undefined, config); + this._interfaces = defineInterfaces.bind(undefined, config); + config.resolveType == null || typeof config.resolveType === 'function' || (0, _devAssert.devAssert)(false, `${this.name} must provide "resolveType" as a function, ` + `but got: ${(0, _inspect.inspect)(config.resolveType)}.`); } get [Symbol.toStringTag]() { return 'GraphQLInterfaceType'; @@ -32340,6 +31519,7 @@ class GraphQLInterfaceType { return this.toString(); } } + /** * Union Type Definition * @@ -32374,7 +31554,8 @@ class GraphQLUnionType { this.extensions = (0, _toObjMap.toObjMap)(config.extensions); this.astNode = config.astNode; this.extensionASTNodes = (_config$extensionASTN4 = config.extensionASTNodes) !== null && _config$extensionASTN4 !== void 0 ? _config$extensionASTN4 : []; - this._types = defineTypes.bind(undefined, config.types); + this._types = defineTypes.bind(undefined, config); + config.resolveType == null || typeof config.resolveType === 'function' || (0, _devAssert.devAssert)(false, `${this.name} must provide "resolveType" as a function, ` + `but got: ${(0, _inspect.inspect)(config.resolveType)}.`); } get [Symbol.toStringTag]() { return 'GraphQLUnionType'; @@ -32404,19 +31585,12 @@ class GraphQLUnionType { } } exports.GraphQLUnionType = GraphQLUnionType; -function defineTypes(types) { - return resolveReadonlyArrayThunk(types); -} -function enumValuesFromConfig(values) { - return Object.entries(values).map(([valueName, valueConfig]) => ({ - name: (0, _assertName.assertEnumValueName)(valueName), - description: valueConfig.description, - value: valueConfig.value !== undefined ? valueConfig.value : valueName, - deprecationReason: valueConfig.deprecationReason, - extensions: (0, _toObjMap.toObjMap)(valueConfig.extensions), - astNode: valueConfig.astNode - })); +function defineTypes(config) { + const types = resolveReadonlyArrayThunk(config.types); + Array.isArray(types) || (0, _devAssert.devAssert)(false, `Must provide Array of types or a function which returns such an array for Union ${config.name}.`); + return types; } + /** * Enum Type Definition * @@ -32440,7 +31614,8 @@ function enumValuesFromConfig(values) { * Note: If a value is not provided in a definition, the name of the enum value * will be used as its internal value. */ -class GraphQLEnumType /* */ { +class GraphQLEnumType { + /* */ constructor(config) { var _config$extensionASTN5; this.name = (0, _assertName.assertName)(config.name); @@ -32448,7 +31623,7 @@ class GraphQLEnumType /* */ { this.extensions = (0, _toObjMap.toObjMap)(config.extensions); this.astNode = config.astNode; this.extensionASTNodes = (_config$extensionASTN5 = config.extensionASTNodes) !== null && _config$extensionASTN5 !== void 0 ? _config$extensionASTN5 : []; - this._values = typeof config.values === 'function' ? config.values : enumValuesFromConfig(config.values); + this._values = typeof config.values === 'function' ? config.values : defineEnumValues(this.name, config.values); this._valueLookup = null; this._nameLookup = null; } @@ -32457,7 +31632,7 @@ class GraphQLEnumType /* */ { } getValues() { if (typeof this._values === 'function') { - this._values = enumValuesFromConfig(this._values()); + this._values = defineEnumValues(this.name, this._values()); } return this._values; } @@ -32467,7 +31642,7 @@ class GraphQLEnumType /* */ { } return this._nameLookup[name]; } - serialize(outputValue /* T */) { + serialize(outputValue) { if (this._valueLookup === null) { this._valueLookup = new Map(this.getValues().map(enumValue => [enumValue.value, enumValue])); } @@ -32477,7 +31652,8 @@ class GraphQLEnumType /* */ { } return enumValue.name; } - parseValue(inputValue) { + parseValue(inputValue) /* T */ + { if (typeof inputValue !== 'string') { const valueStr = (0, _inspect.inspect)(inputValue); throw new _GraphQLError.GraphQLError(`Enum "${this.name}" cannot represent non-string value: ${valueStr}.` + didYouMeanEnumValue(this, valueStr)); @@ -32488,7 +31664,8 @@ class GraphQLEnumType /* */ { } return enumValue.value; } - parseLiteral(valueNode, _variables) { + parseLiteral(valueNode, _variables) /* T */ + { // Note: variables will be resolved to a value before calling this function. if (valueNode.kind !== _kinds.Kind.ENUM) { const valueStr = (0, _printer.print)(valueNode); @@ -32535,6 +31712,21 @@ function didYouMeanEnumValue(enumType, unknownValueStr) { const suggestedValues = (0, _suggestionList.suggestionList)(unknownValueStr, allNames); return (0, _didYouMean.didYouMean)('the enum value', suggestedValues); } +function defineEnumValues(typeName, valueMap) { + isPlainObj(valueMap) || (0, _devAssert.devAssert)(false, `${typeName} values must be an object with value names as keys.`); + return Object.entries(valueMap).map(([valueName, valueConfig]) => { + isPlainObj(valueConfig) || (0, _devAssert.devAssert)(false, `${typeName}.${valueName} must refer to an object with a "value" key ` + `representing an internal value but got: ${(0, _inspect.inspect)(valueConfig)}.`); + return { + name: (0, _assertName.assertEnumValueName)(valueName), + description: valueConfig.description, + value: valueConfig.value !== undefined ? valueConfig.value : valueName, + deprecationReason: valueConfig.deprecationReason, + extensions: (0, _toObjMap.toObjMap)(valueConfig.extensions), + astNode: valueConfig.astNode + }; + }); +} + /** * Input Object Type Definition * @@ -32565,7 +31757,7 @@ class GraphQLInputObjectType { this.astNode = config.astNode; this.extensionASTNodes = (_config$extensionASTN6 = config.extensionASTNodes) !== null && _config$extensionASTN6 !== void 0 ? _config$extensionASTN6 : []; this.isOneOf = (_config$isOneOf = config.isOneOf) !== null && _config$isOneOf !== void 0 ? _config$isOneOf : false; - this._fields = defineInputFieldMap.bind(undefined, config.fields); + this._fields = defineInputFieldMap.bind(undefined, config); } get [Symbol.toStringTag]() { return 'GraphQLInputObjectType'; @@ -32603,17 +31795,21 @@ class GraphQLInputObjectType { } } exports.GraphQLInputObjectType = GraphQLInputObjectType; -function defineInputFieldMap(fields) { - const fieldMap = resolveObjMapThunk(fields); - return (0, _mapValue.mapValue)(fieldMap, (fieldConfig, fieldName) => ({ - name: (0, _assertName.assertName)(fieldName), - description: fieldConfig.description, - type: fieldConfig.type, - defaultValue: fieldConfig.defaultValue, - deprecationReason: fieldConfig.deprecationReason, - extensions: (0, _toObjMap.toObjMap)(fieldConfig.extensions), - astNode: fieldConfig.astNode - })); +function defineInputFieldMap(config) { + const fieldMap = resolveObjMapThunk(config.fields); + isPlainObj(fieldMap) || (0, _devAssert.devAssert)(false, `${config.name} fields must be an object with field names as keys or a function which returns such an object.`); + return (0, _mapValue.mapValue)(fieldMap, (fieldConfig, fieldName) => { + !('resolve' in fieldConfig) || (0, _devAssert.devAssert)(false, `${config.name}.${fieldName} field has a resolve property, but Input Types cannot define resolvers.`); + return { + name: (0, _assertName.assertName)(fieldName), + description: fieldConfig.description, + type: fieldConfig.type, + defaultValue: fieldConfig.defaultValue, + deprecationReason: fieldConfig.deprecationReason, + extensions: (0, _toObjMap.toObjMap)(fieldConfig.extensions), + astNode: fieldConfig.astNode + }; + }); } function isRequiredInputField(field) { return isNonNullType(field.type) && field.defaultValue === undefined; @@ -32632,13 +31828,15 @@ function isRequiredInputField(field) { Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.GraphQLStreamDirective = exports.GraphQLSpecifiedByDirective = exports.GraphQLSkipDirective = exports.GraphQLOneOfDirective = exports.GraphQLIncludeDirective = exports.GraphQLDirective = exports.GraphQLDeprecatedDirective = exports.GraphQLDeferDirective = exports.DEFAULT_DEPRECATION_REASON = void 0; +exports.GraphQLSpecifiedByDirective = exports.GraphQLSkipDirective = exports.GraphQLOneOfDirective = exports.GraphQLIncludeDirective = exports.GraphQLDirective = exports.GraphQLDeprecatedDirective = exports.DEFAULT_DEPRECATION_REASON = void 0; exports.assertDirective = assertDirective; exports.isDirective = isDirective; exports.isSpecifiedDirective = isSpecifiedDirective; exports.specifiedDirectives = void 0; +var _devAssert = __webpack_require__(/*! ../jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); var _instanceOf = __webpack_require__(/*! ../jsutils/instanceOf.mjs */ "../../../node_modules/graphql/jsutils/instanceOf.mjs"); +var _isObjectLike = __webpack_require__(/*! ../jsutils/isObjectLike.mjs */ "../../../node_modules/graphql/jsutils/isObjectLike.mjs"); var _toObjMap = __webpack_require__(/*! ../jsutils/toObjMap.mjs */ "../../../node_modules/graphql/jsutils/toObjMap.mjs"); var _directiveLocation = __webpack_require__(/*! ../language/directiveLocation.mjs */ "../../../node_modules/graphql/language/directiveLocation.mjs"); var _assertName = __webpack_require__(/*! ./assertName.mjs */ "../../../node_modules/graphql/type/assertName.mjs"); @@ -32647,6 +31845,7 @@ var _scalars = __webpack_require__(/*! ./scalars.mjs */ "../../../node_modules/g /** * Test if the given value is a GraphQL directive. */ + function isDirective(directive) { return (0, _instanceOf.instanceOf)(directive, GraphQLDirective); } @@ -32656,6 +31855,16 @@ function assertDirective(directive) { } return directive; } +/** + * Custom extensions + * + * @remarks + * Use a unique identifier name for your extension, for example the name of + * your library or project. Do not use a shortened identifier as this increases + * the risk of conflicts. We recommend you add at most one extension field, + * an object which can contain all the values you need. + */ + /** * Directives are used by the GraphQL runtime as a way of modifying execution * behavior. Type system creators will usually not create these directly. @@ -32669,7 +31878,9 @@ class GraphQLDirective { this.isRepeatable = (_config$isRepeatable = config.isRepeatable) !== null && _config$isRepeatable !== void 0 ? _config$isRepeatable : false; this.extensions = (0, _toObjMap.toObjMap)(config.extensions); this.astNode = config.astNode; + Array.isArray(config.locations) || (0, _devAssert.devAssert)(false, `@${config.name} locations must be an Array.`); const args = (_config$args = config.args) !== null && _config$args !== void 0 ? _config$args : {}; + (0, _isObjectLike.isObjectLike)(args) && !Array.isArray(args) || (0, _devAssert.devAssert)(false, `@${config.name} args must be an object with argument names as keys.`); this.args = (0, _definition.defineArguments)(args); } get [Symbol.toStringTag]() { @@ -32693,6 +31904,7 @@ class GraphQLDirective { return this.toString(); } } + /** * Used to conditionally include fields or fragments. */ @@ -32711,6 +31923,7 @@ const GraphQLIncludeDirective = exports.GraphQLIncludeDirective = new GraphQLDir /** * Used to conditionally skip (exclude) fields or fragments. */ + const GraphQLSkipDirective = exports.GraphQLSkipDirective = new GraphQLDirective({ name: 'skip', description: 'Directs the executor to skip this field or fragment when the `if` argument is true.', @@ -32722,56 +31935,15 @@ const GraphQLSkipDirective = exports.GraphQLSkipDirective = new GraphQLDirective } } }); -/** - * Used to conditionally defer fragments. - */ -const GraphQLDeferDirective = exports.GraphQLDeferDirective = new GraphQLDirective({ - name: 'defer', - description: 'Directs the executor to defer this fragment when the `if` argument is true or undefined.', - locations: [_directiveLocation.DirectiveLocation.FRAGMENT_SPREAD, _directiveLocation.DirectiveLocation.INLINE_FRAGMENT], - args: { - if: { - type: new _definition.GraphQLNonNull(_scalars.GraphQLBoolean), - description: 'Deferred when true or undefined.', - defaultValue: true - }, - label: { - type: _scalars.GraphQLString, - description: 'Unique name' - } - } -}); -/** - * Used to conditionally stream list fields. - */ -const GraphQLStreamDirective = exports.GraphQLStreamDirective = new GraphQLDirective({ - name: 'stream', - description: 'Directs the executor to stream plural fields when the `if` argument is true or undefined.', - locations: [_directiveLocation.DirectiveLocation.FIELD], - args: { - if: { - type: new _definition.GraphQLNonNull(_scalars.GraphQLBoolean), - description: 'Stream when true or undefined.', - defaultValue: true - }, - label: { - type: _scalars.GraphQLString, - description: 'Unique name' - }, - initialCount: { - defaultValue: 0, - type: _scalars.GraphQLInt, - description: 'Number of items to return immediately' - } - } -}); /** * Constant string used for default reason for a deprecation. */ + const DEFAULT_DEPRECATION_REASON = exports.DEFAULT_DEPRECATION_REASON = 'No longer supported'; /** * Used to declare element of a GraphQL schema as deprecated. */ + const GraphQLDeprecatedDirective = exports.GraphQLDeprecatedDirective = new GraphQLDirective({ name: 'deprecated', description: 'Marks an element of a GraphQL schema as no longer supported.', @@ -32787,6 +31959,7 @@ const GraphQLDeprecatedDirective = exports.GraphQLDeprecatedDirective = new Grap /** * Used to provide a URL for specifying the behavior of custom scalar definitions. */ + const GraphQLSpecifiedByDirective = exports.GraphQLSpecifiedByDirective = new GraphQLDirective({ name: 'specifiedBy', description: 'Exposes a URL that specifies the behavior of this scalar.', @@ -32801,6 +31974,7 @@ const GraphQLSpecifiedByDirective = exports.GraphQLSpecifiedByDirective = new Gr /** * Used to indicate an Input Object is a OneOf Input Object. */ + const GraphQLOneOfDirective = exports.GraphQLOneOfDirective = new GraphQLDirective({ name: 'oneOf', description: 'Indicates exactly one field must be supplied and this field must not be `null`.', @@ -32810,6 +31984,7 @@ const GraphQLOneOfDirective = exports.GraphQLOneOfDirective = new GraphQLDirecti /** * The full list of specified directives. */ + const specifiedDirectives = exports.specifiedDirectives = Object.freeze([GraphQLIncludeDirective, GraphQLSkipDirective, GraphQLDeprecatedDirective, GraphQLSpecifiedByDirective, GraphQLOneOfDirective]); function isSpecifiedDirective(directive) { return specifiedDirectives.some(({ @@ -32854,12 +32029,6 @@ Object.defineProperty(exports, "GraphQLBoolean", ({ return _scalars.GraphQLBoolean; } })); -Object.defineProperty(exports, "GraphQLDeferDirective", ({ - enumerable: true, - get: function () { - return _directives.GraphQLDeferDirective; - } -})); Object.defineProperty(exports, "GraphQLDeprecatedDirective", ({ enumerable: true, get: function () { @@ -32962,12 +32131,6 @@ Object.defineProperty(exports, "GraphQLSpecifiedByDirective", ({ return _directives.GraphQLSpecifiedByDirective; } })); -Object.defineProperty(exports, "GraphQLStreamDirective", ({ - enumerable: true, - get: function () { - return _directives.GraphQLStreamDirective; - } -})); Object.defineProperty(exports, "GraphQLString", ({ enumerable: true, get: function () { @@ -33474,7 +32637,7 @@ const __Directive = exports.__Directive = new _definition.GraphQLObjectType({ resolve(field, { includeDeprecated }) { - return includeDeprecated === true ? field.args : field.args.filter(arg => arg.deprecationReason == null); + return includeDeprecated ? field.args : field.args.filter(arg => arg.deprecationReason == null); } } }) @@ -33594,6 +32757,7 @@ const __Type = exports.__Type = new _definition.GraphQLObjectType({ } /* c8 ignore next 3 */ // Not reachable, all possible types have been considered) + false || (0, _invariant.invariant)(false, `Unexpected type: "${(0, _inspect.inspect)(type)}".`); } }, @@ -33603,9 +32767,8 @@ const __Type = exports.__Type = new _definition.GraphQLObjectType({ }, description: { type: _scalars.GraphQLString, - resolve: type => - // FIXME: add test case - /* c8 ignore next */ + resolve: (type // FIXME: add test case + ) => /* c8 ignore next */ 'description' in type ? type.description : undefined }, specifiedByURL: { @@ -33625,7 +32788,7 @@ const __Type = exports.__Type = new _definition.GraphQLObjectType({ }) { if ((0, _definition.isObjectType)(type) || (0, _definition.isInterfaceType)(type)) { const fields = Object.values(type.getFields()); - return includeDeprecated === true ? fields : fields.filter(field => field.deprecationReason == null); + return includeDeprecated ? fields : fields.filter(field => field.deprecationReason == null); } } }, @@ -33660,7 +32823,7 @@ const __Type = exports.__Type = new _definition.GraphQLObjectType({ }) { if ((0, _definition.isEnumType)(type)) { const values = type.getValues(); - return includeDeprecated === true ? values : values.filter(field => field.deprecationReason == null); + return includeDeprecated ? values : values.filter(field => field.deprecationReason == null); } } }, @@ -33677,7 +32840,7 @@ const __Type = exports.__Type = new _definition.GraphQLObjectType({ }) { if ((0, _definition.isInputObjectType)(type)) { const values = Object.values(type.getFields()); - return includeDeprecated === true ? values : values.filter(field => field.deprecationReason == null); + return includeDeprecated ? values : values.filter(field => field.deprecationReason == null); } } }, @@ -33718,7 +32881,7 @@ const __Field = exports.__Field = new _definition.GraphQLObjectType({ resolve(field, { includeDeprecated }) { - return includeDeprecated === true ? field.args : field.args.filter(arg => arg.deprecationReason == null); + return includeDeprecated ? field.args : field.args.filter(arg => arg.deprecationReason == null); } }, type: { @@ -33848,6 +33011,7 @@ const __TypeKind = exports.__TypeKind = new _definition.GraphQLEnumType({ * Note that these are GraphQLField and not GraphQLFieldConfig, * so the format for args is different. */ + const SchemaMetaFieldDef = exports.SchemaMetaFieldDef = { name: '__schema', type: new _definition.GraphQLNonNull(__Schema), @@ -33927,11 +33091,13 @@ var _definition = __webpack_require__(/*! ./definition.mjs */ "../../../node_mod * Maximum possible Int value as per GraphQL Spec (32-bit signed integer). * n.b. This differs from JavaScript's numbers that are IEEE 754 doubles safe up-to 2^53 - 1 * */ + const GRAPHQL_MAX_INT = exports.GRAPHQL_MAX_INT = 2147483647; /** * Minimum possible Int value as per GraphQL Spec (32-bit signed integer). * n.b. This differs from JavaScript's numbers that are IEEE 754 doubles safe starting at -(2^53 - 1) * */ + const GRAPHQL_MIN_INT = exports.GRAPHQL_MIN_INT = -2147483648; const GraphQLInt = exports.GraphQLInt = new _definition.GraphQLScalarType({ name: 'Int', @@ -34002,9 +33168,7 @@ const GraphQLFloat = exports.GraphQLFloat = new _definition.GraphQLScalarType({ }, parseLiteral(valueNode) { if (valueNode.kind !== _kinds.Kind.FLOAT && valueNode.kind !== _kinds.Kind.INT) { - throw new _GraphQLError.GraphQLError(`Float cannot represent non numeric value: ${(0, _printer.print)(valueNode)}`, { - nodes: valueNode - }); + throw new _GraphQLError.GraphQLError(`Float cannot represent non numeric value: ${(0, _printer.print)(valueNode)}`, valueNode); } return parseFloat(valueNode.value); } @@ -34013,9 +33177,9 @@ const GraphQLString = exports.GraphQLString = new _definition.GraphQLScalarType( name: 'String', description: 'The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.', serialize(outputValue) { - const coercedValue = serializeObject(outputValue); - // Serialize string, boolean and number values to a string, but do not + const coercedValue = serializeObject(outputValue); // Serialize string, boolean and number values to a string, but do not // attempt to coerce object, function, symbol, or other types as strings. + if (typeof coercedValue === 'string') { return coercedValue; } @@ -34106,10 +33270,10 @@ function isSpecifiedScalarType(type) { return specifiedScalarTypes.some(({ name }) => type.name === name); -} -// Support serializing objects with custom valueOf() or toJSON() functions - +} // Support serializing objects with custom valueOf() or toJSON() functions - // a common way to represent a complex value which can be represented as // a string (ex: MongoDB id objects). + function serializeObject(outputValue) { if ((0, _isObjectLike.isObjectLike)(outputValue)) { if (typeof outputValue.valueOf === 'function') { @@ -34141,8 +33305,10 @@ Object.defineProperty(exports, "__esModule", ({ exports.GraphQLSchema = void 0; exports.assertSchema = assertSchema; exports.isSchema = isSchema; +var _devAssert = __webpack_require__(/*! ../jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); var _instanceOf = __webpack_require__(/*! ../jsutils/instanceOf.mjs */ "../../../node_modules/graphql/jsutils/instanceOf.mjs"); +var _isObjectLike = __webpack_require__(/*! ../jsutils/isObjectLike.mjs */ "../../../node_modules/graphql/jsutils/isObjectLike.mjs"); var _toObjMap = __webpack_require__(/*! ../jsutils/toObjMap.mjs */ "../../../node_modules/graphql/jsutils/toObjMap.mjs"); var _ast = __webpack_require__(/*! ../language/ast.mjs */ "../../../node_modules/graphql/language/ast.mjs"); var _definition = __webpack_require__(/*! ./definition.mjs */ "../../../node_modules/graphql/type/definition.mjs"); @@ -34151,6 +33317,7 @@ var _introspection = __webpack_require__(/*! ./introspection.mjs */ "../../../no /** * Test if the given value is a GraphQL schema. */ + function isSchema(schema) { return (0, _instanceOf.instanceOf)(schema, GraphQLSchema); } @@ -34160,6 +33327,16 @@ function assertSchema(schema) { } return schema; } +/** + * Custom extensions + * + * @remarks + * Use a unique identifier name for your extension, for example the name of + * your library or project. Do not use a shortened identifier as this increases + * the risk of conflicts. We recommend you add at most one extension field, + * an object which can contain all the values you need. + */ + /** * Schema Definition * @@ -34229,22 +33406,28 @@ function assertSchema(schema) { * ``` */ class GraphQLSchema { + // Used as a cache for validateSchema(). constructor(config) { var _config$extensionASTN, _config$directives; + // If this schema was built from a source known to be valid, then it may be // marked with assumeValid to avoid an additional type system validation. - this.__validationErrors = config.assumeValid === true ? [] : undefined; + this.__validationErrors = config.assumeValid === true ? [] : undefined; // Check for common mistakes during construction to produce early errors. + + (0, _isObjectLike.isObjectLike)(config) || (0, _devAssert.devAssert)(false, 'Must provide configuration object.'); + !config.types || Array.isArray(config.types) || (0, _devAssert.devAssert)(false, `"types" must be Array if provided but got: ${(0, _inspect.inspect)(config.types)}.`); + !config.directives || Array.isArray(config.directives) || (0, _devAssert.devAssert)(false, '"directives" must be Array if provided but got: ' + `${(0, _inspect.inspect)(config.directives)}.`); this.description = config.description; this.extensions = (0, _toObjMap.toObjMap)(config.extensions); this.astNode = config.astNode; this.extensionASTNodes = (_config$extensionASTN = config.extensionASTNodes) !== null && _config$extensionASTN !== void 0 ? _config$extensionASTN : []; this._queryType = config.query; this._mutationType = config.mutation; - this._subscriptionType = config.subscription; - // Provide specified directives (e.g. @include and @skip) by default. - this._directives = (_config$directives = config.directives) !== null && _config$directives !== void 0 ? _config$directives : _directives.specifiedDirectives; - // To preserve order of user-provided types, we add first to add them to + this._subscriptionType = config.subscription; // Provide specified directives (e.g. @include and @skip) by default. + + this._directives = (_config$directives = config.directives) !== null && _config$directives !== void 0 ? _config$directives : _directives.specifiedDirectives; // To preserve order of user-provided types, we add first to add them to // the set of "collected" types, so `collectReferencedTypes` ignore them. + const allReferencedTypes = new Set(config.types); if (config.types != null) { for (const type of config.types) { @@ -34271,17 +33454,18 @@ class GraphQLSchema { } } } - collectReferencedTypes(_introspection.__Schema, allReferencedTypes); - // Storing the resulting map for reference by the schema. + collectReferencedTypes(_introspection.__Schema, allReferencedTypes); // Storing the resulting map for reference by the schema. + this._typeMap = Object.create(null); - this._subTypeMap = new Map(); - // Keep track of all implementations by interface name. + this._subTypeMap = Object.create(null); // Keep track of all implementations by interface name. + this._implementationsMap = Object.create(null); for (const namedType of allReferencedTypes) { if (namedType == null) { continue; } const typeName = namedType.name; + typeName || (0, _devAssert.devAssert)(false, 'One of the provided types for building the Schema is missing a name.'); if (this._typeMap[typeName] !== undefined) { throw new Error(`Schema must contain uniquely named types but contains multiple types named "${typeName}".`); } @@ -34356,17 +33540,25 @@ class GraphQLSchema { }; } isSubType(abstractType, maybeSubType) { - let set = this._subTypeMap.get(abstractType); - if (set === undefined) { + let map = this._subTypeMap[abstractType.name]; + if (map === undefined) { + map = Object.create(null); if ((0, _definition.isUnionType)(abstractType)) { - set = new Set(abstractType.getTypes()); + for (const type of abstractType.getTypes()) { + map[type.name] = true; + } } else { const implementations = this.getImplementations(abstractType); - set = new Set([...implementations.objects, ...implementations.interfaces]); + for (const type of implementations.objects) { + map[type.name] = true; + } + for (const type of implementations.interfaces) { + map[type.name] = true; + } } - this._subTypeMap.set(abstractType, set); + this._subTypeMap[abstractType.name] = map; } - return set.has(maybeSubType); + return map[maybeSubType.name] !== undefined; } getDirectives() { return this._directives; @@ -34374,33 +33566,6 @@ class GraphQLSchema { getDirective(name) { return this.getDirectives().find(directive => directive.name === name); } - /** - * This method looks up the field on the given type definition. - * It has special casing for the three introspection fields, `__schema`, - * `__type` and `__typename`. - * - * `__typename` is special because it can always be queried as a field, even - * in situations where no other fields are allowed, like on a Union. - * - * `__schema` and `__type` could get automatically added to the query type, - * but that would require mutating type definitions, which would cause issues. - */ - getField(parentType, fieldName) { - switch (fieldName) { - case _introspection.SchemaMetaFieldDef.name: - return this.getQueryType() === parentType ? _introspection.SchemaMetaFieldDef : undefined; - case _introspection.TypeMetaFieldDef.name: - return this.getQueryType() === parentType ? _introspection.TypeMetaFieldDef : undefined; - case _introspection.TypeNameMetaFieldDef.name: - return _introspection.TypeNameMetaFieldDef; - } - // this function is part "hot" path inside executor and check presence - // of 'getFields' is faster than to use `!isUnionType` - if ('getFields' in parentType) { - return parentType.getFields()[fieldName]; - } - return undefined; - } toConfig() { return { description: this.description, @@ -34459,9 +33624,6 @@ Object.defineProperty(exports, "__esModule", ({ })); exports.assertValidSchema = assertValidSchema; exports.validateSchema = validateSchema; -var _AccumulatorMap = __webpack_require__(/*! ../jsutils/AccumulatorMap.mjs */ "../../../node_modules/graphql/jsutils/AccumulatorMap.mjs"); -var _capitalize = __webpack_require__(/*! ../jsutils/capitalize.mjs */ "../../../node_modules/graphql/jsutils/capitalize.mjs"); -var _formatList = __webpack_require__(/*! ../jsutils/formatList.mjs */ "../../../node_modules/graphql/jsutils/formatList.mjs"); var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); var _GraphQLError = __webpack_require__(/*! ../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); var _ast = __webpack_require__(/*! ../language/ast.mjs */ "../../../node_modules/graphql/language/ast.mjs"); @@ -34477,20 +33639,21 @@ var _schema = __webpack_require__(/*! ./schema.mjs */ "../../../node_modules/gra * Validation runs synchronously, returning an array of encountered errors, or * an empty array if no errors were encountered and the Schema is valid. */ + function validateSchema(schema) { // First check to ensure the provided value is in fact a GraphQLSchema. - (0, _schema.assertSchema)(schema); - // If this Schema has already been validated, return the previous results. + (0, _schema.assertSchema)(schema); // If this Schema has already been validated, return the previous results. + if (schema.__validationErrors) { return schema.__validationErrors; - } - // Validate the schema, producing a list of errors. + } // Validate the schema, producing a list of errors. + const context = new SchemaValidationContext(schema); validateRootTypes(context); validateDirectives(context); - validateTypes(context); - // Persist the results of validation before returning to ensure validation + validateTypes(context); // Persist the results of validation before returning to ensure validation // does not run multiple times for this schema. + const errors = context.getErrors(); schema.__validationErrors = errors; return errors; @@ -34499,6 +33662,7 @@ function validateSchema(schema) { * Utility function which asserts a schema is valid by throwing an error if * it is invalid. */ + function assertValidSchema(schema) { const errors = validateSchema(schema); if (errors.length !== 0) { @@ -34522,28 +33686,22 @@ class SchemaValidationContext { } function validateRootTypes(context) { const schema = context.schema; - if (schema.getQueryType() == null) { + const queryType = schema.getQueryType(); + if (!queryType) { context.reportError('Query root type must be provided.', schema.astNode); + } else if (!(0, _definition.isObjectType)(queryType)) { + var _getOperationTypeNode; + context.reportError(`Query root type must be Object type, it cannot be ${(0, _inspect.inspect)(queryType)}.`, (_getOperationTypeNode = getOperationTypeNode(schema, _ast.OperationTypeNode.QUERY)) !== null && _getOperationTypeNode !== void 0 ? _getOperationTypeNode : queryType.astNode); } - const rootTypesMap = new _AccumulatorMap.AccumulatorMap(); - for (const operationType of Object.values(_ast.OperationTypeNode)) { - const rootType = schema.getRootType(operationType); - if (rootType != null) { - if (!(0, _definition.isObjectType)(rootType)) { - var _getOperationTypeNode; - const operationTypeStr = (0, _capitalize.capitalize)(operationType); - const rootTypeStr = (0, _inspect.inspect)(rootType); - context.reportError(operationType === _ast.OperationTypeNode.QUERY ? `${operationTypeStr} root type must be Object type, it cannot be ${rootTypeStr}.` : `${operationTypeStr} root type must be Object type if provided, it cannot be ${rootTypeStr}.`, (_getOperationTypeNode = getOperationTypeNode(schema, operationType)) !== null && _getOperationTypeNode !== void 0 ? _getOperationTypeNode : rootType.astNode); - } else { - rootTypesMap.add(rootType, operationType); - } - } + const mutationType = schema.getMutationType(); + if (mutationType && !(0, _definition.isObjectType)(mutationType)) { + var _getOperationTypeNode2; + context.reportError('Mutation root type must be Object type if provided, it cannot be ' + `${(0, _inspect.inspect)(mutationType)}.`, (_getOperationTypeNode2 = getOperationTypeNode(schema, _ast.OperationTypeNode.MUTATION)) !== null && _getOperationTypeNode2 !== void 0 ? _getOperationTypeNode2 : mutationType.astNode); } - for (const [rootType, operationTypes] of rootTypesMap) { - if (operationTypes.length > 1) { - const operationList = (0, _formatList.andList)(operationTypes); - context.reportError(`All root types must be different, "${rootType.name}" type is used as ${operationList} root types.`, operationTypes.map(operationType => getOperationTypeNode(schema, operationType))); - } + const subscriptionType = schema.getSubscriptionType(); + if (subscriptionType && !(0, _definition.isObjectType)(subscriptionType)) { + var _getOperationTypeNode3; + context.reportError('Subscription root type must be Object type if provided, it cannot be ' + `${(0, _inspect.inspect)(subscriptionType)}.`, (_getOperationTypeNode3 = getOperationTypeNode(schema, _ast.OperationTypeNode.SUBSCRIPTION)) !== null && _getOperationTypeNode3 !== void 0 ? _getOperationTypeNode3 : subscriptionType.astNode); } } function getOperationTypeNode(schema, operation) { @@ -34552,7 +33710,9 @@ function getOperationTypeNode(schema, operation) { // FIXME: https://github.com/graphql/graphql-js/issues/2203 schemaNode => { var _schemaNode$operation; - return /* c8 ignore next */(_schemaNode$operation = schemaNode === null || schemaNode === void 0 ? void 0 : schemaNode.operationTypes) !== null && _schemaNode$operation !== void 0 ? _schemaNode$operation : []; + return /* c8 ignore next */( + (_schemaNode$operation = schemaNode === null || schemaNode === void 0 ? void 0 : schemaNode.operationTypes) !== null && _schemaNode$operation !== void 0 ? _schemaNode$operation : [] + ); }).find(operationNode => operationNode.operation === operation)) === null || _flatMap$find === void 0 ? void 0 : _flatMap$find.type; } function validateDirectives(context) { @@ -34561,17 +33721,15 @@ function validateDirectives(context) { if (!(0, _directives.isDirective)(directive)) { context.reportError(`Expected directive but got: ${(0, _inspect.inspect)(directive)}.`, directive === null || directive === void 0 ? void 0 : directive.astNode); continue; - } - // Ensure they are named correctly. - validateName(context, directive); - if (directive.locations.length === 0) { - context.reportError(`Directive @${directive.name} must include 1 or more locations.`, directive.astNode); - } + } // Ensure they are named correctly. + + validateName(context, directive); // TODO: Ensure proper locations. // Ensure the arguments are valid. + for (const arg of directive.args) { // Ensure they are named correctly. - validateName(context, arg); - // Ensure the type is an input type. + validateName(context, arg); // Ensure the type is an input type. + if (!(0, _definition.isInputType)(arg.type)) { context.reportError(`The type of @${directive.name}(${arg.name}:) must be Input Type ` + `but got: ${(0, _inspect.inspect)(arg.type)}.`, arg.astNode); } @@ -34596,20 +33754,20 @@ function validateTypes(context) { if (!(0, _definition.isNamedType)(type)) { context.reportError(`Expected GraphQL named type but got: ${(0, _inspect.inspect)(type)}.`, type.astNode); continue; - } - // Ensure it is named correctly (excluding introspection types). + } // Ensure it is named correctly (excluding introspection types). + if (!(0, _introspection.isIntrospectionType)(type)) { validateName(context, type); } if ((0, _definition.isObjectType)(type)) { // Ensure fields are valid - validateFields(context, type); - // Ensure objects implement the interfaces they claim to. + validateFields(context, type); // Ensure objects implement the interfaces they claim to. + validateInterfaces(context, type); } else if ((0, _definition.isInterfaceType)(type)) { // Ensure fields are valid. - validateFields(context, type); - // Ensure interfaces implement the interfaces they claim to. + validateFields(context, type); // Ensure interfaces implement the interfaces they claim to. + validateInterfaces(context, type); } else if ((0, _definition.isUnionType)(type)) { // Ensure Unions include valid member types. @@ -34619,32 +33777,32 @@ function validateTypes(context) { validateEnumValues(context, type); } else if ((0, _definition.isInputObjectType)(type)) { // Ensure Input Object fields are valid. - validateInputFields(context, type); - // Ensure Input Objects do not contain non-nullable circular references + validateInputFields(context, type); // Ensure Input Objects do not contain non-nullable circular references + validateInputObjectCircularRefs(type); } } } function validateFields(context, type) { - const fields = Object.values(type.getFields()); - // Objects and Interfaces both must define one or more fields. + const fields = Object.values(type.getFields()); // Objects and Interfaces both must define one or more fields. + if (fields.length === 0) { context.reportError(`Type ${type.name} must define one or more fields.`, [type.astNode, ...type.extensionASTNodes]); } for (const field of fields) { // Ensure they are named correctly. - validateName(context, field); - // Ensure the type is an output type + validateName(context, field); // Ensure the type is an output type + if (!(0, _definition.isOutputType)(field.type)) { var _field$astNode; context.reportError(`The type of ${type.name}.${field.name} must be Output Type ` + `but got: ${(0, _inspect.inspect)(field.type)}.`, (_field$astNode = field.astNode) === null || _field$astNode === void 0 ? void 0 : _field$astNode.type); - } - // Ensure the arguments are valid + } // Ensure the arguments are valid + for (const arg of field.args) { - const argName = arg.name; - // Ensure they are named correctly. - validateName(context, arg); - // Ensure the type is an input type + const argName = arg.name; // Ensure they are named correctly. + + validateName(context, arg); // Ensure the type is an input type + if (!(0, _definition.isInputType)(arg.type)) { var _arg$astNode2; context.reportError(`The type of ${type.name}.${field.name}(${argName}:) must be Input ` + `Type but got: ${(0, _inspect.inspect)(arg.type)}.`, (_arg$astNode2 = arg.astNode) === null || _arg$astNode2 === void 0 ? void 0 : _arg$astNode2.type); @@ -34657,7 +33815,7 @@ function validateFields(context, type) { } } function validateInterfaces(context, type) { - const ifaceTypeNames = new Set(); + const ifaceTypeNames = Object.create(null); for (const iface of type.getInterfaces()) { if (!(0, _definition.isInterfaceType)(iface)) { context.reportError(`Type ${(0, _inspect.inspect)(type)} must only implement Interface types, ` + `it cannot implement ${(0, _inspect.inspect)(iface)}.`, getAllImplementsInterfaceNodes(type, iface)); @@ -34667,51 +33825,50 @@ function validateInterfaces(context, type) { context.reportError(`Type ${type.name} cannot implement itself because it would create a circular reference.`, getAllImplementsInterfaceNodes(type, iface)); continue; } - if (ifaceTypeNames.has(iface.name)) { + if (ifaceTypeNames[iface.name]) { context.reportError(`Type ${type.name} can only implement ${iface.name} once.`, getAllImplementsInterfaceNodes(type, iface)); continue; } - ifaceTypeNames.add(iface.name); + ifaceTypeNames[iface.name] = true; validateTypeImplementsAncestors(context, type, iface); validateTypeImplementsInterface(context, type, iface); } } function validateTypeImplementsInterface(context, type, iface) { - const typeFieldMap = type.getFields(); - // Assert each interface field is implemented. + const typeFieldMap = type.getFields(); // Assert each interface field is implemented. + for (const ifaceField of Object.values(iface.getFields())) { const fieldName = ifaceField.name; - const typeField = typeFieldMap[fieldName]; - // Assert interface field exists on type. - if (typeField == null) { + const typeField = typeFieldMap[fieldName]; // Assert interface field exists on type. + + if (!typeField) { context.reportError(`Interface field ${iface.name}.${fieldName} expected but ${type.name} does not provide it.`, [ifaceField.astNode, type.astNode, ...type.extensionASTNodes]); continue; - } - // Assert interface field type is satisfied by type field type, by being + } // Assert interface field type is satisfied by type field type, by being // a valid subtype. (covariant) + if (!(0, _typeComparators.isTypeSubTypeOf)(context.schema, typeField.type, ifaceField.type)) { var _ifaceField$astNode, _typeField$astNode; context.reportError(`Interface field ${iface.name}.${fieldName} expects type ` + `${(0, _inspect.inspect)(ifaceField.type)} but ${type.name}.${fieldName} ` + `is type ${(0, _inspect.inspect)(typeField.type)}.`, [(_ifaceField$astNode = ifaceField.astNode) === null || _ifaceField$astNode === void 0 ? void 0 : _ifaceField$astNode.type, (_typeField$astNode = typeField.astNode) === null || _typeField$astNode === void 0 ? void 0 : _typeField$astNode.type]); - } - // Assert each interface field arg is implemented. + } // Assert each interface field arg is implemented. + for (const ifaceArg of ifaceField.args) { const argName = ifaceArg.name; - const typeArg = typeField.args.find(arg => arg.name === argName); - // Assert interface field arg exists on object field. + const typeArg = typeField.args.find(arg => arg.name === argName); // Assert interface field arg exists on object field. + if (!typeArg) { context.reportError(`Interface field argument ${iface.name}.${fieldName}(${argName}:) expected but ${type.name}.${fieldName} does not provide it.`, [ifaceArg.astNode, typeField.astNode]); continue; - } - // Assert interface field arg type matches object field arg type. + } // Assert interface field arg type matches object field arg type. // (invariant) // TODO: change to contravariant? + if (!(0, _typeComparators.isEqualType)(ifaceArg.type, typeArg.type)) { var _ifaceArg$astNode, _typeArg$astNode; context.reportError(`Interface field argument ${iface.name}.${fieldName}(${argName}:) ` + `expects type ${(0, _inspect.inspect)(ifaceArg.type)} but ` + `${type.name}.${fieldName}(${argName}:) is type ` + `${(0, _inspect.inspect)(typeArg.type)}.`, [(_ifaceArg$astNode = ifaceArg.astNode) === null || _ifaceArg$astNode === void 0 ? void 0 : _ifaceArg$astNode.type, (_typeArg$astNode = typeArg.astNode) === null || _typeArg$astNode === void 0 ? void 0 : _typeArg$astNode.type]); - } - // TODO: validate default values? - } - // Assert additional arguments must not be required. + } // TODO: validate default values? + } // Assert additional arguments must not be required. + for (const typeArg of typeField.args) { const argName = typeArg.name; const ifaceArg = ifaceField.args.find(arg => arg.name === argName); @@ -34734,13 +33891,13 @@ function validateUnionMembers(context, union) { if (memberTypes.length === 0) { context.reportError(`Union type ${union.name} must define one or more member types.`, [union.astNode, ...union.extensionASTNodes]); } - const includedTypeNames = new Set(); + const includedTypeNames = Object.create(null); for (const memberType of memberTypes) { - if (includedTypeNames.has(memberType.name)) { + if (includedTypeNames[memberType.name]) { context.reportError(`Union type ${union.name} can only include type ${memberType.name} once.`, getUnionMemberTypeNodes(union, memberType.name)); continue; } - includedTypeNames.add(memberType.name); + includedTypeNames[memberType.name] = true; if (!(0, _definition.isObjectType)(memberType)) { context.reportError(`Union type ${union.name} can only include Object types, ` + `it cannot include ${(0, _inspect.inspect)(memberType)}.`, getUnionMemberTypeNodes(union, String(memberType))); } @@ -34760,12 +33917,12 @@ function validateInputFields(context, inputObj) { const fields = Object.values(inputObj.getFields()); if (fields.length === 0) { context.reportError(`Input Object type ${inputObj.name} must define one or more fields.`, [inputObj.astNode, ...inputObj.extensionASTNodes]); - } - // Ensure the arguments are valid + } // Ensure the arguments are valid + for (const field of fields) { // Ensure they are named correctly. - validateName(context, field); - // Ensure the type is an input type + validateName(context, field); // Ensure the type is an input type + if (!(0, _definition.isInputType)(field.type)) { var _field$astNode2; context.reportError(`The type of ${inputObj.name}.${field.name} must be Input Type ` + `but got: ${(0, _inspect.inspect)(field.type)}.`, (_field$astNode2 = field.astNode) === null || _field$astNode2 === void 0 ? void 0 : _field$astNode2.type); @@ -34792,20 +33949,20 @@ function createInputObjectCircularRefsValidator(context) { // Modified copy of algorithm from 'src/validation/rules/NoFragmentCycles.js'. // Tracks already visited types to maintain O(N) and to ensure that cycles // are not redundantly reported. - const visitedTypes = new Set(); - // Array of types nodes used to produce meaningful errors - const fieldPath = []; - // Position in the type path + const visitedTypes = Object.create(null); // Array of types nodes used to produce meaningful errors + + const fieldPath = []; // Position in the type path + const fieldPathIndexByTypeName = Object.create(null); - return detectCycleRecursive; - // This does a straight-forward DFS to find cycles. + return detectCycleRecursive; // This does a straight-forward DFS to find cycles. // It does not terminate when a cycle was found but continues to explore // the graph to find all possible cycles. + function detectCycleRecursive(inputObj) { - if (visitedTypes.has(inputObj)) { + if (visitedTypes[inputObj.name]) { return; } - visitedTypes.add(inputObj); + visitedTypes[inputObj.name] = true; fieldPathIndexByTypeName[inputObj.name] = fieldPath.length; const fields = Object.values(inputObj.getFields()); for (const field of fields) { @@ -34831,11 +33988,13 @@ function getAllImplementsInterfaceNodes(type, iface) { astNode, extensionASTNodes } = type; - const nodes = astNode != null ? [astNode, ...extensionASTNodes] : extensionASTNodes; - // FIXME: https://github.com/graphql/graphql-js/issues/2203 + const nodes = astNode != null ? [astNode, ...extensionASTNodes] : extensionASTNodes; // FIXME: https://github.com/graphql/graphql-js/issues/2203 + return nodes.flatMap(typeNode => { var _typeNode$interfaces; - return /* c8 ignore next */(_typeNode$interfaces = typeNode.interfaces) !== null && _typeNode$interfaces !== void 0 ? _typeNode$interfaces : []; + return /* c8 ignore next */( + (_typeNode$interfaces = typeNode.interfaces) !== null && _typeNode$interfaces !== void 0 ? _typeNode$interfaces : [] + ); }).filter(ifaceNode => ifaceNode.name.value === iface.name); } function getUnionMemberTypeNodes(union, typeName) { @@ -34843,11 +34002,13 @@ function getUnionMemberTypeNodes(union, typeName) { astNode, extensionASTNodes } = union; - const nodes = astNode != null ? [astNode, ...extensionASTNodes] : extensionASTNodes; - // FIXME: https://github.com/graphql/graphql-js/issues/2203 + const nodes = astNode != null ? [astNode, ...extensionASTNodes] : extensionASTNodes; // FIXME: https://github.com/graphql/graphql-js/issues/2203 + return nodes.flatMap(unionNode => { var _unionNode$types; - return /* c8 ignore next */(_unionNode$types = unionNode.types) !== null && _unionNode$types !== void 0 ? _unionNode$types : []; + return /* c8 ignore next */( + (_unionNode$types = unionNode.types) !== null && _unionNode$types !== void 0 ? _unionNode$types : [] + ); }).filter(typeNode => typeNode.name.value === typeName); } function getDeprecatedDirectiveNode(definitionNode) { @@ -34874,12 +34035,14 @@ var _ast = __webpack_require__(/*! ../language/ast.mjs */ "../../../node_modules var _kinds = __webpack_require__(/*! ../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); var _visitor = __webpack_require__(/*! ../language/visitor.mjs */ "../../../node_modules/graphql/language/visitor.mjs"); var _definition = __webpack_require__(/*! ../type/definition.mjs */ "../../../node_modules/graphql/type/definition.mjs"); +var _introspection = __webpack_require__(/*! ../type/introspection.mjs */ "../../../node_modules/graphql/type/introspection.mjs"); var _typeFromAST = __webpack_require__(/*! ./typeFromAST.mjs */ "../../../node_modules/graphql/utilities/typeFromAST.mjs"); /** * TypeInfo is a utility class which, given a GraphQL schema, can keep track * of the current field and type definitions at any point in a GraphQL document * AST during a recursive descent by calling `enter(node)` and `leave(node)`. */ + class TypeInfo { constructor(schema, /** @@ -34914,22 +34077,34 @@ class TypeInfo { return 'TypeInfo'; } getType() { - return this._typeStack.at(-1); + if (this._typeStack.length > 0) { + return this._typeStack[this._typeStack.length - 1]; + } } getParentType() { - return this._parentTypeStack.at(-1); + if (this._parentTypeStack.length > 0) { + return this._parentTypeStack[this._parentTypeStack.length - 1]; + } } getInputType() { - return this._inputTypeStack.at(-1); + if (this._inputTypeStack.length > 0) { + return this._inputTypeStack[this._inputTypeStack.length - 1]; + } } getParentInputType() { - return this._inputTypeStack.at(-2); + if (this._inputTypeStack.length > 1) { + return this._inputTypeStack[this._inputTypeStack.length - 2]; + } } getFieldDef() { - return this._fieldDefStack.at(-1); + if (this._fieldDefStack.length > 0) { + return this._fieldDefStack[this._fieldDefStack.length - 1]; + } } getDefaultValue() { - return this._defaultValueStack.at(-1); + if (this._defaultValueStack.length > 0) { + return this._defaultValueStack[this._defaultValueStack.length - 1]; + } } getDirective() { return this._directive; @@ -34941,11 +34116,11 @@ class TypeInfo { return this._enumValue; } enter(node) { - const schema = this._schema; - // Note: many of the types below are explicitly typed as "unknown" to drop + const schema = this._schema; // Note: many of the types below are explicitly typed as "unknown" to drop // any assumptions of a valid schema to ensure runtime types are properly // checked before continuing since TypeInfo is used as part of validation // which occurs before guarantees of schema and document validity. + switch (node.kind) { case _kinds.Kind.SELECTION_SET: { @@ -35011,8 +34186,8 @@ class TypeInfo { case _kinds.Kind.LIST: { const listType = (0, _definition.getNullableType)(this.getInputType()); - const itemType = (0, _definition.isListType)(listType) ? listType.ofType : listType; - // List positions never have a default value. + const itemType = (0, _definition.isListType)(listType) ? listType.ofType : listType; // List positions never have a default value. + this._defaultValueStack.push(undefined); this._inputTypeStack.push((0, _definition.isInputType)(itemType) ? itemType : undefined); break; @@ -35024,7 +34199,7 @@ class TypeInfo { let inputField; if ((0, _definition.isInputObjectType)(objectType)) { inputField = objectType.getFields()[node.name.value]; - if (inputField != null) { + if (inputField) { inputFieldType = inputField.type; } } @@ -35042,8 +34217,7 @@ class TypeInfo { this._enumValue = enumValue; break; } - default: - // Ignore other nodes + default: // Ignore other nodes } } leave(node) { @@ -35079,19 +34253,37 @@ class TypeInfo { case _kinds.Kind.ENUM: this._enumValue = null; break; - default: - // Ignore other nodes + default: // Ignore other nodes } } } + +/** + * Not exactly the same as the executor's definition of getFieldDef, in this + * statically evaluated environment we do not always have an Object type, + * and need to handle Interface and Union types. + */ exports.TypeInfo = TypeInfo; function getFieldDef(schema, parentType, fieldNode) { - return schema.getField(parentType, fieldNode.name.value); + const name = fieldNode.name.value; + if (name === _introspection.SchemaMetaFieldDef.name && schema.getQueryType() === parentType) { + return _introspection.SchemaMetaFieldDef; + } + if (name === _introspection.TypeMetaFieldDef.name && schema.getQueryType() === parentType) { + return _introspection.TypeMetaFieldDef; + } + if (name === _introspection.TypeNameMetaFieldDef.name && (0, _definition.isCompositeType)(parentType)) { + return _introspection.TypeNameMetaFieldDef; + } + if ((0, _definition.isObjectType)(parentType) || (0, _definition.isInterfaceType)(parentType)) { + return parentType.getFields()[name]; + } } /** * Creates a new visitor instance which maintains a provided TypeInfo instance * along with visiting visitor. */ + function visitWithTypeInfo(typeInfo, visitor) { return { enter(...args) { @@ -35124,6 +34316,56 @@ function visitWithTypeInfo(typeInfo, visitor) { /***/ }), +/***/ "../../../node_modules/graphql/utilities/assertValidName.mjs": +/*!*******************************************************************!*\ + !*** ../../../node_modules/graphql/utilities/assertValidName.mjs ***! + \*******************************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.assertValidName = assertValidName; +exports.isValidNameError = isValidNameError; +var _devAssert = __webpack_require__(/*! ../jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); +var _GraphQLError = __webpack_require__(/*! ../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); +var _assertName = __webpack_require__(/*! ../type/assertName.mjs */ "../../../node_modules/graphql/type/assertName.mjs"); +/* c8 ignore start */ + +/** + * Upholds the spec rules about naming. + * @deprecated Please use `assertName` instead. Will be removed in v17 + */ + +function assertValidName(name) { + const error = isValidNameError(name); + if (error) { + throw error; + } + return name; +} +/** + * Returns an Error if a name is invalid. + * @deprecated Please use `assertName` instead. Will be removed in v17 + */ + +function isValidNameError(name) { + typeof name === 'string' || (0, _devAssert.devAssert)(false, 'Expected name to be a string.'); + if (name.startsWith('__')) { + return new _GraphQLError.GraphQLError(`Name "${name}" must not begin with "__", which is reserved by GraphQL introspection.`); + } + try { + (0, _assertName.assertName)(name); + } catch (error) { + return error; + } +} +/* c8 ignore stop */ + +/***/ }), + /***/ "../../../node_modules/graphql/utilities/astFromValue.mjs": /*!****************************************************************!*\ !*** ../../../node_modules/graphql/utilities/astFromValue.mjs ***! @@ -35164,6 +34406,7 @@ var _scalars = __webpack_require__(/*! ../type/scalars.mjs */ "../../../node_mod * | null | NullValue | * */ + function astFromValue(value, type) { if ((0, _definition.isNonNullType)(type)) { const astValue = astFromValue(value, type.ofType); @@ -35171,19 +34414,19 @@ function astFromValue(value, type) { return null; } return astValue; - } - // only explicit null, not undefined, NaN + } // only explicit null, not undefined, NaN + if (value === null) { return { kind: _kinds.Kind.NULL }; - } - // undefined + } // undefined + if (value === undefined) { return null; - } - // Convert JavaScript array to GraphQL list. If the GraphQLType is a list, but + } // Convert JavaScript array to GraphQL list. If the GraphQLType is a list, but // the value is not an array, convert the value using the list's item type. + if ((0, _definition.isListType)(type)) { const itemType = type.ofType; if ((0, _isIterableObject.isIterableObject)(value)) { @@ -35200,9 +34443,9 @@ function astFromValue(value, type) { }; } return astFromValue(value, itemType); - } - // Populate the fields of the input object by creating ASTs from each value + } // Populate the fields of the input object by creating ASTs from each value // in the JavaScript object according to the fields in the input type. + if ((0, _definition.isInputObjectType)(type)) { if (!(0, _isObjectLike.isObjectLike)(value)) { return null; @@ -35232,15 +34475,15 @@ function astFromValue(value, type) { const serialized = type.serialize(value); if (serialized == null) { return null; - } - // Others serialize based on their corresponding JavaScript scalar types. + } // Others serialize based on their corresponding JavaScript scalar types. + if (typeof serialized === 'boolean') { return { kind: _kinds.Kind.BOOLEAN, value: serialized }; - } - // JavaScript numbers can be Int or Float values. + } // JavaScript numbers can be Int or Float values. + if (typeof serialized === 'number' && Number.isFinite(serialized)) { const stringNum = String(serialized); return integerStringRegExp.test(stringNum) ? { @@ -35258,8 +34501,8 @@ function astFromValue(value, type) { kind: _kinds.Kind.ENUM, value: serialized }; - } - // ID types can use Int literals. + } // ID types can use Int literals. + if (type === _scalars.GraphQLID && integerStringRegExp.test(serialized)) { return { kind: _kinds.Kind.INT, @@ -35275,6 +34518,7 @@ function astFromValue(value, type) { } /* c8 ignore next 3 */ // Not reachable, all possible types have been considered. + false || (0, _invariant.invariant)(false, 'Unexpected input type: ' + (0, _inspect.inspect)(type)); } /** @@ -35282,6 +34526,7 @@ function astFromValue(value, type) { * - NegativeSign? 0 * - NegativeSign? NonZeroDigit ( Digit+ )? */ + const integerStringRegExp = /^-?(?:0|[1-9][0-9]*)$/; /***/ }), @@ -35299,6 +34544,8 @@ Object.defineProperty(exports, "__esModule", ({ })); exports.buildASTSchema = buildASTSchema; exports.buildSchema = buildSchema; +var _devAssert = __webpack_require__(/*! ../jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); +var _kinds = __webpack_require__(/*! ../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); var _parser = __webpack_require__(/*! ../language/parser.mjs */ "../../../node_modules/graphql/language/parser.mjs"); var _directives = __webpack_require__(/*! ../type/directives.mjs */ "../../../node_modules/graphql/type/directives.mjs"); var _schema = __webpack_require__(/*! ../type/schema.mjs */ "../../../node_modules/graphql/type/schema.mjs"); @@ -35315,6 +34562,7 @@ var _extendSchema = __webpack_require__(/*! ./extendSchema.mjs */ "../../../node * has no resolve methods, so execution will use default resolvers. */ function buildASTSchema(documentAST, options) { + documentAST != null && documentAST.kind === _kinds.Kind.DOCUMENT || (0, _devAssert.devAssert)(false, 'Must provide valid Document AST.'); if ((options === null || options === void 0 ? void 0 : options.assumeValid) !== true && (options === null || options === void 0 ? void 0 : options.assumeValidSDL) !== true) { (0, _validate.assertValidSDL)(documentAST); } @@ -35360,6 +34608,7 @@ function buildASTSchema(documentAST, options) { * A helper function to build a GraphQLSchema directly from a source * document. */ + function buildSchema(source, options) { const document = (0, _parser.parse)(source, { noLocation: options === null || options === void 0 ? void 0 : options.noLocation, @@ -35408,50 +34657,49 @@ var _valueFromAST = __webpack_require__(/*! ./valueFromAST.mjs */ "../../../node * This function expects a complete introspection result. Don't forget to check * the "errors" field of a server response before calling this function. */ + function buildClientSchema(introspection, options) { - // Even though the `introspection` argument is typed, in most cases it's received - // as an untyped value from the server, so we will do an additional check here. - (0, _isObjectLike.isObjectLike)(introspection) && (0, _isObjectLike.isObjectLike)(introspection.__schema) || (0, _devAssert.devAssert)(false, `Invalid or incomplete introspection result. Ensure that you are passing "data" property of introspection response and no "errors" was returned alongside: ${(0, _inspect.inspect)(introspection)}.`); - // Get the schema from the introspection result. - const schemaIntrospection = introspection.__schema; - // Iterate through all types, getting the type definition for each. - const typeMap = new Map(schemaIntrospection.types.map(typeIntrospection => [typeIntrospection.name, buildType(typeIntrospection)])); - // Include standard types only if they are used. + (0, _isObjectLike.isObjectLike)(introspection) && (0, _isObjectLike.isObjectLike)(introspection.__schema) || (0, _devAssert.devAssert)(false, `Invalid or incomplete introspection result. Ensure that you are passing "data" property of introspection response and no "errors" was returned alongside: ${(0, _inspect.inspect)(introspection)}.`); // Get the schema from the introspection result. + + const schemaIntrospection = introspection.__schema; // Iterate through all types, getting the type definition for each. + + const typeMap = (0, _keyValMap.keyValMap)(schemaIntrospection.types, typeIntrospection => typeIntrospection.name, typeIntrospection => buildType(typeIntrospection)); // Include standard types only if they are used. + for (const stdType of [..._scalars.specifiedScalarTypes, ..._introspection.introspectionTypes]) { - if (typeMap.has(stdType.name)) { - typeMap.set(stdType.name, stdType); + if (typeMap[stdType.name]) { + typeMap[stdType.name] = stdType; } - } - // Get the root Query, Mutation, and Subscription types. - const queryType = schemaIntrospection.queryType != null ? getObjectType(schemaIntrospection.queryType) : null; - const mutationType = schemaIntrospection.mutationType != null ? getObjectType(schemaIntrospection.mutationType) : null; - const subscriptionType = schemaIntrospection.subscriptionType != null ? getObjectType(schemaIntrospection.subscriptionType) : null; - // Get the directives supported by Introspection, assuming empty-set if + } // Get the root Query, Mutation, and Subscription types. + + const queryType = schemaIntrospection.queryType ? getObjectType(schemaIntrospection.queryType) : null; + const mutationType = schemaIntrospection.mutationType ? getObjectType(schemaIntrospection.mutationType) : null; + const subscriptionType = schemaIntrospection.subscriptionType ? getObjectType(schemaIntrospection.subscriptionType) : null; // Get the directives supported by Introspection, assuming empty-set if // directives were not queried for. - const directives = schemaIntrospection.directives != null ? schemaIntrospection.directives.map(buildDirective) : []; - // Then produce and return a Schema with these types. + + const directives = schemaIntrospection.directives ? schemaIntrospection.directives.map(buildDirective) : []; // Then produce and return a Schema with these types. + return new _schema.GraphQLSchema({ description: schemaIntrospection.description, query: queryType, mutation: mutationType, subscription: subscriptionType, - types: [...typeMap.values()], + types: Object.values(typeMap), directives, assumeValid: options === null || options === void 0 ? void 0 : options.assumeValid - }); - // Given a type reference in introspection, return the GraphQLType instance. + }); // Given a type reference in introspection, return the GraphQLType instance. // preferring cached instances before building new instances. + function getType(typeRef) { if (typeRef.kind === _introspection.TypeKind.LIST) { const itemRef = typeRef.ofType; - if (itemRef == null) { + if (!itemRef) { throw new Error('Decorated type deeper than introspection query.'); } return new _definition.GraphQLList(getType(itemRef)); } if (typeRef.kind === _introspection.TypeKind.NON_NULL) { const nullableRef = typeRef.ofType; - if (nullableRef == null) { + if (!nullableRef) { throw new Error('Decorated type deeper than introspection query.'); } const nullableType = getType(nullableRef); @@ -35464,8 +34712,8 @@ function buildClientSchema(introspection, options) { if (!typeName) { throw new Error(`Unknown type reference: ${(0, _inspect.inspect)(typeRef)}.`); } - const type = typeMap.get(typeName); - if (type == null) { + const type = typeMap[typeName]; + if (!type) { throw new Error(`Invalid or incomplete schema, unknown type: ${typeName}. Ensure that a full introspection query is used in order to build a client schema.`); } return type; @@ -35475,9 +34723,9 @@ function buildClientSchema(introspection, options) { } function getInterfaceType(typeRef) { return (0, _definition.assertInterfaceType)(getNamedType(typeRef)); - } - // Given a type's introspection result, construct the correct + } // Given a type's introspection result, construct the correct // GraphQLType instance. + function buildType(type) { // eslint-disable-next-line @typescript-eslint/prefer-optional-chain if (type != null && type.name != null && type.kind != null) { @@ -35514,7 +34762,7 @@ function buildClientSchema(introspection, options) { if (implementingIntrospection.interfaces === null && implementingIntrospection.kind === _introspection.TypeKind.INTERFACE) { return []; } - if (implementingIntrospection.interfaces == null) { + if (!implementingIntrospection.interfaces) { const implementingIntrospectionStr = (0, _inspect.inspect)(implementingIntrospection); throw new Error(`Introspection result missing interfaces: ${implementingIntrospectionStr}.`); } @@ -35537,7 +34785,7 @@ function buildClientSchema(introspection, options) { }); } function buildUnionDef(unionIntrospection) { - if (unionIntrospection.possibleTypes == null) { + if (!unionIntrospection.possibleTypes) { const unionIntrospectionStr = (0, _inspect.inspect)(unionIntrospection); throw new Error(`Introspection result missing possibleTypes: ${unionIntrospectionStr}.`); } @@ -35548,7 +34796,7 @@ function buildClientSchema(introspection, options) { }); } function buildEnumDef(enumIntrospection) { - if (enumIntrospection.enumValues == null) { + if (!enumIntrospection.enumValues) { const enumIntrospectionStr = (0, _inspect.inspect)(enumIntrospection); throw new Error(`Introspection result missing enumValues: ${enumIntrospectionStr}.`); } @@ -35562,7 +34810,7 @@ function buildClientSchema(introspection, options) { }); } function buildInputObjectDef(inputObjectIntrospection) { - if (inputObjectIntrospection.inputFields == null) { + if (!inputObjectIntrospection.inputFields) { const inputObjectIntrospectionStr = (0, _inspect.inspect)(inputObjectIntrospection); throw new Error(`Introspection result missing inputFields: ${inputObjectIntrospectionStr}.`); } @@ -35574,7 +34822,7 @@ function buildClientSchema(introspection, options) { }); } function buildFieldDefMap(typeIntrospection) { - if (typeIntrospection.fields == null) { + if (!typeIntrospection.fields) { throw new Error(`Introspection result missing fields: ${(0, _inspect.inspect)(typeIntrospection)}.`); } return (0, _keyValMap.keyValMap)(typeIntrospection.fields, fieldIntrospection => fieldIntrospection.name, buildField); @@ -35585,7 +34833,7 @@ function buildClientSchema(introspection, options) { const typeStr = (0, _inspect.inspect)(type); throw new Error(`Introspection must provide output type for fields, but received: ${typeStr}.`); } - if (fieldIntrospection.args == null) { + if (!fieldIntrospection.args) { const fieldIntrospectionStr = (0, _inspect.inspect)(fieldIntrospection); throw new Error(`Introspection result missing field args: ${fieldIntrospectionStr}.`); } @@ -35614,11 +34862,11 @@ function buildClientSchema(introspection, options) { }; } function buildDirective(directiveIntrospection) { - if (directiveIntrospection.args == null) { + if (!directiveIntrospection.args) { const directiveIntrospectionStr = (0, _inspect.inspect)(directiveIntrospection); throw new Error(`Introspection result missing directive args: ${directiveIntrospectionStr}.`); } - if (directiveIntrospection.locations == null) { + if (!directiveIntrospection.locations) { const directiveIntrospectionStr = (0, _inspect.inspect)(directiveIntrospection); throw new Error(`Introspection result missing directive locations: ${directiveIntrospectionStr}.`); } @@ -35689,8 +34937,8 @@ function coerceInputValueImpl(inputValue, type, onError, path) { const itemPath = (0, _Path.addPath)(path, index, undefined); return coerceInputValueImpl(itemValue, itemType, onError, itemPath); }); - } - // Lists accept a non-list value as a list of one. + } // Lists accept a non-list value as a list of one. + return [coerceInputValueImpl(inputValue, itemType, onError, path)]; } if ((0, _definition.isInputObjectType)(type)) { @@ -35712,10 +34960,10 @@ function coerceInputValueImpl(inputValue, type, onError, path) { continue; } coercedValue[field.name] = coerceInputValueImpl(fieldValue, field.type, onError, (0, _Path.addPath)(path, field.name, type.name)); - } - // Ensure every provided field is defined. + } // Ensure every provided field is defined. + for (const fieldName of Object.keys(inputValue)) { - if (fieldDefs[fieldName] == null) { + if (!fieldDefs[fieldName]) { const suggestions = (0, _suggestionList.suggestionList)(fieldName, Object.keys(type.getFields())); onError((0, _Path.pathToArray)(path), inputValue, new _GraphQLError.GraphQLError(`Field "${fieldName}" is not defined by type "${type.name}".` + (0, _didYouMean.didYouMean)(suggestions))); } @@ -35734,10 +34982,10 @@ function coerceInputValueImpl(inputValue, type, onError, path) { return coercedValue; } if ((0, _definition.isLeafType)(type)) { - let parseResult; - // Scalars and Enums determine if an input value is valid via parseValue(), + let parseResult; // Scalars and Enums determine if a input value is valid via parseValue(), // which can throw to indicate failure. If it throws, maintain a reference // to the original error. + try { parseResult = type.parseValue(inputValue); } catch (error) { @@ -35757,6 +35005,7 @@ function coerceInputValueImpl(inputValue, type, onError, path) { } /* c8 ignore next 3 */ // Not reachable, all possible types have been considered. + false || (0, _invariant.invariant)(false, 'Unexpected input type: ' + (0, _inspect.inspect)(type)); } @@ -35780,6 +35029,7 @@ var _kinds = __webpack_require__(/*! ../language/kinds.mjs */ "../../../node_mod * concatenate the ASTs together into batched AST, useful for validating many * GraphQL source files which together represent one conceptual application. */ + function concatAST(documents) { const definitions = []; for (const doc of documents) { @@ -35806,11 +35056,13 @@ Object.defineProperty(exports, "__esModule", ({ })); exports.extendSchema = extendSchema; exports.extendSchemaImpl = extendSchemaImpl; -var _AccumulatorMap = __webpack_require__(/*! ../jsutils/AccumulatorMap.mjs */ "../../../node_modules/graphql/jsutils/AccumulatorMap.mjs"); +var _devAssert = __webpack_require__(/*! ../jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); var _invariant = __webpack_require__(/*! ../jsutils/invariant.mjs */ "../../../node_modules/graphql/jsutils/invariant.mjs"); +var _keyMap = __webpack_require__(/*! ../jsutils/keyMap.mjs */ "../../../node_modules/graphql/jsutils/keyMap.mjs"); var _mapValue = __webpack_require__(/*! ../jsutils/mapValue.mjs */ "../../../node_modules/graphql/jsutils/mapValue.mjs"); var _kinds = __webpack_require__(/*! ../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); +var _predicates = __webpack_require__(/*! ../language/predicates.mjs */ "../../../node_modules/graphql/language/predicates.mjs"); var _definition = __webpack_require__(/*! ../type/definition.mjs */ "../../../node_modules/graphql/type/definition.mjs"); var _directives = __webpack_require__(/*! ../type/directives.mjs */ "../../../node_modules/graphql/type/directives.mjs"); var _introspection = __webpack_require__(/*! ../type/introspection.mjs */ "../../../node_modules/graphql/type/introspection.mjs"); @@ -35833,6 +35085,7 @@ var _valueFromAST = __webpack_require__(/*! ./valueFromAST.mjs */ "../../../node */ function extendSchema(schema, documentAST, options) { (0, _schema.assertSchema)(schema); + documentAST != null && documentAST.kind === _kinds.Kind.DOCUMENT || (0, _devAssert.devAssert)(false, 'Must provide valid Document AST.'); if ((options === null || options === void 0 ? void 0 : options.assumeValid) !== true && (options === null || options === void 0 ? void 0 : options.assumeValidSDL) !== true) { (0, _validate.assertValidSDLExtension)(documentAST, schema); } @@ -35843,77 +35096,47 @@ function extendSchema(schema, documentAST, options) { /** * @internal */ + function extendSchemaImpl(schemaConfig, documentAST, options) { - var _schemaDef$descriptio, _schemaDef, _schemaDef$descriptio2, _schemaDef2, _options$assumeValid; + var _schemaDef, _schemaDef$descriptio, _schemaDef2, _options$assumeValid; + // Collect the type definitions and extensions found in the document. const typeDefs = []; - const scalarExtensions = new _AccumulatorMap.AccumulatorMap(); - const objectExtensions = new _AccumulatorMap.AccumulatorMap(); - const interfaceExtensions = new _AccumulatorMap.AccumulatorMap(); - const unionExtensions = new _AccumulatorMap.AccumulatorMap(); - const enumExtensions = new _AccumulatorMap.AccumulatorMap(); - const inputObjectExtensions = new _AccumulatorMap.AccumulatorMap(); - // New directives and types are separate because a directives and types can + const typeExtensionsMap = Object.create(null); // New directives and types are separate because a directives and types can // have the same name. For example, a type named "skip". + const directiveDefs = []; - let schemaDef; - // Schema extensions are collected which may add additional operation types. + let schemaDef; // Schema extensions are collected which may add additional operation types. + const schemaExtensions = []; - let isSchemaChanged = false; for (const def of documentAST.definitions) { - switch (def.kind) { - case _kinds.Kind.SCHEMA_DEFINITION: - schemaDef = def; - break; - case _kinds.Kind.SCHEMA_EXTENSION: - schemaExtensions.push(def); - break; - case _kinds.Kind.DIRECTIVE_DEFINITION: - directiveDefs.push(def); - break; - // Type Definitions - case _kinds.Kind.SCALAR_TYPE_DEFINITION: - case _kinds.Kind.OBJECT_TYPE_DEFINITION: - case _kinds.Kind.INTERFACE_TYPE_DEFINITION: - case _kinds.Kind.UNION_TYPE_DEFINITION: - case _kinds.Kind.ENUM_TYPE_DEFINITION: - case _kinds.Kind.INPUT_OBJECT_TYPE_DEFINITION: - typeDefs.push(def); - break; - // Type System Extensions - case _kinds.Kind.SCALAR_TYPE_EXTENSION: - scalarExtensions.add(def.name.value, def); - break; - case _kinds.Kind.OBJECT_TYPE_EXTENSION: - objectExtensions.add(def.name.value, def); - break; - case _kinds.Kind.INTERFACE_TYPE_EXTENSION: - interfaceExtensions.add(def.name.value, def); - break; - case _kinds.Kind.UNION_TYPE_EXTENSION: - unionExtensions.add(def.name.value, def); - break; - case _kinds.Kind.ENUM_TYPE_EXTENSION: - enumExtensions.add(def.name.value, def); - break; - case _kinds.Kind.INPUT_OBJECT_TYPE_EXTENSION: - inputObjectExtensions.add(def.name.value, def); - break; - default: - continue; + if (def.kind === _kinds.Kind.SCHEMA_DEFINITION) { + schemaDef = def; + } else if (def.kind === _kinds.Kind.SCHEMA_EXTENSION) { + schemaExtensions.push(def); + } else if ((0, _predicates.isTypeDefinitionNode)(def)) { + typeDefs.push(def); + } else if ((0, _predicates.isTypeExtensionNode)(def)) { + const extendedTypeName = def.name.value; + const existingTypeExtensions = typeExtensionsMap[extendedTypeName]; + typeExtensionsMap[extendedTypeName] = existingTypeExtensions ? existingTypeExtensions.concat([def]) : [def]; + } else if (def.kind === _kinds.Kind.DIRECTIVE_DEFINITION) { + directiveDefs.push(def); } - isSchemaChanged = true; - } - // If this document contains no new types, extensions, or directives then + } // If this document contains no new types, extensions, or directives then // return the same unmodified GraphQLSchema instance. - if (!isSchemaChanged) { + + if (Object.keys(typeExtensionsMap).length === 0 && typeDefs.length === 0 && directiveDefs.length === 0 && schemaExtensions.length === 0 && schemaDef == null) { return schemaConfig; } - const typeMap = new Map(schemaConfig.types.map(type => [type.name, extendNamedType(type)])); + const typeMap = Object.create(null); + for (const existingType of schemaConfig.types) { + typeMap[existingType.name] = extendNamedType(existingType); + } for (const typeNode of typeDefs) { - var _stdTypeMap$get; + var _stdTypeMap$name; const name = typeNode.name.value; - typeMap.set(name, (_stdTypeMap$get = stdTypeMap.get(name)) !== null && _stdTypeMap$get !== void 0 ? _stdTypeMap$get : buildType(typeNode)); + typeMap[name] = (_stdTypeMap$name = stdTypeMap[name]) !== null && _stdTypeMap$name !== void 0 ? _stdTypeMap$name : buildType(typeNode); } const operationTypes = { // Get the extended root operation types. @@ -35923,20 +35146,20 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { // Then, incorporate schema definition and all schema extensions. ...(schemaDef && getOperationTypes([schemaDef])), ...getOperationTypes(schemaExtensions) - }; - // Then produce and return a Schema config with these types. + }; // Then produce and return a Schema config with these types. + return { - description: (_schemaDef$descriptio = (_schemaDef = schemaDef) === null || _schemaDef === void 0 ? void 0 : (_schemaDef$descriptio2 = _schemaDef.description) === null || _schemaDef$descriptio2 === void 0 ? void 0 : _schemaDef$descriptio2.value) !== null && _schemaDef$descriptio !== void 0 ? _schemaDef$descriptio : schemaConfig.description, + description: (_schemaDef = schemaDef) === null || _schemaDef === void 0 ? void 0 : (_schemaDef$descriptio = _schemaDef.description) === null || _schemaDef$descriptio === void 0 ? void 0 : _schemaDef$descriptio.value, ...operationTypes, - types: Array.from(typeMap.values()), + types: Object.values(typeMap), directives: [...schemaConfig.directives.map(replaceDirective), ...directiveDefs.map(buildDirective)], - extensions: schemaConfig.extensions, + extensions: Object.create(null), astNode: (_schemaDef2 = schemaDef) !== null && _schemaDef2 !== void 0 ? _schemaDef2 : schemaConfig.astNode, extensionASTNodes: schemaConfig.extensionASTNodes.concat(schemaExtensions), assumeValid: (_options$assumeValid = options === null || options === void 0 ? void 0 : options.assumeValid) !== null && _options$assumeValid !== void 0 ? _options$assumeValid : false - }; - // Below are functions used for producing this schema that have closed over + }; // Below are functions used for producing this schema that have closed over // this scope and have access to the schema, cache, and newly defined types. + function replaceType(type) { if ((0, _definition.isListType)(type)) { // @ts-expect-error @@ -35945,21 +35168,17 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { if ((0, _definition.isNonNullType)(type)) { // @ts-expect-error return new _definition.GraphQLNonNull(replaceType(type.ofType)); - } - // @ts-expect-error FIXME + } // @ts-expect-error FIXME + return replaceNamedType(type); } function replaceNamedType(type) { // Note: While this could make early assertions to get the correctly // typed values, that would throw immediately while type system // validation with validateSchema() will produce more actionable results. - return typeMap.get(type.name); + return typeMap[type.name]; } function replaceDirective(directive) { - if ((0, _directives.isSpecifiedDirective)(directive)) { - // Builtin directives are not extended. - return directive; - } const config = directive.toConfig(); return new _directives.GraphQLDirective({ ...config, @@ -35991,12 +35210,13 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { } /* c8 ignore next 3 */ // Not reachable, all possible type definition nodes have been considered. + false || (0, _invariant.invariant)(false, 'Unexpected type: ' + (0, _inspect.inspect)(type)); } function extendInputObjectType(type) { - var _inputObjectExtension; + var _typeExtensionsMap$co; const config = type.toConfig(); - const extensions = (_inputObjectExtension = inputObjectExtensions.get(config.name)) !== null && _inputObjectExtension !== void 0 ? _inputObjectExtension : []; + const extensions = (_typeExtensionsMap$co = typeExtensionsMap[config.name]) !== null && _typeExtensionsMap$co !== void 0 ? _typeExtensionsMap$co : []; return new _definition.GraphQLInputObjectType({ ...config, fields: () => ({ @@ -36010,9 +35230,9 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { }); } function extendEnumType(type) { - var _enumExtensions$get; + var _typeExtensionsMap$ty; const config = type.toConfig(); - const extensions = (_enumExtensions$get = enumExtensions.get(type.name)) !== null && _enumExtensions$get !== void 0 ? _enumExtensions$get : []; + const extensions = (_typeExtensionsMap$ty = typeExtensionsMap[type.name]) !== null && _typeExtensionsMap$ty !== void 0 ? _typeExtensionsMap$ty : []; return new _definition.GraphQLEnumType({ ...config, values: { @@ -36023,9 +35243,9 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { }); } function extendScalarType(type) { - var _scalarExtensions$get; + var _typeExtensionsMap$co2; const config = type.toConfig(); - const extensions = (_scalarExtensions$get = scalarExtensions.get(config.name)) !== null && _scalarExtensions$get !== void 0 ? _scalarExtensions$get : []; + const extensions = (_typeExtensionsMap$co2 = typeExtensionsMap[config.name]) !== null && _typeExtensionsMap$co2 !== void 0 ? _typeExtensionsMap$co2 : []; let specifiedByURL = config.specifiedByURL; for (const extensionNode of extensions) { var _getSpecifiedByURL; @@ -36038,9 +35258,9 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { }); } function extendObjectType(type) { - var _objectExtensions$get; + var _typeExtensionsMap$co3; const config = type.toConfig(); - const extensions = (_objectExtensions$get = objectExtensions.get(config.name)) !== null && _objectExtensions$get !== void 0 ? _objectExtensions$get : []; + const extensions = (_typeExtensionsMap$co3 = typeExtensionsMap[config.name]) !== null && _typeExtensionsMap$co3 !== void 0 ? _typeExtensionsMap$co3 : []; return new _definition.GraphQLObjectType({ ...config, interfaces: () => [...type.getInterfaces().map(replaceNamedType), ...buildInterfaces(extensions)], @@ -36052,9 +35272,9 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { }); } function extendInterfaceType(type) { - var _interfaceExtensions$; + var _typeExtensionsMap$co4; const config = type.toConfig(); - const extensions = (_interfaceExtensions$ = interfaceExtensions.get(config.name)) !== null && _interfaceExtensions$ !== void 0 ? _interfaceExtensions$ : []; + const extensions = (_typeExtensionsMap$co4 = typeExtensionsMap[config.name]) !== null && _typeExtensionsMap$co4 !== void 0 ? _typeExtensionsMap$co4 : []; return new _definition.GraphQLInterfaceType({ ...config, interfaces: () => [...type.getInterfaces().map(replaceNamedType), ...buildInterfaces(extensions)], @@ -36066,9 +35286,9 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { }); } function extendUnionType(type) { - var _unionExtensions$get; + var _typeExtensionsMap$co5; const config = type.toConfig(); - const extensions = (_unionExtensions$get = unionExtensions.get(config.name)) !== null && _unionExtensions$get !== void 0 ? _unionExtensions$get : []; + const extensions = (_typeExtensionsMap$co5 = typeExtensionsMap[config.name]) !== null && _typeExtensionsMap$co5 !== void 0 ? _typeExtensionsMap$co5 : []; return new _definition.GraphQLUnionType({ ...config, types: () => [...type.getTypes().map(replaceNamedType), ...buildUnionTypes(extensions)], @@ -36092,8 +35312,10 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { const opTypes = {}; for (const node of nodes) { var _node$operationTypes; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 - const operationTypesNodes = /* c8 ignore next */(_node$operationTypes = node.operationTypes) !== null && _node$operationTypes !== void 0 ? _node$operationTypes : []; + const operationTypesNodes = /* c8 ignore next */ + (_node$operationTypes = node.operationTypes) !== null && _node$operationTypes !== void 0 ? _node$operationTypes : []; for (const operationType of operationTypesNodes) { // Note: While this could make early assertions to get the correctly // typed values below, that would throw immediately while type system @@ -36105,9 +35327,9 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { return opTypes; } function getNamedType(node) { - var _stdTypeMap$get2; + var _stdTypeMap$name2; const name = node.name.value; - const type = (_stdTypeMap$get2 = stdTypeMap.get(name)) !== null && _stdTypeMap$get2 !== void 0 ? _stdTypeMap$get2 : typeMap.get(name); + const type = (_stdTypeMap$name2 = stdTypeMap[name]) !== null && _stdTypeMap$name2 !== void 0 ? _stdTypeMap$name2 : typeMap[name]; if (type === undefined) { throw new Error(`Unknown type: "${name}".`); } @@ -36140,8 +35362,10 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { const fieldConfigMap = Object.create(null); for (const node of nodes) { var _node$fields; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 - const nodeFields = /* c8 ignore next */(_node$fields = node.fields) !== null && _node$fields !== void 0 ? _node$fields : []; + const nodeFields = /* c8 ignore next */ + (_node$fields = node.fields) !== null && _node$fields !== void 0 ? _node$fields : []; for (const field of nodeFields) { var _field$description; fieldConfigMap[field.name.value] = { @@ -36160,10 +35384,12 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { } function buildArgumentMap(args) { // FIXME: https://github.com/graphql/graphql-js/issues/2203 - const argsNodes = /* c8 ignore next */args !== null && args !== void 0 ? args : []; + const argsNodes = /* c8 ignore next */ + args !== null && args !== void 0 ? args : []; const argConfigMap = Object.create(null); for (const arg of argsNodes) { var _arg$description; + // Note: While this could make assertions to get the correctly typed // value, that would throw immediately while type system validation // with validateSchema() will produce more actionable results. @@ -36182,10 +35408,13 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { const inputFieldMap = Object.create(null); for (const node of nodes) { var _node$fields2; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 - const fieldsNodes = /* c8 ignore next */(_node$fields2 = node.fields) !== null && _node$fields2 !== void 0 ? _node$fields2 : []; + const fieldsNodes = /* c8 ignore next */ + (_node$fields2 = node.fields) !== null && _node$fields2 !== void 0 ? _node$fields2 : []; for (const field of fieldsNodes) { var _field$description2; + // Note: While this could make assertions to get the correctly typed // value, that would throw immediately while type system validation // with validateSchema() will produce more actionable results. @@ -36205,8 +35434,10 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { const enumValueMap = Object.create(null); for (const node of nodes) { var _node$values; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 - const valuesNodes = /* c8 ignore next */(_node$values = node.values) !== null && _node$values !== void 0 ? _node$values : []; + const valuesNodes = /* c8 ignore next */ + (_node$values = node.values) !== null && _node$values !== void 0 ? _node$values : []; for (const value of valuesNodes) { var _value$description; enumValueMap[value.name.value] = { @@ -36227,7 +35458,9 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { // FIXME: https://github.com/graphql/graphql-js/issues/2203 node => { var _node$interfaces$map, _node$interfaces; - return /* c8 ignore next */(_node$interfaces$map = (_node$interfaces = node.interfaces) === null || _node$interfaces === void 0 ? void 0 : _node$interfaces.map(getNamedType)) !== null && _node$interfaces$map !== void 0 ? _node$interfaces$map : []; + return /* c8 ignore next */( + (_node$interfaces$map = (_node$interfaces = node.interfaces) === null || _node$interfaces === void 0 ? void 0 : _node$interfaces.map(getNamedType)) !== null && _node$interfaces$map !== void 0 ? _node$interfaces$map : [] + ); }); } function buildUnionTypes(nodes) { @@ -36239,16 +35472,19 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { // FIXME: https://github.com/graphql/graphql-js/issues/2203 node => { var _node$types$map, _node$types; - return /* c8 ignore next */(_node$types$map = (_node$types = node.types) === null || _node$types === void 0 ? void 0 : _node$types.map(getNamedType)) !== null && _node$types$map !== void 0 ? _node$types$map : []; + return /* c8 ignore next */( + (_node$types$map = (_node$types = node.types) === null || _node$types === void 0 ? void 0 : _node$types.map(getNamedType)) !== null && _node$types$map !== void 0 ? _node$types$map : [] + ); }); } function buildType(astNode) { + var _typeExtensionsMap$na; const name = astNode.name.value; + const extensionASTNodes = (_typeExtensionsMap$na = typeExtensionsMap[name]) !== null && _typeExtensionsMap$na !== void 0 ? _typeExtensionsMap$na : []; switch (astNode.kind) { case _kinds.Kind.OBJECT_TYPE_DEFINITION: { - var _objectExtensions$get2, _astNode$description; - const extensionASTNodes = (_objectExtensions$get2 = objectExtensions.get(name)) !== null && _objectExtensions$get2 !== void 0 ? _objectExtensions$get2 : []; + var _astNode$description; const allNodes = [astNode, ...extensionASTNodes]; return new _definition.GraphQLObjectType({ name, @@ -36261,8 +35497,7 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { } case _kinds.Kind.INTERFACE_TYPE_DEFINITION: { - var _interfaceExtensions$2, _astNode$description2; - const extensionASTNodes = (_interfaceExtensions$2 = interfaceExtensions.get(name)) !== null && _interfaceExtensions$2 !== void 0 ? _interfaceExtensions$2 : []; + var _astNode$description2; const allNodes = [astNode, ...extensionASTNodes]; return new _definition.GraphQLInterfaceType({ name, @@ -36275,8 +35510,7 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { } case _kinds.Kind.ENUM_TYPE_DEFINITION: { - var _enumExtensions$get2, _astNode$description3; - const extensionASTNodes = (_enumExtensions$get2 = enumExtensions.get(name)) !== null && _enumExtensions$get2 !== void 0 ? _enumExtensions$get2 : []; + var _astNode$description3; const allNodes = [astNode, ...extensionASTNodes]; return new _definition.GraphQLEnumType({ name, @@ -36288,8 +35522,7 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { } case _kinds.Kind.UNION_TYPE_DEFINITION: { - var _unionExtensions$get2, _astNode$description4; - const extensionASTNodes = (_unionExtensions$get2 = unionExtensions.get(name)) !== null && _unionExtensions$get2 !== void 0 ? _unionExtensions$get2 : []; + var _astNode$description4; const allNodes = [astNode, ...extensionASTNodes]; return new _definition.GraphQLUnionType({ name, @@ -36301,8 +35534,7 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { } case _kinds.Kind.SCALAR_TYPE_DEFINITION: { - var _scalarExtensions$get2, _astNode$description5; - const extensionASTNodes = (_scalarExtensions$get2 = scalarExtensions.get(name)) !== null && _scalarExtensions$get2 !== void 0 ? _scalarExtensions$get2 : []; + var _astNode$description5; return new _definition.GraphQLScalarType({ name, description: (_astNode$description5 = astNode.description) === null || _astNode$description5 === void 0 ? void 0 : _astNode$description5.value, @@ -36313,8 +35545,7 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { } case _kinds.Kind.INPUT_OBJECT_TYPE_DEFINITION: { - var _inputObjectExtension2, _astNode$description6; - const extensionASTNodes = (_inputObjectExtension2 = inputObjectExtensions.get(name)) !== null && _inputObjectExtension2 !== void 0 ? _inputObjectExtension2 : []; + var _astNode$description6; const allNodes = [astNode, ...extensionASTNodes]; return new _definition.GraphQLInputObjectType({ name, @@ -36328,27 +35559,30 @@ function extendSchemaImpl(schemaConfig, documentAST, options) { } } } -const stdTypeMap = new Map([..._scalars.specifiedScalarTypes, ..._introspection.introspectionTypes].map(type => [type.name, type])); +const stdTypeMap = (0, _keyMap.keyMap)([..._scalars.specifiedScalarTypes, ..._introspection.introspectionTypes], type => type.name); /** * Given a field or enum value node, returns the string value for the * deprecation reason. */ + function getDeprecationReason(node) { - const deprecated = (0, _values.getDirectiveValues)(_directives.GraphQLDeprecatedDirective, node); - // @ts-expect-error validated by `getDirectiveValues` + const deprecated = (0, _values.getDirectiveValues)(_directives.GraphQLDeprecatedDirective, node); // @ts-expect-error validated by `getDirectiveValues` + return deprecated === null || deprecated === void 0 ? void 0 : deprecated.reason; } /** * Given a scalar node, returns the string value for the specifiedByURL. */ + function getSpecifiedByURL(node) { - const specifiedBy = (0, _values.getDirectiveValues)(_directives.GraphQLSpecifiedByDirective, node); - // @ts-expect-error validated by `getDirectiveValues` + const specifiedBy = (0, _values.getDirectiveValues)(_directives.GraphQLSpecifiedByDirective, node); // @ts-expect-error validated by `getDirectiveValues` + return specifiedBy === null || specifiedBy === void 0 ? void 0 : specifiedBy.url; } /** * Given an input object node, returns if the node should be OneOf. */ + function isOneOf(node) { return Boolean((0, _values.getDirectiveValues)(_directives.GraphQLOneOfDirective, node)); } @@ -36417,6 +35651,7 @@ function findBreakingChanges(oldSchema, newSchema) { * Given two schemas, returns an Array containing descriptions of all the types * of potentially dangerous changes covered by the other functions down below. */ + function findDangerousChanges(oldSchema, newSchema) { // @ts-expect-error return findSchemaChanges(oldSchema, newSchema).filter(change => change.type in DangerousChangeType); @@ -36685,8 +35920,8 @@ function isChangeSafeForInputObjectFieldOrFieldArg(oldType, newType) { // moving from non-null to nullable of the same underlying type is safe !(0, _definition.isNonNullType)(newType) && isChangeSafeForInputObjectFieldOrFieldArg(oldType.ofType, newType) ); - } - // if they're both named types, see if their names are equivalent + } // if they're both named types, see if their names are equivalent + return (0, _definition.isNamedType)(newType) && oldType.name === newType.name; } function typeKindName(type) { @@ -36710,6 +35945,7 @@ function typeKindName(type) { } /* c8 ignore next 3 */ // Not reachable, all possible types have been considered. + false || (0, _invariant.invariant)(false, 'Unexpected type: ' + (0, _inspect.inspect)(type)); } function stringifyValue(value, type) { @@ -36912,6 +36148,7 @@ var _kinds = __webpack_require__(/*! ../language/kinds.mjs */ "../../../node_mod * name. If a name is not provided, an operation is only returned if only one is * provided in the document. */ + function getOperationAST(documentAST, operationName) { let operation = null; for (const definition of documentAST.definitions) { @@ -36935,6 +36172,59 @@ function getOperationAST(documentAST, operationName) { /***/ }), +/***/ "../../../node_modules/graphql/utilities/getOperationRootType.mjs": +/*!************************************************************************!*\ + !*** ../../../node_modules/graphql/utilities/getOperationRootType.mjs ***! + \************************************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.getOperationRootType = getOperationRootType; +var _GraphQLError = __webpack_require__(/*! ../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); +/** + * Extracts the root type of the operation from the schema. + * + * @deprecated Please use `GraphQLSchema.getRootType` instead. Will be removed in v17 + */ +function getOperationRootType(schema, operation) { + if (operation.operation === 'query') { + const queryType = schema.getQueryType(); + if (!queryType) { + throw new _GraphQLError.GraphQLError('Schema does not define the required query root type.', { + nodes: operation + }); + } + return queryType; + } + if (operation.operation === 'mutation') { + const mutationType = schema.getMutationType(); + if (!mutationType) { + throw new _GraphQLError.GraphQLError('Schema is not configured for mutations.', { + nodes: operation + }); + } + return mutationType; + } + if (operation.operation === 'subscription') { + const subscriptionType = schema.getSubscriptionType(); + if (!subscriptionType) { + throw new _GraphQLError.GraphQLError('Schema is not configured for subscriptions.', { + nodes: operation + }); + } + return subscriptionType; + } + throw new _GraphQLError.GraphQLError('Can only have query, mutation and subscription operations.', { + nodes: operation + }); +} + +/***/ }), + /***/ "../../../node_modules/graphql/utilities/index.mjs": /*!*********************************************************!*\ !*** ../../../node_modules/graphql/utilities/index.mjs ***! @@ -36964,6 +36254,12 @@ Object.defineProperty(exports, "TypeInfo", ({ return _TypeInfo.TypeInfo; } })); +Object.defineProperty(exports, "assertValidName", ({ + enumerable: true, + get: function () { + return _assertValidName.assertValidName; + } +})); Object.defineProperty(exports, "astFromValue", ({ enumerable: true, get: function () { @@ -37036,6 +36332,12 @@ Object.defineProperty(exports, "getOperationAST", ({ return _getOperationAST.getOperationAST; } })); +Object.defineProperty(exports, "getOperationRootType", ({ + enumerable: true, + get: function () { + return _getOperationRootType.getOperationRootType; + } +})); Object.defineProperty(exports, "introspectionFromSchema", ({ enumerable: true, get: function () { @@ -37054,18 +36356,18 @@ Object.defineProperty(exports, "isTypeSubTypeOf", ({ return _typeComparators.isTypeSubTypeOf; } })); +Object.defineProperty(exports, "isValidNameError", ({ + enumerable: true, + get: function () { + return _assertValidName.isValidNameError; + } +})); Object.defineProperty(exports, "lexicographicSortSchema", ({ enumerable: true, get: function () { return _lexicographicSortSchema.lexicographicSortSchema; } })); -Object.defineProperty(exports, "printDirective", ({ - enumerable: true, - get: function () { - return _printSchema.printDirective; - } -})); Object.defineProperty(exports, "printIntrospectionSchema", ({ enumerable: true, get: function () { @@ -37122,6 +36424,7 @@ Object.defineProperty(exports, "visitWithTypeInfo", ({ })); var _getIntrospectionQuery = __webpack_require__(/*! ./getIntrospectionQuery.mjs */ "../../../node_modules/graphql/utilities/getIntrospectionQuery.mjs"); var _getOperationAST = __webpack_require__(/*! ./getOperationAST.mjs */ "../../../node_modules/graphql/utilities/getOperationAST.mjs"); +var _getOperationRootType = __webpack_require__(/*! ./getOperationRootType.mjs */ "../../../node_modules/graphql/utilities/getOperationRootType.mjs"); var _introspectionFromSchema = __webpack_require__(/*! ./introspectionFromSchema.mjs */ "../../../node_modules/graphql/utilities/introspectionFromSchema.mjs"); var _buildClientSchema = __webpack_require__(/*! ./buildClientSchema.mjs */ "../../../node_modules/graphql/utilities/buildClientSchema.mjs"); var _buildASTSchema = __webpack_require__(/*! ./buildASTSchema.mjs */ "../../../node_modules/graphql/utilities/buildASTSchema.mjs"); @@ -37138,6 +36441,7 @@ var _concatAST = __webpack_require__(/*! ./concatAST.mjs */ "../../../node_modul var _separateOperations = __webpack_require__(/*! ./separateOperations.mjs */ "../../../node_modules/graphql/utilities/separateOperations.mjs"); var _stripIgnoredCharacters = __webpack_require__(/*! ./stripIgnoredCharacters.mjs */ "../../../node_modules/graphql/utilities/stripIgnoredCharacters.mjs"); var _typeComparators = __webpack_require__(/*! ./typeComparators.mjs */ "../../../node_modules/graphql/utilities/typeComparators.mjs"); +var _assertValidName = __webpack_require__(/*! ./assertValidName.mjs */ "../../../node_modules/graphql/utilities/assertValidName.mjs"); var _findBreakingChanges = __webpack_require__(/*! ./findBreakingChanges.mjs */ "../../../node_modules/graphql/utilities/findBreakingChanges.mjs"); /***/ }), @@ -37167,6 +36471,7 @@ var _getIntrospectionQuery = __webpack_require__(/*! ./getIntrospectionQuery.mjs * This is the inverse of buildClientSchema. The primary use case is outside * of the server context, for instance when doing schema comparisons. */ + function introspectionFromSchema(schema, options) { const optionsWithDefaults = { specifiedByUrl: true, @@ -37181,7 +36486,7 @@ function introspectionFromSchema(schema, options) { schema, document }); - result.errors == null && result.data != null || (0, _invariant.invariant)(false); + !result.errors && result.data || (0, _invariant.invariant)(false); return result.data; } @@ -37201,6 +36506,7 @@ Object.defineProperty(exports, "__esModule", ({ exports.lexicographicSortSchema = lexicographicSortSchema; var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); var _invariant = __webpack_require__(/*! ../jsutils/invariant.mjs */ "../../../node_modules/graphql/jsutils/invariant.mjs"); +var _keyValMap = __webpack_require__(/*! ../jsutils/keyValMap.mjs */ "../../../node_modules/graphql/jsutils/keyValMap.mjs"); var _naturalCompare = __webpack_require__(/*! ../jsutils/naturalCompare.mjs */ "../../../node_modules/graphql/jsutils/naturalCompare.mjs"); var _definition = __webpack_require__(/*! ../type/definition.mjs */ "../../../node_modules/graphql/type/definition.mjs"); var _directives = __webpack_require__(/*! ../type/directives.mjs */ "../../../node_modules/graphql/type/directives.mjs"); @@ -37211,12 +36517,13 @@ var _schema = __webpack_require__(/*! ../type/schema.mjs */ "../../../node_modul * * This function returns a sorted copy of the given GraphQLSchema. */ + function lexicographicSortSchema(schema) { const schemaConfig = schema.toConfig(); - const typeMap = new Map(sortByName(schemaConfig.types).map(type => [type.name, sortNamedType(type)])); + const typeMap = (0, _keyValMap.keyValMap)(sortByName(schemaConfig.types), type => type.name, sortNamedType); return new _schema.GraphQLSchema({ ...schemaConfig, - types: Array.from(typeMap.values()), + types: Object.values(typeMap), directives: sortByName(schemaConfig.directives).map(sortDirective), query: replaceMaybeType(schemaConfig.query), mutation: replaceMaybeType(schemaConfig.mutation), @@ -37229,12 +36536,12 @@ function lexicographicSortSchema(schema) { } else if ((0, _definition.isNonNullType)(type)) { // @ts-expect-error return new _definition.GraphQLNonNull(replaceType(type.ofType)); - } - // @ts-expect-error FIXME: TS Conversion + } // @ts-expect-error FIXME: TS Conversion + return replaceNamedType(type); } function replaceNamedType(type) { - return typeMap.get(type.name); + return typeMap[type.name]; } function replaceMaybeType(maybeType) { return maybeType && replaceNamedType(maybeType); @@ -37312,6 +36619,7 @@ function lexicographicSortSchema(schema) { } /* c8 ignore next 3 */ // Not reachable, all possible types have been considered. + false || (0, _invariant.invariant)(false, 'Unexpected type: ' + (0, _inspect.inspect)(type)); } } @@ -37346,7 +36654,6 @@ function sortBy(array, mapToKey) { Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.printDirective = printDirective; exports.printIntrospectionSchema = printIntrospectionSchema; exports.printSchema = printSchema; exports.printType = printType; @@ -37375,19 +36682,23 @@ function printFilteredSchema(schema, directiveFilter, typeFilter) { return [printSchemaDefinition(schema), ...directives.map(directive => printDirective(directive)), ...types.map(type => printType(type))].filter(Boolean).join('\n\n'); } function printSchemaDefinition(schema) { - const queryType = schema.getQueryType(); - const mutationType = schema.getMutationType(); - const subscriptionType = schema.getSubscriptionType(); - // Special case: When a schema has no root operation types, no valid schema - // definition can be printed. - if (!queryType && !mutationType && !subscriptionType) { + if (schema.description == null && isSchemaOfCommonNames(schema)) { return; } - // Only print a schema definition if there is a description or if it should - // not be omitted because of having default type names. - if (schema.description != null || !hasDefaultRootOperationTypes(schema)) { - return printDescription(schema) + 'schema {\n' + (queryType ? ` query: ${queryType.name}\n` : '') + (mutationType ? ` mutation: ${mutationType.name}\n` : '') + (subscriptionType ? ` subscription: ${subscriptionType.name}\n` : '') + '}'; + const operationTypes = []; + const queryType = schema.getQueryType(); + if (queryType) { + operationTypes.push(` query: ${queryType.name}`); } + const mutationType = schema.getMutationType(); + if (mutationType) { + operationTypes.push(` mutation: ${mutationType.name}`); + } + const subscriptionType = schema.getSubscriptionType(); + if (subscriptionType) { + operationTypes.push(` subscription: ${subscriptionType.name}`); + } + return printDescription(schema) + `schema {\n${operationTypes.join('\n')}\n}`; } /** * GraphQL schema define root types for each type of operation. These types are @@ -37402,16 +36713,23 @@ function printSchemaDefinition(schema) { * } * ``` * - * When using this naming convention, the schema description can be omitted so - * long as these names are only used for operation types. - * - * Note however that if any of these default names are used elsewhere in the - * schema but not as a root operation type, the schema definition must still - * be printed to avoid ambiguity. + * When using this naming convention, the schema description can be omitted. */ -function hasDefaultRootOperationTypes(schema) { - /* eslint-disable eqeqeq */ - return schema.getQueryType() == schema.getType('Query') && schema.getMutationType() == schema.getType('Mutation') && schema.getSubscriptionType() == schema.getType('Subscription'); + +function isSchemaOfCommonNames(schema) { + const queryType = schema.getQueryType(); + if (queryType && queryType.name !== 'Query') { + return false; + } + const mutationType = schema.getMutationType(); + if (mutationType && mutationType.name !== 'Mutation') { + return false; + } + const subscriptionType = schema.getSubscriptionType(); + if (subscriptionType && subscriptionType.name !== 'Subscription') { + return false; + } + return true; } function printType(type) { if ((0, _definition.isScalarType)(type)) { @@ -37434,6 +36752,7 @@ function printType(type) { } /* c8 ignore next 3 */ // Not reachable, all possible types have been considered. + false || (0, _invariant.invariant)(false, 'Unexpected type: ' + (0, _inspect.inspect)(type)); } function printScalar(type) { @@ -37472,9 +36791,9 @@ function printBlock(items) { function printArgs(args, indentation = '') { if (args.length === 0) { return ''; - } - // If every arg does not have a description, print them on one line. - if (args.every(arg => arg.description == null)) { + } // If every arg does not have a description, print them on one line. + + if (args.every(arg => !arg.description)) { return '(' + args.map(printInputValue).join(', ') + ')'; } return '(\n' + args.map((arg, i) => printDescription(arg, ' ' + indentation, !i) + ' ' + indentation + printInputValue(arg)).join('\n') + '\n' + indentation + ')'; @@ -37526,7 +36845,7 @@ function printDescription(def, indentation = '', firstInBlock = true) { block: (0, _blockString.isPrintableAsBlockString)(description) }); const prefix = indentation && !firstInBlock ? '\n' + indentation : indentation; - return prefix + blockString.replaceAll('\n', '\n' + indentation) + '\n'; + return prefix + blockString.replace(/\n/g, '\n' + indentation) + '\n'; } /***/ }), @@ -37551,10 +36870,11 @@ var _visitor = __webpack_require__(/*! ../language/visitor.mjs */ "../../../node * which contains a single operation as well the fragment definitions it * refers to. */ + function separateOperations(documentAST) { const operations = []; - const depGraph = Object.create(null); - // Populate metadata and build a dependency graph. + const depGraph = Object.create(null); // Populate metadata and build a dependency graph. + for (const definitionNode of documentAST.definitions) { switch (definitionNode.kind) { case _kinds.Kind.OPERATION_DEFINITION: @@ -37563,22 +36883,21 @@ function separateOperations(documentAST) { case _kinds.Kind.FRAGMENT_DEFINITION: depGraph[definitionNode.name.value] = collectDependencies(definitionNode.selectionSet); break; - default: - // ignore non-executable definitions + default: // ignore non-executable definitions } - } - // For each operation, produce a new synthesized AST which includes only what + } // For each operation, produce a new synthesized AST which includes only what // is necessary for completing that operation. + const separatedDocumentASTs = Object.create(null); for (const operation of operations) { const dependencies = new Set(); for (const fragmentName of collectDependencies(operation.selectionSet)) { collectTransitiveDependencies(dependencies, depGraph, fragmentName); - } - // Provides the empty string for anonymous operations. - const operationName = operation.name ? operation.name.value : ''; - // The list of definition nodes to be included for this operation, sorted + } // Provides the empty string for anonymous operations. + + const operationName = operation.name ? operation.name.value : ''; // The list of definition nodes to be included for this operation, sorted // to retain the same order as the original document. + separatedDocumentASTs[operationName] = { kind: _kinds.Kind.DOCUMENT, definitions: documentAST.definitions.filter(node => node === operation || node.kind === _kinds.Kind.FRAGMENT_DEFINITION && dependencies.has(node.name.value)) @@ -37586,6 +36905,7 @@ function separateOperations(documentAST) { } return separatedDocumentASTs; } + // From a dependency graph, collects a list of transitive dependencies by // recursing through a dependency graph. function collectTransitiveDependencies(collected, depGraph, fromName) { @@ -37632,6 +36952,7 @@ var _kinds = __webpack_require__(/*! ../language/kinds.mjs */ "../../../node_mod * * @internal */ + function sortValueNode(valueNode) { switch (valueNode.kind) { case _kinds.Kind.OBJECT: @@ -37739,6 +37060,7 @@ var _tokenKind = __webpack_require__(/*! ../language/tokenKind.mjs */ "../../../ * """Type description""" type Foo{"""Field description""" bar:String} * ``` */ + function stripIgnoredCharacters(source) { const sourceObj = (0, _source.isSource)(source) ? source : new _source.Source(source); const body = sourceObj.body; @@ -37753,6 +37075,7 @@ function stripIgnoredCharacters(source) { * Also prevent case of non-punctuator token following by spread resulting * in invalid token (e.g. `1...` is invalid Float token). */ + const isNonPunctuator = !(0, _lexer.isPunctuatorTokenKind)(currentToken.kind); if (wasLastAddedTokenNonPunctuator) { if (isNonPunctuator || currentToken.kind === _tokenKind.TokenKind.SPREAD) { @@ -37796,28 +37119,29 @@ function isEqualType(typeA, typeB) { // Equivalent types are equal. if (typeA === typeB) { return true; - } - // If either type is non-null, the other must also be non-null. + } // If either type is non-null, the other must also be non-null. + if ((0, _definition.isNonNullType)(typeA) && (0, _definition.isNonNullType)(typeB)) { return isEqualType(typeA.ofType, typeB.ofType); - } - // If either type is a list, the other must also be a list. + } // If either type is a list, the other must also be a list. + if ((0, _definition.isListType)(typeA) && (0, _definition.isListType)(typeB)) { return isEqualType(typeA.ofType, typeB.ofType); - } - // Otherwise the types are not equal. + } // Otherwise the types are not equal. + return false; } /** * Provided a type and a super type, return true if the first type is either * equal or a subset of the second super type (covariant). */ + function isTypeSubTypeOf(schema, maybeSubType, superType) { // Equivalent type is a valid subtype if (maybeSubType === superType) { return true; - } - // If superType is non-null, maybeSubType must also be non-null. + } // If superType is non-null, maybeSubType must also be non-null. + if ((0, _definition.isNonNullType)(superType)) { if ((0, _definition.isNonNullType)(maybeSubType)) { return isTypeSubTypeOf(schema, maybeSubType.ofType, superType.ofType); @@ -37827,8 +37151,8 @@ function isTypeSubTypeOf(schema, maybeSubType, superType) { if ((0, _definition.isNonNullType)(maybeSubType)) { // If superType is nullable, maybeSubType may be non-null or nullable. return isTypeSubTypeOf(schema, maybeSubType.ofType, superType); - } - // If superType type is a list, maybeSubType type must also be a list. + } // If superType type is a list, maybeSubType type must also be a list. + if ((0, _definition.isListType)(superType)) { if ((0, _definition.isListType)(maybeSubType)) { return isTypeSubTypeOf(schema, maybeSubType.ofType, superType.ofType); @@ -37838,9 +37162,9 @@ function isTypeSubTypeOf(schema, maybeSubType, superType) { if ((0, _definition.isListType)(maybeSubType)) { // If superType is not a list, maybeSubType must also be not a list. return false; - } - // If superType type is an abstract type, check if it is super type of maybeSubType. + } // If superType type is an abstract type, check if it is super type of maybeSubType. // Otherwise, the child type is not a valid subtype of the parent type. + return (0, _definition.isAbstractType)(superType) && ((0, _definition.isInterfaceType)(maybeSubType) || (0, _definition.isObjectType)(maybeSubType)) && schema.isSubType(superType, maybeSubType); } /** @@ -37852,6 +37176,7 @@ function isTypeSubTypeOf(schema, maybeSubType, superType) { * * This function is commutative. */ + function doTypesOverlap(schema, typeA, typeB) { // Equivalent types overlap if (typeA === typeB) { @@ -37862,15 +37187,15 @@ function doTypesOverlap(schema, typeA, typeB) { // If both types are abstract, then determine if there is any intersection // between possible concrete types of each. return schema.getPossibleTypes(typeA).some(type => schema.isSubType(typeB, type)); - } - // Determine if the latter type is a possible concrete type of the former. + } // Determine if the latter type is a possible concrete type of the former. + return schema.isSubType(typeA, typeB); } if ((0, _definition.isAbstractType)(typeB)) { // Determine if the former type is a possible concrete type of the latter. return schema.isSubType(typeB, typeA); - } - // Otherwise the types do not overlap. + } // Otherwise the types do not overlap. + return false; } @@ -37923,6 +37248,7 @@ Object.defineProperty(exports, "__esModule", ({ exports.valueFromAST = valueFromAST; var _inspect = __webpack_require__(/*! ../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); var _invariant = __webpack_require__(/*! ../jsutils/invariant.mjs */ "../../../node_modules/graphql/jsutils/invariant.mjs"); +var _keyMap = __webpack_require__(/*! ../jsutils/keyMap.mjs */ "../../../node_modules/graphql/jsutils/keyMap.mjs"); var _kinds = __webpack_require__(/*! ../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); var _definition = __webpack_require__(/*! ../type/definition.mjs */ "../../../node_modules/graphql/type/definition.mjs"); /** @@ -37945,6 +37271,7 @@ var _definition = __webpack_require__(/*! ../type/definition.mjs */ "../../../no * | NullValue | null | * */ + function valueFromAST(valueNode, type, variables) { if (!valueNode) { // When there is no node, then there is also no value. @@ -37960,10 +37287,10 @@ function valueFromAST(valueNode, type, variables) { const variableValue = variables[variableName]; if (variableValue === null && (0, _definition.isNonNullType)(type)) { return; // Invalid: intentionally return no value. - } - // Note: This does no further checking that this variable is correct. + } // Note: This does no further checking that this variable is correct. // This assumes that this query has been validated and the variable // usage here is of the correct type. + return variableValue; } if ((0, _definition.isNonNullType)(type)) { @@ -38009,10 +37336,10 @@ function valueFromAST(valueNode, type, variables) { return; // Invalid: intentionally return no value. } const coercedObj = Object.create(null); - const fieldNodes = new Map(valueNode.fields.map(field => [field.name.value, field])); + const fieldNodes = (0, _keyMap.keyMap)(valueNode.fields, field => field.name.value); for (const field of Object.values(type.getFields())) { - const fieldNode = fieldNodes.get(field.name); - if (fieldNode == null || isMissingVariable(fieldNode.value, variables)) { + const fieldNode = fieldNodes[field.name]; + if (!fieldNode || isMissingVariable(fieldNode.value, variables)) { if (field.defaultValue !== undefined) { coercedObj[field.name] = field.defaultValue; } else if ((0, _definition.isNonNullType)(field.type)) { @@ -38054,10 +37381,11 @@ function valueFromAST(valueNode, type, variables) { } /* c8 ignore next 3 */ // Not reachable, all possible input types have been considered. + false || (0, _invariant.invariant)(false, 'Unexpected input type: ' + (0, _inspect.inspect)(type)); -} -// Returns true if the provided valueNode is a variable which is not defined +} // Returns true if the provided valueNode is a variable which is not defined // in the set of variables. + function isMissingVariable(valueNode, variables) { return valueNode.kind === _kinds.Kind.VARIABLE && (variables == null || variables[valueNode.name.value] === undefined); } @@ -38094,6 +37422,7 @@ var _kinds = __webpack_require__(/*! ../language/kinds.mjs */ "../../../node_mod * | Null | null | * */ + function valueFromASTUntyped(valueNode, variables) { switch (valueNode.kind) { case _kinds.Kind.NULL: @@ -38192,14 +37521,14 @@ class ASTValidationContext { let fragments = this._recursivelyReferencedFragments.get(operation); if (!fragments) { fragments = []; - const collectedNames = new Set(); + const collectedNames = Object.create(null); const nodesToVisit = [operation.selectionSet]; let node; while (node = nodesToVisit.pop()) { for (const spread of this.getFragmentSpreads(node)) { const fragName = spread.name.value; - if (!collectedNames.has(fragName)) { - collectedNames.add(fragName); + if (collectedNames[fragName] !== true) { + collectedNames[fragName] = true; const fragment = this.getFragment(fragName); if (fragment) { fragments.push(fragment); @@ -38312,24 +37641,6 @@ exports.ValidationContext = ValidationContext; Object.defineProperty(exports, "__esModule", ({ value: true })); -Object.defineProperty(exports, "DeferStreamDirectiveLabelRule", ({ - enumerable: true, - get: function () { - return _DeferStreamDirectiveLabelRule.DeferStreamDirectiveLabelRule; - } -})); -Object.defineProperty(exports, "DeferStreamDirectiveOnRootFieldRule", ({ - enumerable: true, - get: function () { - return _DeferStreamDirectiveOnRootFieldRule.DeferStreamDirectiveOnRootFieldRule; - } -})); -Object.defineProperty(exports, "DeferStreamDirectiveOnValidOperationsRule", ({ - enumerable: true, - get: function () { - return _DeferStreamDirectiveOnValidOperationsRule.DeferStreamDirectiveOnValidOperationsRule; - } -})); Object.defineProperty(exports, "ExecutableDefinitionsRule", ({ enumerable: true, get: function () { @@ -38462,12 +37773,6 @@ Object.defineProperty(exports, "SingleFieldSubscriptionsRule", ({ return _SingleFieldSubscriptionsRule.SingleFieldSubscriptionsRule; } })); -Object.defineProperty(exports, "StreamDirectiveOnListFieldRule", ({ - enumerable: true, - get: function () { - return _StreamDirectiveOnListFieldRule.StreamDirectiveOnListFieldRule; - } -})); Object.defineProperty(exports, "UniqueArgumentDefinitionNamesRule", ({ enumerable: true, get: function () { @@ -38585,9 +37890,6 @@ Object.defineProperty(exports, "validate", ({ var _validate = __webpack_require__(/*! ./validate.mjs */ "../../../node_modules/graphql/validation/validate.mjs"); var _ValidationContext = __webpack_require__(/*! ./ValidationContext.mjs */ "../../../node_modules/graphql/validation/ValidationContext.mjs"); var _specifiedRules = __webpack_require__(/*! ./specifiedRules.mjs */ "../../../node_modules/graphql/validation/specifiedRules.mjs"); -var _DeferStreamDirectiveLabelRule = __webpack_require__(/*! ./rules/DeferStreamDirectiveLabelRule.mjs */ "../../../node_modules/graphql/validation/rules/DeferStreamDirectiveLabelRule.mjs"); -var _DeferStreamDirectiveOnRootFieldRule = __webpack_require__(/*! ./rules/DeferStreamDirectiveOnRootFieldRule.mjs */ "../../../node_modules/graphql/validation/rules/DeferStreamDirectiveOnRootFieldRule.mjs"); -var _DeferStreamDirectiveOnValidOperationsRule = __webpack_require__(/*! ./rules/DeferStreamDirectiveOnValidOperationsRule.mjs */ "../../../node_modules/graphql/validation/rules/DeferStreamDirectiveOnValidOperationsRule.mjs"); var _ExecutableDefinitionsRule = __webpack_require__(/*! ./rules/ExecutableDefinitionsRule.mjs */ "../../../node_modules/graphql/validation/rules/ExecutableDefinitionsRule.mjs"); var _FieldsOnCorrectTypeRule = __webpack_require__(/*! ./rules/FieldsOnCorrectTypeRule.mjs */ "../../../node_modules/graphql/validation/rules/FieldsOnCorrectTypeRule.mjs"); var _FragmentsOnCompositeTypesRule = __webpack_require__(/*! ./rules/FragmentsOnCompositeTypesRule.mjs */ "../../../node_modules/graphql/validation/rules/FragmentsOnCompositeTypesRule.mjs"); @@ -38605,7 +37907,6 @@ var _PossibleFragmentSpreadsRule = __webpack_require__(/*! ./rules/PossibleFragm var _ProvidedRequiredArgumentsRule = __webpack_require__(/*! ./rules/ProvidedRequiredArgumentsRule.mjs */ "../../../node_modules/graphql/validation/rules/ProvidedRequiredArgumentsRule.mjs"); var _ScalarLeafsRule = __webpack_require__(/*! ./rules/ScalarLeafsRule.mjs */ "../../../node_modules/graphql/validation/rules/ScalarLeafsRule.mjs"); var _SingleFieldSubscriptionsRule = __webpack_require__(/*! ./rules/SingleFieldSubscriptionsRule.mjs */ "../../../node_modules/graphql/validation/rules/SingleFieldSubscriptionsRule.mjs"); -var _StreamDirectiveOnListFieldRule = __webpack_require__(/*! ./rules/StreamDirectiveOnListFieldRule.mjs */ "../../../node_modules/graphql/validation/rules/StreamDirectiveOnListFieldRule.mjs"); var _UniqueArgumentNamesRule = __webpack_require__(/*! ./rules/UniqueArgumentNamesRule.mjs */ "../../../node_modules/graphql/validation/rules/UniqueArgumentNamesRule.mjs"); var _UniqueDirectivesPerLocationRule = __webpack_require__(/*! ./rules/UniqueDirectivesPerLocationRule.mjs */ "../../../node_modules/graphql/validation/rules/UniqueDirectivesPerLocationRule.mjs"); var _UniqueFragmentNamesRule = __webpack_require__(/*! ./rules/UniqueFragmentNamesRule.mjs */ "../../../node_modules/graphql/validation/rules/UniqueFragmentNamesRule.mjs"); @@ -38629,182 +37930,6 @@ var _NoSchemaIntrospectionCustomRule = __webpack_require__(/*! ./rules/custom/No /***/ }), -/***/ "../../../node_modules/graphql/validation/rules/DeferStreamDirectiveLabelRule.mjs": -/*!****************************************************************************************!*\ - !*** ../../../node_modules/graphql/validation/rules/DeferStreamDirectiveLabelRule.mjs ***! - \****************************************************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.DeferStreamDirectiveLabelRule = DeferStreamDirectiveLabelRule; -var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); -var _kinds = __webpack_require__(/*! ../../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); -var _directives = __webpack_require__(/*! ../../type/directives.mjs */ "../../../node_modules/graphql/type/directives.mjs"); -/** - * Defer and stream directive labels are unique - * - * A GraphQL document is only valid if defer and stream directives' label argument is static and unique. - */ -function DeferStreamDirectiveLabelRule(context) { - const knownLabels = new Map(); - return { - Directive(node) { - if (node.name.value === _directives.GraphQLDeferDirective.name || node.name.value === _directives.GraphQLStreamDirective.name) { - var _node$arguments; - const labelArgument = (_node$arguments = node.arguments) === null || _node$arguments === void 0 ? void 0 : _node$arguments.find(arg => arg.name.value === 'label'); - const labelValue = labelArgument === null || labelArgument === void 0 ? void 0 : labelArgument.value; - if (!labelValue) { - return; - } - if (labelValue.kind !== _kinds.Kind.STRING) { - context.reportError(new _GraphQLError.GraphQLError(`Directive "${node.name.value}"'s label argument must be a static string.`, { - nodes: node - })); - return; - } - const knownLabel = knownLabels.get(labelValue.value); - if (knownLabel != null) { - context.reportError(new _GraphQLError.GraphQLError('Defer/Stream directive label argument must be unique.', { - nodes: [knownLabel, node] - })); - } else { - knownLabels.set(labelValue.value, node); - } - } - } - }; -} - -/***/ }), - -/***/ "../../../node_modules/graphql/validation/rules/DeferStreamDirectiveOnRootFieldRule.mjs": -/*!**********************************************************************************************!*\ - !*** ../../../node_modules/graphql/validation/rules/DeferStreamDirectiveOnRootFieldRule.mjs ***! - \**********************************************************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.DeferStreamDirectiveOnRootFieldRule = DeferStreamDirectiveOnRootFieldRule; -var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); -var _directives = __webpack_require__(/*! ../../type/directives.mjs */ "../../../node_modules/graphql/type/directives.mjs"); -/** - * Defer and stream directives are used on valid root field - * - * A GraphQL document is only valid if defer directives are not used on root mutation or subscription types. - */ -function DeferStreamDirectiveOnRootFieldRule(context) { - return { - Directive(node) { - const mutationType = context.getSchema().getMutationType(); - const subscriptionType = context.getSchema().getSubscriptionType(); - const parentType = context.getParentType(); - if (parentType && node.name.value === _directives.GraphQLDeferDirective.name) { - if (mutationType && parentType === mutationType) { - context.reportError(new _GraphQLError.GraphQLError(`Defer directive cannot be used on root mutation type "${parentType.name}".`, { - nodes: node - })); - } - if (subscriptionType && parentType === subscriptionType) { - context.reportError(new _GraphQLError.GraphQLError(`Defer directive cannot be used on root subscription type "${parentType.name}".`, { - nodes: node - })); - } - } - if (parentType && node.name.value === _directives.GraphQLStreamDirective.name) { - if (mutationType && parentType === mutationType) { - context.reportError(new _GraphQLError.GraphQLError(`Stream directive cannot be used on root mutation type "${parentType.name}".`, { - nodes: node - })); - } - if (subscriptionType && parentType === subscriptionType) { - context.reportError(new _GraphQLError.GraphQLError(`Stream directive cannot be used on root subscription type "${parentType.name}".`, { - nodes: node - })); - } - } - } - }; -} - -/***/ }), - -/***/ "../../../node_modules/graphql/validation/rules/DeferStreamDirectiveOnValidOperationsRule.mjs": -/*!****************************************************************************************************!*\ - !*** ../../../node_modules/graphql/validation/rules/DeferStreamDirectiveOnValidOperationsRule.mjs ***! - \****************************************************************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.DeferStreamDirectiveOnValidOperationsRule = DeferStreamDirectiveOnValidOperationsRule; -var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); -var _ast = __webpack_require__(/*! ../../language/ast.mjs */ "../../../node_modules/graphql/language/ast.mjs"); -var _kinds = __webpack_require__(/*! ../../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); -var _directives = __webpack_require__(/*! ../../type/directives.mjs */ "../../../node_modules/graphql/type/directives.mjs"); -function ifArgumentCanBeFalse(node) { - var _node$arguments; - const ifArgument = (_node$arguments = node.arguments) === null || _node$arguments === void 0 ? void 0 : _node$arguments.find(arg => arg.name.value === 'if'); - if (!ifArgument) { - return false; - } - if (ifArgument.value.kind === _kinds.Kind.BOOLEAN) { - if (ifArgument.value.value) { - return false; - } - } else if (ifArgument.value.kind !== _kinds.Kind.VARIABLE) { - return false; - } - return true; -} -/** - * Defer And Stream Directives Are Used On Valid Operations - * - * A GraphQL document is only valid if defer directives are not used on root mutation or subscription types. - */ -function DeferStreamDirectiveOnValidOperationsRule(context) { - const fragmentsUsedOnSubscriptions = new Set(); - return { - OperationDefinition(operation) { - if (operation.operation === _ast.OperationTypeNode.SUBSCRIPTION) { - for (const fragment of context.getRecursivelyReferencedFragments(operation)) { - fragmentsUsedOnSubscriptions.add(fragment.name.value); - } - } - }, - Directive(node, _key, _parent, _path, ancestors) { - const definitionNode = ancestors[2]; - if ('kind' in definitionNode && (definitionNode.kind === _kinds.Kind.FRAGMENT_DEFINITION && fragmentsUsedOnSubscriptions.has(definitionNode.name.value) || definitionNode.kind === _kinds.Kind.OPERATION_DEFINITION && definitionNode.operation === _ast.OperationTypeNode.SUBSCRIPTION)) { - if (node.name.value === _directives.GraphQLDeferDirective.name) { - if (!ifArgumentCanBeFalse(node)) { - context.reportError(new _GraphQLError.GraphQLError('Defer directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.', { - nodes: node - })); - } - } else if (node.name.value === _directives.GraphQLStreamDirective.name) { - if (!ifArgumentCanBeFalse(node)) { - context.reportError(new _GraphQLError.GraphQLError('Stream directive not supported on subscription operations. Disable `@stream` by setting the `if` argument to `false`.', { - nodes: node - })); - } - } - } - } - }; -} - -/***/ }), - /***/ "../../../node_modules/graphql/validation/rules/ExecutableDefinitionsRule.mjs": /*!************************************************************************************!*\ !*** ../../../node_modules/graphql/validation/rules/ExecutableDefinitionsRule.mjs ***! @@ -38880,14 +38005,14 @@ function FieldsOnCorrectTypeRule(context) { if (!fieldDef) { // This field doesn't exist, lets look for suggestions. const schema = context.getSchema(); - const fieldName = node.name.value; - // First determine if there are any suggested types to condition on. - let suggestion = (0, _didYouMean.didYouMean)('to use an inline fragment on', getSuggestedTypeNames(schema, type, fieldName)); - // If there are no suggested types, then perhaps this was a typo? + const fieldName = node.name.value; // First determine if there are any suggested types to condition on. + + let suggestion = (0, _didYouMean.didYouMean)('to use an inline fragment on', getSuggestedTypeNames(schema, type, fieldName)); // If there are no suggested types, then perhaps this was a typo? + if (suggestion === '') { suggestion = (0, _didYouMean.didYouMean)(getSuggestedFieldNames(type, fieldName)); - } - // Report an error, including helpful suggestions. + } // Report an error, including helpful suggestions. + context.reportError(new _GraphQLError.GraphQLError(`Cannot query field "${fieldName}" on type "${type.name}".` + suggestion, { nodes: node })); @@ -38901,6 +38026,7 @@ function FieldsOnCorrectTypeRule(context) { * they implement. If any of those types include the provided field, suggest them, * sorted by how often the type is referenced. */ + function getSuggestedTypeNames(schema, type, fieldName) { if (!(0, _definition.isAbstractType)(type)) { // Must be an Object type, which does not have possible fields. @@ -38909,18 +38035,18 @@ function getSuggestedTypeNames(schema, type, fieldName) { const suggestedTypes = new Set(); const usageCount = Object.create(null); for (const possibleType of schema.getPossibleTypes(type)) { - if (possibleType.getFields()[fieldName] == null) { + if (!possibleType.getFields()[fieldName]) { continue; - } - // This object type defines this field. + } // This object type defines this field. + suggestedTypes.add(possibleType); usageCount[possibleType.name] = 1; for (const possibleInterface of possibleType.getInterfaces()) { var _usageCount$possibleI; - if (possibleInterface.getFields()[fieldName] == null) { + if (!possibleInterface.getFields()[fieldName]) { continue; - } - // This interface type defines this field. + } // This interface type defines this field. + suggestedTypes.add(possibleInterface); usageCount[possibleInterface.name] = ((_usageCount$possibleI = usageCount[possibleInterface.name]) !== null && _usageCount$possibleI !== void 0 ? _usageCount$possibleI : 0) + 1; } @@ -38930,8 +38056,8 @@ function getSuggestedTypeNames(schema, type, fieldName) { const usageCountDiff = usageCount[typeB.name] - usageCount[typeA.name]; if (usageCountDiff !== 0) { return usageCountDiff; - } - // Suggest super types first followed by subtypes + } // Suggest super types first followed by subtypes + if ((0, _definition.isInterfaceType)(typeA) && schema.isSubType(typeA, typeB)) { return -1; } @@ -38945,12 +38071,13 @@ function getSuggestedTypeNames(schema, type, fieldName) { * For the field name provided, determine if there are any similar field names * that may be the result of a typo. */ + function getSuggestedFieldNames(type, fieldName) { if ((0, _definition.isObjectType)(type) || (0, _definition.isInterfaceType)(type)) { const possibleFieldNames = Object.keys(type.getFields()); return (0, _suggestionList.suggestionList)(fieldName, possibleFieldNames); - } - // Otherwise, must be a Union type, which does not define fields. + } // Otherwise, must be a Union type, which does not define fields. + return []; } @@ -39058,28 +38185,31 @@ function KnownArgumentNamesRule(context) { /** * @internal */ + function KnownArgumentNamesOnDirectivesRule(context) { - const directiveArgs = new Map(); + const directiveArgs = Object.create(null); const schema = context.getSchema(); const definedDirectives = schema ? schema.getDirectives() : _directives.specifiedDirectives; for (const directive of definedDirectives) { - directiveArgs.set(directive.name, directive.args.map(arg => arg.name)); + directiveArgs[directive.name] = directive.args.map(arg => arg.name); } const astDefinitions = context.getDocument().definitions; for (const def of astDefinitions) { if (def.kind === _kinds.Kind.DIRECTIVE_DEFINITION) { var _def$arguments; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const argsNodes = (_def$arguments = def.arguments) !== null && _def$arguments !== void 0 ? _def$arguments : []; - directiveArgs.set(def.name.value, argsNodes.map(arg => arg.name.value)); + directiveArgs[def.name.value] = argsNodes.map(arg => arg.name.value); } } return { Directive(directiveNode) { const directiveName = directiveNode.name.value; - const knownArgs = directiveArgs.get(directiveName); - if (directiveNode.arguments != null && knownArgs != null) { + const knownArgs = directiveArgs[directiveName]; + if (directiveNode.arguments && knownArgs) { for (const argNode of directiveNode.arguments) { const argName = argNode.name.value; if (!knownArgs.includes(argName)) { @@ -39125,30 +38255,30 @@ var _directives = __webpack_require__(/*! ../../type/directives.mjs */ "../../.. * See https://spec.graphql.org/draft/#sec-Directives-Are-Defined */ function KnownDirectivesRule(context) { - const locationsMap = new Map(); + const locationsMap = Object.create(null); const schema = context.getSchema(); const definedDirectives = schema ? schema.getDirectives() : _directives.specifiedDirectives; for (const directive of definedDirectives) { - locationsMap.set(directive.name, directive.locations); + locationsMap[directive.name] = directive.locations; } const astDefinitions = context.getDocument().definitions; for (const def of astDefinitions) { if (def.kind === _kinds.Kind.DIRECTIVE_DEFINITION) { - locationsMap.set(def.name.value, def.locations.map(name => name.value)); + locationsMap[def.name.value] = def.locations.map(name => name.value); } } return { Directive(node, _key, _parent, _path, ancestors) { const name = node.name.value; - const locations = locationsMap.get(name); - if (locations == null) { + const locations = locationsMap[name]; + if (!locations) { context.reportError(new _GraphQLError.GraphQLError(`Unknown directive "@${name}".`, { nodes: node })); return; } const candidateLocation = getDirectiveLocationForASTPath(ancestors); - if (candidateLocation != null && !locations.includes(candidateLocation)) { + if (candidateLocation && !locations.includes(candidateLocation)) { context.reportError(new _GraphQLError.GraphQLError(`Directive "@${name}" may not be used on ${candidateLocation}.`, { nodes: node })); @@ -39157,8 +38287,8 @@ function KnownDirectivesRule(context) { }; } function getDirectiveLocationForASTPath(ancestors) { - const appliedTo = ancestors.at(-1); - appliedTo != null && 'kind' in appliedTo || (0, _invariant.invariant)(false); + const appliedTo = ancestors[ancestors.length - 1]; + 'kind' in appliedTo || (0, _invariant.invariant)(false); switch (appliedTo.kind) { case _kinds.Kind.OPERATION_DEFINITION: return getDirectiveLocationForOperation(appliedTo.operation); @@ -39199,12 +38329,14 @@ function getDirectiveLocationForASTPath(ancestors) { return _directiveLocation.DirectiveLocation.INPUT_OBJECT; case _kinds.Kind.INPUT_VALUE_DEFINITION: { - const parentNode = ancestors.at(-3); - parentNode != null && 'kind' in parentNode || (0, _invariant.invariant)(false); + const parentNode = ancestors[ancestors.length - 3]; + 'kind' in parentNode || (0, _invariant.invariant)(false); return parentNode.kind === _kinds.Kind.INPUT_OBJECT_TYPE_DEFINITION ? _directiveLocation.DirectiveLocation.INPUT_FIELD_DEFINITION : _directiveLocation.DirectiveLocation.ARGUMENT_DEFINITION; } // Not reachable, all possible types have been considered. - /* c8 ignore next 2 */ + + /* c8 ignore next */ + default: false || (0, _invariant.invariant)(false, 'Unexpected kind: ' + (0, _inspect.inspect)(appliedTo.kind)); } @@ -39286,23 +38418,26 @@ var _scalars = __webpack_require__(/*! ../../type/scalars.mjs */ "../../../node_ * See https://spec.graphql.org/draft/#sec-Fragment-Spread-Type-Existence */ function KnownTypeNamesRule(context) { - var _context$getSchema$ge, _context$getSchema; - const { - definitions - } = context.getDocument(); - const existingTypesMap = (_context$getSchema$ge = (_context$getSchema = context.getSchema()) === null || _context$getSchema === void 0 ? void 0 : _context$getSchema.getTypeMap()) !== null && _context$getSchema$ge !== void 0 ? _context$getSchema$ge : {}; - const typeNames = new Set([...Object.keys(existingTypesMap), ...definitions.filter(_predicates.isTypeDefinitionNode).map(def => def.name.value)]); + const schema = context.getSchema(); + const existingTypesMap = schema ? schema.getTypeMap() : Object.create(null); + const definedTypes = Object.create(null); + for (const def of context.getDocument().definitions) { + if ((0, _predicates.isTypeDefinitionNode)(def)) { + definedTypes[def.name.value] = true; + } + } + const typeNames = [...Object.keys(existingTypesMap), ...Object.keys(definedTypes)]; return { NamedType(node, _1, parent, _2, ancestors) { const typeName = node.name.value; - if (!typeNames.has(typeName)) { + if (!existingTypesMap[typeName] && !definedTypes[typeName]) { var _ancestors$; const definitionNode = (_ancestors$ = ancestors[2]) !== null && _ancestors$ !== void 0 ? _ancestors$ : parent; const isSDL = definitionNode != null && isSDLNode(definitionNode); - if (isSDL && standardTypeNames.has(typeName)) { + if (isSDL && standardTypeNames.includes(typeName)) { return; } - const suggestedTypes = (0, _suggestionList.suggestionList)(typeName, isSDL ? [...standardTypeNames, ...typeNames] : [...typeNames]); + const suggestedTypes = (0, _suggestionList.suggestionList)(typeName, isSDL ? standardTypeNames.concat(typeNames) : typeNames); context.reportError(new _GraphQLError.GraphQLError(`Unknown type "${typeName}".` + (0, _didYouMean.didYouMean)(suggestedTypes), { nodes: node })); @@ -39310,7 +38445,7 @@ function KnownTypeNamesRule(context) { } }; } -const standardTypeNames = new Set([..._scalars.specifiedScalarTypes, ..._introspection.introspectionTypes].map(type => type.name)); +const standardTypeNames = [..._scalars.specifiedScalarTypes, ..._introspection.introspectionTypes].map(type => type.name); function isSDLNode(value) { return 'kind' in value && ((0, _predicates.isTypeSystemDefinitionNode)(value) || (0, _predicates.isTypeSystemExtensionNode)(value)); } @@ -39429,14 +38564,14 @@ function MaxIntrospectionDepthRule(context) { } const fragment = context.getFragment(fragmentName); if (!fragment) { - // Missing fragments checks are handled by the `KnownFragmentNamesRule`. + // Missing fragments checks are handled by `KnownFragmentNamesRule`. return false; - } - // Rather than following an immutable programming pattern which has + } // Rather than following an immutable programming pattern which has // significant memory and garbage collection overhead, we've opted to // take a mutable approach for efficiency's sake. Importantly visiting a // fragment twice is fine, so long as you don't do one visit inside the // other. + try { visitedFragments[fragmentName] = true; return checkDepth(fragment, visitedFragments, depth); @@ -39446,15 +38581,14 @@ function MaxIntrospectionDepthRule(context) { } if (node.kind === _kinds.Kind.FIELD && ( // check all introspection lists - // TODO: instead of relying on field names, check whether the type is a list node.name.value === 'fields' || node.name.value === 'interfaces' || node.name.value === 'possibleTypes' || node.name.value === 'inputFields')) { // eslint-disable-next-line no-param-reassign depth++; if (depth >= MAX_LISTS_DEPTH) { return true; } - } - // handles fields and inline fragments + } // handles fields and inline fragments + if ('selectionSet' in node && node.selectionSet) { for (const child of node.selectionSet.selections) { if (checkDepth(child, visitedFragments, depth)) { @@ -39504,10 +38638,10 @@ var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../ function NoFragmentCyclesRule(context) { // Tracks already visited fragments to maintain O(N) and to ensure that cycles // are not redundantly reported. - const visitedFrags = new Set(); - // Array of AST nodes used to produce meaningful errors - const spreadPath = []; - // Position in the spread path + const visitedFrags = Object.create(null); // Array of AST nodes used to produce meaningful errors + + const spreadPath = []; // Position in the spread path + const spreadPathIndexByName = Object.create(null); return { OperationDefinition: () => false, @@ -39515,16 +38649,16 @@ function NoFragmentCyclesRule(context) { detectCycleRecursive(node); return false; } - }; - // This does a straight-forward DFS to find cycles. + }; // This does a straight-forward DFS to find cycles. // It does not terminate when a cycle was found but continues to explore // the graph to find all possible cycles. + function detectCycleRecursive(fragment) { - if (visitedFrags.has(fragment.name.value)) { + if (visitedFrags[fragment.name.value]) { return; } const fragmentName = fragment.name.value; - visitedFrags.add(fragmentName); + visitedFrags[fragmentName] = true; const spreadNodes = context.getFragmentSpreads(fragment.selectionSet); if (spreadNodes.length === 0) { return; @@ -39576,21 +38710,28 @@ var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../ * See https://spec.graphql.org/draft/#sec-All-Variable-Uses-Defined */ function NoUndefinedVariablesRule(context) { + let variableNameDefined = Object.create(null); return { - OperationDefinition(operation) { - var _operation$variableDe; - const variableNameDefined = new Set((_operation$variableDe = operation.variableDefinitions) === null || _operation$variableDe === void 0 ? void 0 : _operation$variableDe.map(node => node.variable.name.value)); - const usages = context.getRecursiveVariableUsages(operation); - for (const { - node - } of usages) { - const varName = node.name.value; - if (!variableNameDefined.has(varName)) { - context.reportError(new _GraphQLError.GraphQLError(operation.name ? `Variable "$${varName}" is not defined by operation "${operation.name.value}".` : `Variable "$${varName}" is not defined.`, { - nodes: [node, operation] - })); + OperationDefinition: { + enter() { + variableNameDefined = Object.create(null); + }, + leave(operation) { + const usages = context.getRecursiveVariableUsages(operation); + for (const { + node + } of usages) { + const varName = node.name.value; + if (variableNameDefined[varName] !== true) { + context.reportError(new _GraphQLError.GraphQLError(operation.name ? `Variable "$${varName}" is not defined by operation "${operation.name.value}".` : `Variable "$${varName}" is not defined.`, { + nodes: [node, operation] + })); + } } } + }, + VariableDefinition(node) { + variableNameDefined[node.variable.name.value] = true; } }; } @@ -39619,13 +38760,11 @@ var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../ * See https://spec.graphql.org/draft/#sec-Fragments-Must-Be-Used */ function NoUnusedFragmentsRule(context) { - const fragmentNameUsed = new Set(); + const operationDefs = []; const fragmentDefs = []; return { - OperationDefinition(operation) { - for (const fragment of context.getRecursivelyReferencedFragments(operation)) { - fragmentNameUsed.add(fragment.name.value); - } + OperationDefinition(node) { + operationDefs.push(node); return false; }, FragmentDefinition(node) { @@ -39634,9 +38773,15 @@ function NoUnusedFragmentsRule(context) { }, Document: { leave() { + const fragmentNameUsed = Object.create(null); + for (const operation of operationDefs) { + for (const fragment of context.getRecursivelyReferencedFragments(operation)) { + fragmentNameUsed[fragment.name.value] = true; + } + } for (const fragmentDef of fragmentDefs) { const fragName = fragmentDef.name.value; - if (!fragmentNameUsed.has(fragName)) { + if (fragmentNameUsed[fragName] !== true) { context.reportError(new _GraphQLError.GraphQLError(`Fragment "${fragName}" is never used.`, { nodes: fragmentDef })); @@ -39671,24 +38816,32 @@ var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../ * See https://spec.graphql.org/draft/#sec-All-Variables-Used */ function NoUnusedVariablesRule(context) { + let variableDefs = []; return { - OperationDefinition(operation) { - var _operation$variableDe; - const usages = context.getRecursiveVariableUsages(operation); - const variableNameUsed = new Set(usages.map(({ - node - }) => node.name.value)); - // FIXME: https://github.com/graphql/graphql-js/issues/2203 - /* c8 ignore next */ - const variableDefinitions = (_operation$variableDe = operation.variableDefinitions) !== null && _operation$variableDe !== void 0 ? _operation$variableDe : []; - for (const variableDef of variableDefinitions) { - const variableName = variableDef.variable.name.value; - if (!variableNameUsed.has(variableName)) { - context.reportError(new _GraphQLError.GraphQLError(operation.name ? `Variable "$${variableName}" is never used in operation "${operation.name.value}".` : `Variable "$${variableName}" is never used.`, { - nodes: variableDef - })); + OperationDefinition: { + enter() { + variableDefs = []; + }, + leave(operation) { + const variableNameUsed = Object.create(null); + const usages = context.getRecursiveVariableUsages(operation); + for (const { + node + } of usages) { + variableNameUsed[node.name.value] = true; + } + for (const variableDef of variableDefs) { + const variableName = variableDef.variable.name.value; + if (variableNameUsed[variableName] !== true) { + context.reportError(new _GraphQLError.GraphQLError(operation.name ? `Variable "$${variableName}" is never used in operation "${operation.name.value}".` : `Variable "$${variableName}" is never used.`, { + nodes: variableDef + })); + } } } + }, + VariableDefinition(def) { + variableDefs.push(def); } }; } @@ -39714,9 +38867,6 @@ var _printer = __webpack_require__(/*! ../../language/printer.mjs */ "../../../n var _definition = __webpack_require__(/*! ../../type/definition.mjs */ "../../../node_modules/graphql/type/definition.mjs"); var _sortValueNode = __webpack_require__(/*! ../../utilities/sortValueNode.mjs */ "../../../node_modules/graphql/utilities/sortValueNode.mjs"); var _typeFromAST = __webpack_require__(/*! ../../utilities/typeFromAST.mjs */ "../../../node_modules/graphql/utilities/typeFromAST.mjs"); -/* eslint-disable max-params */ -// This file contains a lot of such errors but we plan to refactor it anyway -// so just disable it for entire file. function reasonMessage(reason) { if (Array.isArray(reason)) { return reason.map(([responseName, subReason]) => `subfields "${responseName}" conflict because ` + reasonMessage(subReason)).join(' and '); @@ -39732,14 +38882,15 @@ function reasonMessage(reason) { * * See https://spec.graphql.org/draft/#sec-Field-Selection-Merging */ + function OverlappingFieldsCanBeMergedRule(context) { // A memoization for when two fragments are compared "between" each other for // conflicts. Two fragments may be compared many times, so memoizing this can // dramatically improve the performance of this validator. - const comparedFragmentPairs = new PairSet(); - // A cache for the "field map" and list of fragment names found in any given + const comparedFragmentPairs = new PairSet(); // A cache for the "field map" and list of fragment names found in any given // selection set. Selection sets may be asked for this information multiple // times, so this improves the performance of this validator. + const cachedFieldsAndFragmentNames = new Map(); return { SelectionSet(selectionSet) { @@ -39753,6 +38904,7 @@ function OverlappingFieldsCanBeMergedRule(context) { } }; } + /** * Algorithm: * @@ -39812,43 +38964,43 @@ function OverlappingFieldsCanBeMergedRule(context) { // GraphQL Document. function findConflictsWithinSelectionSet(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentType, selectionSet) { const conflicts = []; - const [fieldMap, fragmentNames] = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType, selectionSet); - // (A) Find find all conflicts "within" the fields of this selection set. + const [fieldMap, fragmentNames] = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType, selectionSet); // (A) Find find all conflicts "within" the fields of this selection set. // Note: this is the *only place* `collectConflictsWithin` is called. + collectConflictsWithin(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, fieldMap); if (fragmentNames.length !== 0) { // (B) Then collect conflicts between these fields and those represented by // each spread fragment name found. for (let i = 0; i < fragmentNames.length; i++) { - collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, false, fieldMap, fragmentNames[i]); - // (C) Then compare this fragment with all other fragments found in this + collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, false, fieldMap, fragmentNames[i]); // (C) Then compare this fragment with all other fragments found in this // selection set to collect conflicts between fragments spread together. // This compares each item in the list of fragment names to every other // item in that same list (except for itself). + for (let j = i + 1; j < fragmentNames.length; j++) { collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, false, fragmentNames[i], fragmentNames[j]); } } } return conflicts; -} -// Collect all conflicts found between a set of fields and a fragment reference +} // Collect all conflicts found between a set of fields and a fragment reference // including via spreading in any nested fragments. + function collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fragmentName) { const fragment = context.getFragment(fragmentName); if (!fragment) { return; } - const [fieldMap2, referencedFragmentNames] = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment); - // Do not compare a fragment's fieldMap to itself. + const [fieldMap2, referencedFragmentNames] = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment); // Do not compare a fragment's fieldMap to itself. + if (fieldMap === fieldMap2) { return; - } - // (D) First collect any conflicts between the provided collection of fields + } // (D) First collect any conflicts between the provided collection of fields // and the collection of fields represented by the given fragment. - collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fieldMap2); - // (E) Then collect any conflicts between the provided collection of fields + + collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fieldMap2); // (E) Then collect any conflicts between the provided collection of fields // and any fragment names found in the given fragment. + for (const referencedFragmentName of referencedFragmentNames) { // Memoize so two fragments are not compared for conflicts more than once. if (comparedFragmentPairs.has(referencedFragmentName, fragmentName, areMutuallyExclusive)) { @@ -39857,15 +39009,15 @@ function collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFiel comparedFragmentPairs.add(referencedFragmentName, fragmentName, areMutuallyExclusive); collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap, referencedFragmentName); } -} -// Collect all conflicts found between two fragments, including via spreading in +} // Collect all conflicts found between two fragments, including via spreading in // any nested fragments. + function collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentName1, fragmentName2) { // No need to compare a fragment to itself. if (fragmentName1 === fragmentName2) { return; - } - // Memoize so two fragments are not compared for conflicts more than once. + } // Memoize so two fragments are not compared for conflicts more than once. + if (comparedFragmentPairs.has(fragmentName1, fragmentName2, areMutuallyExclusive)) { return; } @@ -39876,57 +39028,57 @@ function collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFra return; } const [fieldMap1, referencedFragmentNames1] = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment1); - const [fieldMap2, referencedFragmentNames2] = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment2); - // (F) First, collect all conflicts between these two collections of fields + const [fieldMap2, referencedFragmentNames2] = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment2); // (F) First, collect all conflicts between these two collections of fields // (not including any nested fragments). - collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fieldMap2); - // (G) Then collect conflicts between the first fragment and any nested + + collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fieldMap2); // (G) Then collect conflicts between the first fragment and any nested // fragments spread in the second fragment. + for (const referencedFragmentName2 of referencedFragmentNames2) { collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentName1, referencedFragmentName2); - } - // (G) Then collect conflicts between the second fragment and any nested + } // (G) Then collect conflicts between the second fragment and any nested // fragments spread in the first fragment. + for (const referencedFragmentName1 of referencedFragmentNames1) { collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, referencedFragmentName1, fragmentName2); } -} -// Find all conflicts found between two selection sets, including those found +} // Find all conflicts found between two selection sets, including those found // via spreading in fragments. Called when determining if conflicts exist // between the sub-fields of two overlapping fields. + function findConflictsBetweenSubSelectionSets(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, parentType1, selectionSet1, parentType2, selectionSet2) { const conflicts = []; const [fieldMap1, fragmentNames1] = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType1, selectionSet1); - const [fieldMap2, fragmentNames2] = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType2, selectionSet2); - // (H) First, collect all conflicts between these two collections of field. - collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fieldMap2); - // (I) Then collect conflicts between the first collection of fields and + const [fieldMap2, fragmentNames2] = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType2, selectionSet2); // (H) First, collect all conflicts between these two collections of field. + + collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fieldMap2); // (I) Then collect conflicts between the first collection of fields and // those referenced by each fragment name associated with the second. + for (const fragmentName2 of fragmentNames2) { collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fragmentName2); - } - // (I) Then collect conflicts between the second collection of fields and + } // (I) Then collect conflicts between the second collection of fields and // those referenced by each fragment name associated with the first. + for (const fragmentName1 of fragmentNames1) { collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap2, fragmentName1); - } - // (J) Also collect conflicts between any fragment names by the first and + } // (J) Also collect conflicts between any fragment names by the first and // fragment names by the second. This compares each item in the first set of // names to each item in the second set of names. + for (const fragmentName1 of fragmentNames1) { for (const fragmentName2 of fragmentNames2) { collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentName1, fragmentName2); } } return conflicts; -} -// Collect all Conflicts "within" one collection of fields. +} // Collect all Conflicts "within" one collection of fields. + function collectConflictsWithin(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, fieldMap) { // A field map is a keyed collection, where each key represents a response // name and the value at that key is a list of all fields which provide that // response name. For every response name, if there are multiple fields, they // must be compared to find a potential conflict. - for (const [responseName, fields] of fieldMap.entries()) { + for (const [responseName, fields] of Object.entries(fieldMap)) { // This compares every field in the list to every other field in this list // (except to itself). If the list only has one item, nothing needs to // be compared. @@ -39943,21 +39095,21 @@ function collectConflictsWithin(context, conflicts, cachedFieldsAndFragmentNames } } } -} -// Collect all Conflicts between two collections of fields. This is similar to, +} // Collect all Conflicts between two collections of fields. This is similar to, // but different from the `collectConflictsWithin` function above. This check // assumes that `collectConflictsWithin` has already been called on each // provided collection of fields. This is true because this validator traverses // each individual selection set. + function collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, fieldMap1, fieldMap2) { // A field map is a keyed collection, where each key represents a response // name and the value at that key is a list of all fields which provide that // response name. For any response name which appears in both provided field // maps, each field from the first field map must be compared to every field // in the second field map to find potential conflicts. - for (const [responseName, fields1] of fieldMap1.entries()) { - const fields2 = fieldMap2.get(responseName); - if (fields2 != null) { + for (const [responseName, fields1] of Object.entries(fieldMap1)) { + const fields2 = fieldMap2[responseName]; + if (fields2) { for (const field1 of fields1) { for (const field2 of fields2) { const conflict = findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, responseName, field1, field2); @@ -39968,14 +39120,12 @@ function collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentName } } } -} -// Determines if there is a conflict between two particular fields, including +} // Determines if there is a conflict between two particular fields, including // comparing their sub-fields. + function findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, responseName, field1, field2) { - var _node1$directives, _node2$directives; const [parentType1, node1, def1] = field1; - const [parentType2, node2, def2] = field2; - // If it is known that two fields could not possibly apply at the same + const [parentType2, node2, def2] = field2; // If it is known that two fields could not possibly apply at the same // time, due to the parent types, then it is safe to permit them to diverge // in aliased field or arguments used as they will not present any ambiguity // by differing. @@ -39983,6 +39133,7 @@ function findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPai // different Object types. Interface or Union types might overlap - if not // in the current state of the schema, then perhaps in some future version, // thus may not safely diverge. + const areMutuallyExclusive = parentFieldsAreMutuallyExclusive || parentType1 !== parentType2 && (0, _definition.isObjectType)(parentType1) && (0, _definition.isObjectType)(parentType2); if (!areMutuallyExclusive) { // Two aliases must refer to the same field. @@ -39990,27 +39141,21 @@ function findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPai const name2 = node2.name.value; if (name1 !== name2) { return [[responseName, `"${name1}" and "${name2}" are different fields`], [node1], [node2]]; - } - // Two field calls must have the same arguments. + } // Two field calls must have the same arguments. + if (!sameArguments(node1, node2)) { return [[responseName, 'they have differing arguments'], [node1], [node2]]; } - } - // FIXME https://github.com/graphql/graphql-js/issues/2203 - const directives1 = /* c8 ignore next */(_node1$directives = node1.directives) !== null && _node1$directives !== void 0 ? _node1$directives : []; - const directives2 = /* c8 ignore next */(_node2$directives = node2.directives) !== null && _node2$directives !== void 0 ? _node2$directives : []; - if (!sameStreams(directives1, directives2)) { - return [[responseName, 'they have differing stream directives'], [node1], [node2]]; - } - // The return type for each field. + } // The return type for each field. + const type1 = def1 === null || def1 === void 0 ? void 0 : def1.type; const type2 = def2 === null || def2 === void 0 ? void 0 : def2.type; if (type1 && type2 && doTypesConflict(type1, type2)) { return [[responseName, `they return conflicting types "${(0, _inspect.inspect)(type1)}" and "${(0, _inspect.inspect)(type2)}"`], [node1], [node2]]; - } - // Collect and compare sub-fields. Use the same "visited fragment names" list + } // Collect and compare sub-fields. Use the same "visited fragment names" list // for both collections so fields in a fragment reference are never // compared to themselves. + const selectionSet1 = node1.selectionSet; const selectionSet2 = node2.selectionSet; if (selectionSet1 && selectionSet2) { @@ -40027,8 +39172,12 @@ function sameArguments(node1, node2) { if (args2 === undefined || args2.length === 0) { return false; } + /* c8 ignore next */ + if (args1.length !== args2.length) { + /* c8 ignore next */ return false; + /* c8 ignore next */ } const values2 = new Map(args2.map(({ name, @@ -40045,26 +39194,10 @@ function sameArguments(node1, node2) { } function stringifyValue(value) { return (0, _printer.print)((0, _sortValueNode.sortValueNode)(value)); -} -function getStreamDirective(directives) { - return directives.find(directive => directive.name.value === 'stream'); -} -function sameStreams(directives1, directives2) { - const stream1 = getStreamDirective(directives1); - const stream2 = getStreamDirective(directives2); - if (!stream1 && !stream2) { - // both fields do not have streams - return true; - } else if (stream1 && stream2) { - // check if both fields have equivalent streams - return sameArguments(stream1, stream2); - } - // fields have a mix of stream and no stream - return false; -} -// Two types conflict if both types could not apply to a value simultaneously. +} // Two types conflict if both types could not apply to a value simultaneously. // Composite types are ignored as their individual field types will be compared // later recursively. However List and Non-Null types must match. + function doTypesConflict(type1, type2) { if ((0, _definition.isListType)(type1)) { return (0, _definition.isListType)(type2) ? doTypesConflict(type1.ofType, type2.ofType) : true; @@ -40082,24 +39215,24 @@ function doTypesConflict(type1, type2) { return type1 !== type2; } return false; -} -// Given a selection set, return the collection of fields (a mapping of response +} // Given a selection set, return the collection of fields (a mapping of response // name to field nodes and definitions) as well as a list of fragment names // referenced via fragment spreads. + function getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType, selectionSet) { const cached = cachedFieldsAndFragmentNames.get(selectionSet); if (cached) { return cached; } - const nodeAndDefs = new Map(); - const fragmentNames = new Set(); + const nodeAndDefs = Object.create(null); + const fragmentNames = Object.create(null); _collectFieldsAndFragmentNames(context, parentType, selectionSet, nodeAndDefs, fragmentNames); - const result = [nodeAndDefs, [...fragmentNames]]; + const result = [nodeAndDefs, Object.keys(fragmentNames)]; cachedFieldsAndFragmentNames.set(selectionSet, result); return result; -} -// Given a reference to a fragment, return the represented collection of fields +} // Given a reference to a fragment, return the represented collection of fields // as well as a list of nested fragment names referenced via fragment spreads. + function getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment) { // Short-circuit building a type from the node if possible. const cached = cachedFieldsAndFragmentNames.get(fragment.selectionSet); @@ -40120,16 +39253,14 @@ function _collectFieldsAndFragmentNames(context, parentType, selectionSet, nodeA fieldDef = parentType.getFields()[fieldName]; } const responseName = selection.alias ? selection.alias.value : fieldName; - let nodeAndDefsList = nodeAndDefs.get(responseName); - if (nodeAndDefsList == null) { - nodeAndDefsList = []; - nodeAndDefs.set(responseName, nodeAndDefsList); + if (!nodeAndDefs[responseName]) { + nodeAndDefs[responseName] = []; } - nodeAndDefsList.push([parentType, selection, fieldDef]); + nodeAndDefs[responseName].push([parentType, selection, fieldDef]); break; } case _kinds.Kind.FRAGMENT_SPREAD: - fragmentNames.add(selection.name.value); + fragmentNames[selection.name.value] = true; break; case _kinds.Kind.INLINE_FRAGMENT: { @@ -40140,9 +39271,9 @@ function _collectFieldsAndFragmentNames(context, parentType, selectionSet, nodeA } } } -} -// Given a series of Conflicts which occurred between two sub-fields, generate +} // Given a series of Conflicts which occurred between two sub-fields, generate // a single Conflict. + function subfieldConflicts(conflicts, responseName, node1, node2) { if (conflicts.length > 0) { return [[responseName, conflicts.map(([reason]) => reason)], [node1, ...conflicts.map(([, fields1]) => fields1).flat()], [node2, ...conflicts.map(([,, fields2]) => fields2).flat()]]; @@ -40151,6 +39282,7 @@ function subfieldConflicts(conflicts, responseName, node1, node2) { /** * A way to keep track of pairs of things when the ordering of the pair does not matter. */ + class PairSet { constructor() { this._data = new Map(); @@ -40161,10 +39293,10 @@ class PairSet { const result = (_this$_data$get = this._data.get(key1)) === null || _this$_data$get === void 0 ? void 0 : _this$_data$get.get(key2); if (result === undefined) { return false; - } - // areMutuallyExclusive being false is a superset of being true, hence if + } // areMutuallyExclusive being false is a superset of being true, hence if // we want to know if this PairSet "has" these two with no exclusivity, // we have to ensure it was added as such. + return areMutuallyExclusive ? true : areMutuallyExclusive === result; } add(a, b, areMutuallyExclusive) { @@ -40270,10 +39402,10 @@ var _definition = __webpack_require__(/*! ../../type/definition.mjs */ "../../.. */ function PossibleTypeExtensionsRule(context) { const schema = context.getSchema(); - const definedTypes = new Map(); + const definedTypes = Object.create(null); for (const def of context.getDocument().definitions) { if ((0, _predicates.isTypeDefinitionNode)(def)) { - definedTypes.set(def.name.value, def); + definedTypes[def.name.value] = def; } } return { @@ -40286,15 +39418,15 @@ function PossibleTypeExtensionsRule(context) { }; function checkExtension(node) { const typeName = node.name.value; - const defNode = definedTypes.get(typeName); + const defNode = definedTypes[typeName]; const existingType = schema === null || schema === void 0 ? void 0 : schema.getType(typeName); let expectedKind; - if (defNode != null) { + if (defNode) { expectedKind = defKindToExtKind[defNode.kind]; } else if (existingType) { expectedKind = typeToExtKind(existingType); } - if (expectedKind != null) { + if (expectedKind) { if (expectedKind !== node.kind) { const kindStr = extensionKindToTypeName(node.kind); context.reportError(new _GraphQLError.GraphQLError(`Cannot extend non-${kindStr} type "${typeName}".`, { @@ -40302,8 +39434,10 @@ function PossibleTypeExtensionsRule(context) { })); } } else { - var _schema$getTypeMap; - const allTypeNames = [...definedTypes.keys(), ...Object.keys((_schema$getTypeMap = schema === null || schema === void 0 ? void 0 : schema.getTypeMap()) !== null && _schema$getTypeMap !== void 0 ? _schema$getTypeMap : {})]; + const allTypeNames = Object.keys({ + ...definedTypes, + ...(schema === null || schema === void 0 ? void 0 : schema.getTypeMap()) + }); const suggestedTypes = (0, _suggestionList.suggestionList)(typeName, allTypeNames); context.reportError(new _GraphQLError.GraphQLError(`Cannot extend type "${typeName}" because it is not defined.` + (0, _didYouMean.didYouMean)(suggestedTypes), { nodes: node.name @@ -40340,6 +39474,7 @@ function typeToExtKind(type) { } /* c8 ignore next 3 */ // Not reachable. All possible types have been considered + false || (0, _invariant.invariant)(false, 'Unexpected type: ' + (0, _inspect.inspect)(type)); } function extensionKindToTypeName(kind) { @@ -40357,7 +39492,9 @@ function extensionKindToTypeName(kind) { case _kinds.Kind.INPUT_OBJECT_TYPE_EXTENSION: return 'input object'; // Not reachable. All possible types have been considered - /* c8 ignore next 2 */ + + /* c8 ignore next */ + default: false || (0, _invariant.invariant)(false, 'Unexpected kind: ' + (0, _inspect.inspect)(kind)); } @@ -40379,6 +39516,7 @@ Object.defineProperty(exports, "__esModule", ({ exports.ProvidedRequiredArgumentsOnDirectivesRule = ProvidedRequiredArgumentsOnDirectivesRule; exports.ProvidedRequiredArgumentsRule = ProvidedRequiredArgumentsRule; var _inspect = __webpack_require__(/*! ../../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); +var _keyMap = __webpack_require__(/*! ../../jsutils/keyMap.mjs */ "../../../node_modules/graphql/jsutils/keyMap.mjs"); var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); var _kinds = __webpack_require__(/*! ../../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); var _printer = __webpack_require__(/*! ../../language/printer.mjs */ "../../../node_modules/graphql/language/printer.mjs"); @@ -40402,7 +39540,8 @@ function ProvidedRequiredArgumentsRule(context) { if (!fieldDef) { return false; } - const providedArgs = new Set( // FIXME: https://github.com/graphql/graphql-js/issues/2203 + const providedArgs = new Set( + // FIXME: https://github.com/graphql/graphql-js/issues/2203 /* c8 ignore next */ (_fieldNode$arguments = fieldNode.arguments) === null || _fieldNode$arguments === void 0 ? void 0 : _fieldNode$arguments.map(arg => arg.name.value)); for (const argDef of fieldDef.args) { @@ -40420,22 +39559,25 @@ function ProvidedRequiredArgumentsRule(context) { /** * @internal */ + function ProvidedRequiredArgumentsOnDirectivesRule(context) { var _schema$getDirectives; - const requiredArgsMap = new Map(); + const requiredArgsMap = Object.create(null); const schema = context.getSchema(); const definedDirectives = (_schema$getDirectives = schema === null || schema === void 0 ? void 0 : schema.getDirectives()) !== null && _schema$getDirectives !== void 0 ? _schema$getDirectives : _directives.specifiedDirectives; for (const directive of definedDirectives) { - requiredArgsMap.set(directive.name, new Map(directive.args.filter(_definition.isRequiredArgument).map(arg => [arg.name, arg]))); + requiredArgsMap[directive.name] = (0, _keyMap.keyMap)(directive.args.filter(_definition.isRequiredArgument), arg => arg.name); } const astDefinitions = context.getDocument().definitions; for (const def of astDefinitions) { if (def.kind === _kinds.Kind.DIRECTIVE_DEFINITION) { var _def$arguments; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const argNodes = (_def$arguments = def.arguments) !== null && _def$arguments !== void 0 ? _def$arguments : []; - requiredArgsMap.set(def.name.value, new Map(argNodes.filter(isRequiredArgumentNode).map(arg => [arg.name.value, arg]))); + requiredArgsMap[def.name.value] = (0, _keyMap.keyMap)(argNodes.filter(isRequiredArgumentNode), arg => arg.name.value); } } return { @@ -40443,14 +39585,16 @@ function ProvidedRequiredArgumentsOnDirectivesRule(context) { // Validate on leave to allow for deeper errors to appear first. leave(directiveNode) { const directiveName = directiveNode.name.value; - const requiredArgs = requiredArgsMap.get(directiveName); - if (requiredArgs != null) { + const requiredArgs = requiredArgsMap[directiveName]; + if (requiredArgs) { var _directiveNode$argume; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const argNodes = (_directiveNode$argume = directiveNode.arguments) !== null && _directiveNode$argume !== void 0 ? _directiveNode$argume : []; const argNodeMap = new Set(argNodes.map(arg => arg.name.value)); - for (const [argName, argDef] of requiredArgs.entries()) { + for (const [argName, argDef] of Object.entries(requiredArgs)) { if (!argNodeMap.has(argName)) { const argType = (0, _definition.isType)(argDef.type) ? (0, _inspect.inspect)(argDef.type) : (0, _printer.print)(argDef.type); context.reportError(new _GraphQLError.GraphQLError(`Directive "@${directiveName}" argument "${argName}" of type "${argType}" is required, but it was not provided.`, { @@ -40533,9 +39677,6 @@ exports.SingleFieldSubscriptionsRule = SingleFieldSubscriptionsRule; var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); var _kinds = __webpack_require__(/*! ../../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); var _collectFields = __webpack_require__(/*! ../../execution/collectFields.mjs */ "../../../node_modules/graphql/execution/collectFields.mjs"); -function toNodes(fieldGroup) { - return fieldGroup.map(fieldDetails => fieldDetails.node); -} /** * Subscriptions must only include a non-introspection field. * @@ -40560,22 +39701,21 @@ function SingleFieldSubscriptionsRule(context) { fragments[definition.name.value] = definition; } } - const { - groupedFieldSet - } = (0, _collectFields.collectFields)(schema, fragments, variableValues, subscriptionType, node); - if (groupedFieldSet.size > 1) { - const fieldGroups = [...groupedFieldSet.values()]; - const extraFieldGroups = fieldGroups.slice(1); - const extraFieldSelections = extraFieldGroups.flatMap(fieldGroup => toNodes(fieldGroup)); + const fields = (0, _collectFields.collectFields)(schema, fragments, variableValues, subscriptionType, node.selectionSet); + if (fields.size > 1) { + const fieldSelectionLists = [...fields.values()]; + const extraFieldSelectionLists = fieldSelectionLists.slice(1); + const extraFieldSelections = extraFieldSelectionLists.flat(); context.reportError(new _GraphQLError.GraphQLError(operationName != null ? `Subscription "${operationName}" must select only one top level field.` : 'Anonymous Subscription must select only one top level field.', { nodes: extraFieldSelections })); } - for (const fieldGroup of groupedFieldSet.values()) { - const fieldName = toNodes(fieldGroup)[0].name.value; + for (const fieldNodes of fields.values()) { + const field = fieldNodes[0]; + const fieldName = field.name.value; if (fieldName.startsWith('__')) { context.reportError(new _GraphQLError.GraphQLError(operationName != null ? `Subscription "${operationName}" must not select an introspection top level field.` : 'Anonymous Subscription must not select an introspection top level field.', { - nodes: toNodes(fieldGroup) + nodes: fieldNodes })); } } @@ -40587,42 +39727,6 @@ function SingleFieldSubscriptionsRule(context) { /***/ }), -/***/ "../../../node_modules/graphql/validation/rules/StreamDirectiveOnListFieldRule.mjs": -/*!*****************************************************************************************!*\ - !*** ../../../node_modules/graphql/validation/rules/StreamDirectiveOnListFieldRule.mjs ***! - \*****************************************************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports.StreamDirectiveOnListFieldRule = StreamDirectiveOnListFieldRule; -var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); -var _definition = __webpack_require__(/*! ../../type/definition.mjs */ "../../../node_modules/graphql/type/definition.mjs"); -var _directives = __webpack_require__(/*! ../../type/directives.mjs */ "../../../node_modules/graphql/type/directives.mjs"); -/** - * Stream directives are used on list fields - * - * A GraphQL document is only valid if stream directives are used on list fields. - */ -function StreamDirectiveOnListFieldRule(context) { - return { - Directive(node) { - const fieldDef = context.getFieldDef(); - const parentType = context.getParentType(); - if (fieldDef && parentType && node.name.value === _directives.GraphQLStreamDirective.name && !((0, _definition.isListType)(fieldDef.type) || (0, _definition.isWrappingType)(fieldDef.type) && (0, _definition.isListType)(fieldDef.type.ofType))) { - context.reportError(new _GraphQLError.GraphQLError(`Stream directive cannot be used on non-list field "${fieldDef.name}" on type "${parentType.name}".`, { - nodes: node - })); - } - } - }; -} - -/***/ }), - /***/ "../../../node_modules/graphql/validation/rules/UniqueArgumentDefinitionNamesRule.mjs": /*!********************************************************************************************!*\ !*** ../../../node_modules/graphql/validation/rules/UniqueArgumentDefinitionNamesRule.mjs ***! @@ -40647,7 +39751,9 @@ function UniqueArgumentDefinitionNamesRule(context) { return { DirectiveDefinition(directiveNode) { var _directiveNode$argume; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const argumentNodes = (_directiveNode$argume = directiveNode.arguments) !== null && _directiveNode$argume !== void 0 ? _directiveNode$argume : []; return checkArgUniqueness(`@${directiveNode.name.value}`, argumentNodes); @@ -40659,15 +39765,17 @@ function UniqueArgumentDefinitionNamesRule(context) { }; function checkArgUniquenessPerField(typeNode) { var _typeNode$fields; - const typeName = typeNode.name.value; - // FIXME: https://github.com/graphql/graphql-js/issues/2203 + const typeName = typeNode.name.value; // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + const fieldNodes = (_typeNode$fields = typeNode.fields) !== null && _typeNode$fields !== void 0 ? _typeNode$fields : []; for (const fieldDef of fieldNodes) { var _fieldDef$arguments; - const fieldName = fieldDef.name.value; - // FIXME: https://github.com/graphql/graphql-js/issues/2203 + const fieldName = fieldDef.name.value; // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + const argumentNodes = (_fieldDef$arguments = fieldDef.arguments) !== null && _fieldDef$arguments !== void 0 ? _fieldDef$arguments : []; checkArgUniqueness(`${typeName}.${fieldName}`, argumentNodes); } @@ -40717,7 +39825,9 @@ function UniqueArgumentNamesRule(context) { }; function checkArgUniqueness(parentNode) { var _parentNode$arguments; + // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const argumentNodes = (_parentNode$arguments = parentNode.arguments) !== null && _parentNode$arguments !== void 0 ? _parentNode$arguments : []; const seenArgs = (0, _groupBy.groupBy)(argumentNodes, arg => arg.name.value); @@ -40752,7 +39862,7 @@ var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../ * A GraphQL document is only valid if all defined directives have unique names. */ function UniqueDirectiveNamesRule(context) { - const knownDirectiveNames = new Map(); + const knownDirectiveNames = Object.create(null); const schema = context.getSchema(); return { DirectiveDefinition(node) { @@ -40763,13 +39873,12 @@ function UniqueDirectiveNamesRule(context) { })); return; } - const knownName = knownDirectiveNames.get(directiveName); - if (knownName) { + if (knownDirectiveNames[directiveName]) { context.reportError(new _GraphQLError.GraphQLError(`There can be only one directive named "@${directiveName}".`, { - nodes: [knownName, node.name] + nodes: [knownDirectiveNames[directiveName], node.name] })); } else { - knownDirectiveNames.set(directiveName, node.name); + knownDirectiveNames[directiveName] = node.name; } return false; } @@ -40803,20 +39912,20 @@ var _directives = __webpack_require__(/*! ../../type/directives.mjs */ "../../.. * See https://spec.graphql.org/draft/#sec-Directives-Are-Unique-Per-Location */ function UniqueDirectivesPerLocationRule(context) { - const uniqueDirectiveMap = new Map(); + const uniqueDirectiveMap = Object.create(null); const schema = context.getSchema(); const definedDirectives = schema ? schema.getDirectives() : _directives.specifiedDirectives; for (const directive of definedDirectives) { - uniqueDirectiveMap.set(directive.name, !directive.isRepeatable); + uniqueDirectiveMap[directive.name] = !directive.isRepeatable; } const astDefinitions = context.getDocument().definitions; for (const def of astDefinitions) { if (def.kind === _kinds.Kind.DIRECTIVE_DEFINITION) { - uniqueDirectiveMap.set(def.name.value, !def.repeatable); + uniqueDirectiveMap[def.name.value] = !def.repeatable; } } - const schemaDirectives = new Map(); - const typeDirectivesMap = new Map(); + const schemaDirectives = Object.create(null); + const typeDirectivesMap = Object.create(null); return { // Many different AST nodes may contain directives. Rather than listing // them all, just listen for entering any node, and check to see if it @@ -40830,24 +39939,22 @@ function UniqueDirectivesPerLocationRule(context) { seenDirectives = schemaDirectives; } else if ((0, _predicates.isTypeDefinitionNode)(node) || (0, _predicates.isTypeExtensionNode)(node)) { const typeName = node.name.value; - seenDirectives = typeDirectivesMap.get(typeName); + seenDirectives = typeDirectivesMap[typeName]; if (seenDirectives === undefined) { - seenDirectives = new Map(); - typeDirectivesMap.set(typeName, seenDirectives); + typeDirectivesMap[typeName] = seenDirectives = Object.create(null); } } else { - seenDirectives = new Map(); + seenDirectives = Object.create(null); } for (const directive of node.directives) { const directiveName = directive.name.value; - if (uniqueDirectiveMap.get(directiveName) === true) { - const seenDirective = seenDirectives.get(directiveName); - if (seenDirective != null) { + if (uniqueDirectiveMap[directiveName]) { + if (seenDirectives[directiveName]) { context.reportError(new _GraphQLError.GraphQLError(`The directive "@${directiveName}" can only be used once at this location.`, { - nodes: [seenDirective, directive] + nodes: [seenDirectives[directiveName], directive] })); } else { - seenDirectives.set(directiveName, directive); + seenDirectives[directiveName] = directive; } } } @@ -40879,7 +39986,7 @@ var _definition = __webpack_require__(/*! ../../type/definition.mjs */ "../../.. function UniqueEnumValueNamesRule(context) { const schema = context.getSchema(); const existingTypeMap = schema ? schema.getTypeMap() : Object.create(null); - const knownValueNames = new Map(); + const knownValueNames = Object.create(null); return { EnumTypeDefinition: checkValueUniqueness, EnumTypeExtension: checkValueUniqueness @@ -40887,14 +39994,14 @@ function UniqueEnumValueNamesRule(context) { function checkValueUniqueness(node) { var _node$values; const typeName = node.name.value; - let valueNames = knownValueNames.get(typeName); - if (valueNames == null) { - valueNames = new Map(); - knownValueNames.set(typeName, valueNames); - } - // FIXME: https://github.com/graphql/graphql-js/issues/2203 + if (!knownValueNames[typeName]) { + knownValueNames[typeName] = Object.create(null); + } // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + const valueNodes = (_node$values = node.values) !== null && _node$values !== void 0 ? _node$values : []; + const valueNames = knownValueNames[typeName]; for (const valueDef of valueNodes) { const valueName = valueDef.name.value; const existingType = existingTypeMap[typeName]; @@ -40902,15 +40009,12 @@ function UniqueEnumValueNamesRule(context) { context.reportError(new _GraphQLError.GraphQLError(`Enum value "${typeName}.${valueName}" already exists in the schema. It cannot also be defined in this type extension.`, { nodes: valueDef.name })); - continue; - } - const knownValueName = valueNames.get(valueName); - if (knownValueName != null) { + } else if (valueNames[valueName]) { context.reportError(new _GraphQLError.GraphQLError(`Enum value "${typeName}.${valueName}" can only be defined once.`, { - nodes: [knownValueName, valueDef.name] + nodes: [valueNames[valueName], valueDef.name] })); } else { - valueNames.set(valueName, valueDef.name); + valueNames[valueName] = valueDef.name; } } return false; @@ -40941,7 +40045,7 @@ var _definition = __webpack_require__(/*! ../../type/definition.mjs */ "../../.. function UniqueFieldDefinitionNamesRule(context) { const schema = context.getSchema(); const existingTypeMap = schema ? schema.getTypeMap() : Object.create(null); - const knownFieldNames = new Map(); + const knownFieldNames = Object.create(null); return { InputObjectTypeDefinition: checkFieldUniqueness, InputObjectTypeExtension: checkFieldUniqueness, @@ -40953,29 +40057,26 @@ function UniqueFieldDefinitionNamesRule(context) { function checkFieldUniqueness(node) { var _node$fields; const typeName = node.name.value; - let fieldNames = knownFieldNames.get(typeName); - if (fieldNames == null) { - fieldNames = new Map(); - knownFieldNames.set(typeName, fieldNames); - } - // FIXME: https://github.com/graphql/graphql-js/issues/2203 + if (!knownFieldNames[typeName]) { + knownFieldNames[typeName] = Object.create(null); + } // FIXME: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ + const fieldNodes = (_node$fields = node.fields) !== null && _node$fields !== void 0 ? _node$fields : []; + const fieldNames = knownFieldNames[typeName]; for (const fieldDef of fieldNodes) { const fieldName = fieldDef.name.value; if (hasField(existingTypeMap[typeName], fieldName)) { context.reportError(new _GraphQLError.GraphQLError(`Field "${typeName}.${fieldName}" already exists in the schema. It cannot also be defined in this type extension.`, { nodes: fieldDef.name })); - continue; - } - const knownFieldName = fieldNames.get(fieldName); - if (knownFieldName != null) { + } else if (fieldNames[fieldName]) { context.reportError(new _GraphQLError.GraphQLError(`Field "${typeName}.${fieldName}" can only be defined once.`, { - nodes: [knownFieldName, fieldDef.name] + nodes: [fieldNames[fieldName], fieldDef.name] })); } else { - fieldNames.set(fieldName, fieldDef.name); + fieldNames[fieldName] = fieldDef.name; } } return false; @@ -41011,18 +40112,17 @@ var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../ * See https://spec.graphql.org/draft/#sec-Fragment-Name-Uniqueness */ function UniqueFragmentNamesRule(context) { - const knownFragmentNames = new Map(); + const knownFragmentNames = Object.create(null); return { OperationDefinition: () => false, FragmentDefinition(node) { const fragmentName = node.name.value; - const knownFragmentName = knownFragmentNames.get(fragmentName); - if (knownFragmentName != null) { + if (knownFragmentNames[fragmentName]) { context.reportError(new _GraphQLError.GraphQLError(`There can be only one fragment named "${fragmentName}".`, { - nodes: [knownFragmentName, node.name] + nodes: [knownFragmentNames[fragmentName], node.name] })); } else { - knownFragmentNames.set(fragmentName, node.name); + knownFragmentNames[fragmentName] = node.name; } return false; } @@ -41055,28 +40155,27 @@ var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../ */ function UniqueInputFieldNamesRule(context) { const knownNameStack = []; - let knownNames = new Map(); + let knownNames = Object.create(null); return { ObjectValue: { enter() { knownNameStack.push(knownNames); - knownNames = new Map(); + knownNames = Object.create(null); }, leave() { const prevKnownNames = knownNameStack.pop(); - prevKnownNames != null || (0, _invariant.invariant)(false); + prevKnownNames || (0, _invariant.invariant)(false); knownNames = prevKnownNames; } }, ObjectField(node) { const fieldName = node.name.value; - const knownName = knownNames.get(fieldName); - if (knownName != null) { + if (knownNames[fieldName]) { context.reportError(new _GraphQLError.GraphQLError(`There can be only one input field named "${fieldName}".`, { - nodes: [knownName, node.name] + nodes: [knownNames[fieldName], node.name] })); } else { - knownNames.set(fieldName, node.name); + knownNames[fieldName] = node.name; } } }; @@ -41105,18 +40204,17 @@ var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../ * See https://spec.graphql.org/draft/#sec-Operation-Name-Uniqueness */ function UniqueOperationNamesRule(context) { - const knownOperationNames = new Map(); + const knownOperationNames = Object.create(null); return { OperationDefinition(node) { const operationName = node.name; - if (operationName != null) { - const knownOperationName = knownOperationNames.get(operationName.value); - if (knownOperationName != null) { + if (operationName) { + if (knownOperationNames[operationName.value]) { context.reportError(new _GraphQLError.GraphQLError(`There can be only one operation named "${operationName.value}".`, { - nodes: [knownOperationName, operationName] + nodes: [knownOperationNames[operationName.value], operationName] })); } else { - knownOperationNames.set(operationName.value, operationName); + knownOperationNames[operationName.value] = operationName; } } return false; @@ -41147,7 +40245,7 @@ var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../ */ function UniqueOperationTypesRule(context) { const schema = context.getSchema(); - const definedOperationTypes = new Map(); + const definedOperationTypes = Object.create(null); const existingOperationTypes = schema ? { query: schema.getQueryType(), mutation: schema.getMutationType(), @@ -41159,12 +40257,14 @@ function UniqueOperationTypesRule(context) { }; function checkOperationTypes(node) { var _node$operationTypes; + // See: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const operationTypesNodes = (_node$operationTypes = node.operationTypes) !== null && _node$operationTypes !== void 0 ? _node$operationTypes : []; for (const operationType of operationTypesNodes) { const operation = operationType.operation; - const alreadyDefinedOperationType = definedOperationTypes.get(operation); + const alreadyDefinedOperationType = definedOperationTypes[operation]; if (existingOperationTypes[operation]) { context.reportError(new _GraphQLError.GraphQLError(`Type for ${operation} already defined in the schema. It cannot be redefined.`, { nodes: operationType @@ -41174,7 +40274,7 @@ function UniqueOperationTypesRule(context) { nodes: [alreadyDefinedOperationType, operationType] })); } else { - definedOperationTypes.set(operation, operationType); + definedOperationTypes[operation] = operationType; } } return false; @@ -41202,7 +40302,7 @@ var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../ * A GraphQL document is only valid if all defined types have unique names. */ function UniqueTypeNamesRule(context) { - const knownTypeNames = new Map(); + const knownTypeNames = Object.create(null); const schema = context.getSchema(); return { ScalarTypeDefinition: checkTypeName, @@ -41220,13 +40320,12 @@ function UniqueTypeNamesRule(context) { })); return; } - const knownNameNode = knownTypeNames.get(typeName); - if (knownNameNode != null) { + if (knownTypeNames[typeName]) { context.reportError(new _GraphQLError.GraphQLError(`There can be only one type named "${typeName}".`, { - nodes: [knownNameNode, node.name] + nodes: [knownTypeNames[typeName], node.name] })); } else { - knownTypeNames.set(typeName, node.name); + knownTypeNames[typeName] = node.name; } return false; } @@ -41257,7 +40356,9 @@ function UniqueVariableNamesRule(context) { return { OperationDefinition(operationNode) { var _operationNode$variab; + // See: https://github.com/graphql/graphql-js/issues/2203 + /* c8 ignore next */ const variableDefinitions = (_operationNode$variab = operationNode.variableDefinitions) !== null && _operationNode$variab !== void 0 ? _operationNode$variab : []; const seenVariableDefinitions = (0, _groupBy.groupBy)(variableDefinitions, node => node.variable.name.value); @@ -41288,6 +40389,7 @@ Object.defineProperty(exports, "__esModule", ({ exports.ValuesOfCorrectTypeRule = ValuesOfCorrectTypeRule; var _didYouMean = __webpack_require__(/*! ../../jsutils/didYouMean.mjs */ "../../../node_modules/graphql/jsutils/didYouMean.mjs"); var _inspect = __webpack_require__(/*! ../../jsutils/inspect.mjs */ "../../../node_modules/graphql/jsutils/inspect.mjs"); +var _keyMap = __webpack_require__(/*! ../../jsutils/keyMap.mjs */ "../../../node_modules/graphql/jsutils/keyMap.mjs"); var _suggestionList = __webpack_require__(/*! ../../jsutils/suggestionList.mjs */ "../../../node_modules/graphql/jsutils/suggestionList.mjs"); var _GraphQLError = __webpack_require__(/*! ../../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); var _kinds = __webpack_require__(/*! ../../language/kinds.mjs */ "../../../node_modules/graphql/language/kinds.mjs"); @@ -41326,11 +40428,11 @@ function ValuesOfCorrectTypeRule(context) { if (!(0, _definition.isInputObjectType)(type)) { isValidValueNode(context, node); return false; // Don't traverse further. - } - // Ensure every required field exists. - const fieldNodeMap = new Map(node.fields.map(field => [field.name.value, field])); + } // Ensure every required field exists. + + const fieldNodeMap = (0, _keyMap.keyMap)(node.fields, field => field.name.value); for (const fieldDef of Object.values(type.getFields())) { - const fieldNode = fieldNodeMap.get(fieldDef.name); + const fieldNode = fieldNodeMap[fieldDef.name]; if (!fieldNode && (0, _definition.isRequiredInputField)(fieldDef)) { const typeStr = (0, _inspect.inspect)(fieldDef.type); context.reportError(new _GraphQLError.GraphQLError(`Field "${type.name}.${fieldDef.name}" of required type "${typeStr}" was not provided.`, { @@ -41371,6 +40473,7 @@ function ValuesOfCorrectTypeRule(context) { * Any value literal may be a valid representation of a Scalar, depending on * that scalar type. */ + function isValidValueNode(context, node) { // Report any error at the full type expected by the location. const locationType = context.getInputType(); @@ -41384,11 +40487,12 @@ function isValidValueNode(context, node) { nodes: node })); return; - } - // Scalars and Enums determine if a literal value is valid via parseLiteral(), + } // Scalars and Enums determine if a literal value is valid via parseLiteral(), // which may throw or return an invalid value to indicate failure. + try { - const parseResult = type.parseLiteral(node, undefined /* variables */); + const parseResult = type.parseLiteral(node, undefined + /* variables */); if (parseResult === undefined) { const typeStr = (0, _inspect.inspect)(locationType); context.reportError(new _GraphQLError.GraphQLError(`Expected value of type "${typeStr}", found ${(0, _printer.print)(node)}.`, { @@ -41408,8 +40512,8 @@ function isValidValueNode(context, node) { } } function validateOneOfInputObject(context, node, type, fieldNodeMap, variableDefinitions) { - var _fieldNodeMap$get; - const keys = Array.from(fieldNodeMap.keys()); + var _fieldNodeMap$keys$; + const keys = Object.keys(fieldNodeMap); const isNotExactlyOneField = keys.length !== 1; if (isNotExactlyOneField) { context.reportError(new _GraphQLError.GraphQLError(`OneOf Input Object "${type.name}" must specify exactly one key.`, { @@ -41417,7 +40521,7 @@ function validateOneOfInputObject(context, node, type, fieldNodeMap, variableDef })); return; } - const value = (_fieldNodeMap$get = fieldNodeMap.get(keys[0])) === null || _fieldNodeMap$get === void 0 ? void 0 : _fieldNodeMap$get.value; + const value = (_fieldNodeMap$keys$ = fieldNodeMap[keys[0]]) === null || _fieldNodeMap$keys$ === void 0 ? void 0 : _fieldNodeMap$keys$.value; const isNullLiteral = !value || value.kind === _kinds.Kind.NULL; const isVariable = (value === null || value === void 0 ? void 0 : value.kind) === _kinds.Kind.VARIABLE; if (isNullLiteral) { @@ -41507,11 +40611,11 @@ var _typeFromAST = __webpack_require__(/*! ../../utilities/typeFromAST.mjs */ ". * See https://spec.graphql.org/draft/#sec-All-Variable-Usages-are-Allowed */ function VariablesInAllowedPositionRule(context) { - let varDefMap; + let varDefMap = Object.create(null); return { OperationDefinition: { enter() { - varDefMap = new Map(); + varDefMap = Object.create(null); }, leave(operation) { const usages = context.getRecursiveVariableUsages(operation); @@ -41521,7 +40625,7 @@ function VariablesInAllowedPositionRule(context) { defaultValue } of usages) { const varName = node.name.value; - const varDef = varDefMap.get(varName); + const varDef = varDefMap[varName]; if (varDef && type) { // A var type is allowed if it is the same or more strict (e.g. is // a subtype of) than the expected type. It can be more strict if @@ -41542,7 +40646,7 @@ function VariablesInAllowedPositionRule(context) { } }, VariableDefinition(node) { - varDefMap.set(node.variable.name.value, node); + varDefMap[node.variable.name.value] = node; } }; } @@ -41551,6 +40655,7 @@ function VariablesInAllowedPositionRule(context) { * which includes considering if default values exist for either the variable * or the location at which it is located. */ + function allowedVariableUsage(schema, varType, varDefaultValue, locationType, locationDefaultValue) { if ((0, _definition.isNonNullType)(locationType) && !(0, _definition.isNonNullType)(varType)) { const hasNonNullVariableDefaultValue = varDefaultValue != null && varDefaultValue.kind !== _kinds.Kind.NULL; @@ -41703,9 +40808,6 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.specifiedSDLRules = exports.specifiedRules = exports.recommendedRules = void 0; -var _DeferStreamDirectiveLabelRule = __webpack_require__(/*! ./rules/DeferStreamDirectiveLabelRule.mjs */ "../../../node_modules/graphql/validation/rules/DeferStreamDirectiveLabelRule.mjs"); -var _DeferStreamDirectiveOnRootFieldRule = __webpack_require__(/*! ./rules/DeferStreamDirectiveOnRootFieldRule.mjs */ "../../../node_modules/graphql/validation/rules/DeferStreamDirectiveOnRootFieldRule.mjs"); -var _DeferStreamDirectiveOnValidOperationsRule = __webpack_require__(/*! ./rules/DeferStreamDirectiveOnValidOperationsRule.mjs */ "../../../node_modules/graphql/validation/rules/DeferStreamDirectiveOnValidOperationsRule.mjs"); var _ExecutableDefinitionsRule = __webpack_require__(/*! ./rules/ExecutableDefinitionsRule.mjs */ "../../../node_modules/graphql/validation/rules/ExecutableDefinitionsRule.mjs"); var _FieldsOnCorrectTypeRule = __webpack_require__(/*! ./rules/FieldsOnCorrectTypeRule.mjs */ "../../../node_modules/graphql/validation/rules/FieldsOnCorrectTypeRule.mjs"); var _FragmentsOnCompositeTypesRule = __webpack_require__(/*! ./rules/FragmentsOnCompositeTypesRule.mjs */ "../../../node_modules/graphql/validation/rules/FragmentsOnCompositeTypesRule.mjs"); @@ -41726,7 +40828,6 @@ var _PossibleTypeExtensionsRule = __webpack_require__(/*! ./rules/PossibleTypeEx var _ProvidedRequiredArgumentsRule = __webpack_require__(/*! ./rules/ProvidedRequiredArgumentsRule.mjs */ "../../../node_modules/graphql/validation/rules/ProvidedRequiredArgumentsRule.mjs"); var _ScalarLeafsRule = __webpack_require__(/*! ./rules/ScalarLeafsRule.mjs */ "../../../node_modules/graphql/validation/rules/ScalarLeafsRule.mjs"); var _SingleFieldSubscriptionsRule = __webpack_require__(/*! ./rules/SingleFieldSubscriptionsRule.mjs */ "../../../node_modules/graphql/validation/rules/SingleFieldSubscriptionsRule.mjs"); -var _StreamDirectiveOnListFieldRule = __webpack_require__(/*! ./rules/StreamDirectiveOnListFieldRule.mjs */ "../../../node_modules/graphql/validation/rules/StreamDirectiveOnListFieldRule.mjs"); var _UniqueArgumentDefinitionNamesRule = __webpack_require__(/*! ./rules/UniqueArgumentDefinitionNamesRule.mjs */ "../../../node_modules/graphql/validation/rules/UniqueArgumentDefinitionNamesRule.mjs"); var _UniqueArgumentNamesRule = __webpack_require__(/*! ./rules/UniqueArgumentNamesRule.mjs */ "../../../node_modules/graphql/validation/rules/UniqueArgumentNamesRule.mjs"); var _UniqueDirectiveNamesRule = __webpack_require__(/*! ./rules/UniqueDirectiveNamesRule.mjs */ "../../../node_modules/graphql/validation/rules/UniqueDirectiveNamesRule.mjs"); @@ -41742,14 +40843,7 @@ var _UniqueVariableNamesRule = __webpack_require__(/*! ./rules/UniqueVariableNam var _ValuesOfCorrectTypeRule = __webpack_require__(/*! ./rules/ValuesOfCorrectTypeRule.mjs */ "../../../node_modules/graphql/validation/rules/ValuesOfCorrectTypeRule.mjs"); var _VariablesAreInputTypesRule = __webpack_require__(/*! ./rules/VariablesAreInputTypesRule.mjs */ "../../../node_modules/graphql/validation/rules/VariablesAreInputTypesRule.mjs"); var _VariablesInAllowedPositionRule = __webpack_require__(/*! ./rules/VariablesInAllowedPositionRule.mjs */ "../../../node_modules/graphql/validation/rules/VariablesInAllowedPositionRule.mjs"); -// Spec Section: "Defer And Stream Directive Labels Are Unique" - -// Spec Section: "Defer And Stream Directives Are Used On Valid Root Field" - -// Spec Section: "Defer And Stream Directives Are Used On Valid Operations" - // Spec Section: "Executable Definitions" - // Spec Section: "Field Selections on Objects, Interfaces, and Unions Types" // Spec Section: "Fragments on Composite Types" @@ -41786,8 +40880,6 @@ var _VariablesInAllowedPositionRule = __webpack_require__(/*! ./rules/VariablesI // Spec Section: "Subscriptions with Single Root Field" -// Spec Section: "Stream Directives Are Used On List Fields" - // Spec Section: "Argument Uniqueness" // Spec Section: "Directives Are Unique Per Location" @@ -41817,10 +40909,12 @@ const recommendedRules = exports.recommendedRules = Object.freeze([_MaxIntrospec * The order of the rules in this list has been adjusted to lead to the * most clear output when encountering multiple validation errors. */ -const specifiedRules = exports.specifiedRules = Object.freeze([_ExecutableDefinitionsRule.ExecutableDefinitionsRule, _UniqueOperationNamesRule.UniqueOperationNamesRule, _LoneAnonymousOperationRule.LoneAnonymousOperationRule, _SingleFieldSubscriptionsRule.SingleFieldSubscriptionsRule, _KnownTypeNamesRule.KnownTypeNamesRule, _FragmentsOnCompositeTypesRule.FragmentsOnCompositeTypesRule, _VariablesAreInputTypesRule.VariablesAreInputTypesRule, _ScalarLeafsRule.ScalarLeafsRule, _FieldsOnCorrectTypeRule.FieldsOnCorrectTypeRule, _UniqueFragmentNamesRule.UniqueFragmentNamesRule, _KnownFragmentNamesRule.KnownFragmentNamesRule, _NoUnusedFragmentsRule.NoUnusedFragmentsRule, _PossibleFragmentSpreadsRule.PossibleFragmentSpreadsRule, _NoFragmentCyclesRule.NoFragmentCyclesRule, _UniqueVariableNamesRule.UniqueVariableNamesRule, _NoUndefinedVariablesRule.NoUndefinedVariablesRule, _NoUnusedVariablesRule.NoUnusedVariablesRule, _KnownDirectivesRule.KnownDirectivesRule, _UniqueDirectivesPerLocationRule.UniqueDirectivesPerLocationRule, _DeferStreamDirectiveOnRootFieldRule.DeferStreamDirectiveOnRootFieldRule, _DeferStreamDirectiveOnValidOperationsRule.DeferStreamDirectiveOnValidOperationsRule, _DeferStreamDirectiveLabelRule.DeferStreamDirectiveLabelRule, _StreamDirectiveOnListFieldRule.StreamDirectiveOnListFieldRule, _KnownArgumentNamesRule.KnownArgumentNamesRule, _UniqueArgumentNamesRule.UniqueArgumentNamesRule, _ValuesOfCorrectTypeRule.ValuesOfCorrectTypeRule, _ProvidedRequiredArgumentsRule.ProvidedRequiredArgumentsRule, _VariablesInAllowedPositionRule.VariablesInAllowedPositionRule, _OverlappingFieldsCanBeMergedRule.OverlappingFieldsCanBeMergedRule, _UniqueInputFieldNamesRule.UniqueInputFieldNamesRule, ...recommendedRules]); + +const specifiedRules = exports.specifiedRules = Object.freeze([_ExecutableDefinitionsRule.ExecutableDefinitionsRule, _UniqueOperationNamesRule.UniqueOperationNamesRule, _LoneAnonymousOperationRule.LoneAnonymousOperationRule, _SingleFieldSubscriptionsRule.SingleFieldSubscriptionsRule, _KnownTypeNamesRule.KnownTypeNamesRule, _FragmentsOnCompositeTypesRule.FragmentsOnCompositeTypesRule, _VariablesAreInputTypesRule.VariablesAreInputTypesRule, _ScalarLeafsRule.ScalarLeafsRule, _FieldsOnCorrectTypeRule.FieldsOnCorrectTypeRule, _UniqueFragmentNamesRule.UniqueFragmentNamesRule, _KnownFragmentNamesRule.KnownFragmentNamesRule, _NoUnusedFragmentsRule.NoUnusedFragmentsRule, _PossibleFragmentSpreadsRule.PossibleFragmentSpreadsRule, _NoFragmentCyclesRule.NoFragmentCyclesRule, _UniqueVariableNamesRule.UniqueVariableNamesRule, _NoUndefinedVariablesRule.NoUndefinedVariablesRule, _NoUnusedVariablesRule.NoUnusedVariablesRule, _KnownDirectivesRule.KnownDirectivesRule, _UniqueDirectivesPerLocationRule.UniqueDirectivesPerLocationRule, _KnownArgumentNamesRule.KnownArgumentNamesRule, _UniqueArgumentNamesRule.UniqueArgumentNamesRule, _ValuesOfCorrectTypeRule.ValuesOfCorrectTypeRule, _ProvidedRequiredArgumentsRule.ProvidedRequiredArgumentsRule, _VariablesInAllowedPositionRule.VariablesInAllowedPositionRule, _OverlappingFieldsCanBeMergedRule.OverlappingFieldsCanBeMergedRule, _UniqueInputFieldNamesRule.UniqueInputFieldNamesRule, ...recommendedRules]); /** * @internal */ + const specifiedSDLRules = exports.specifiedSDLRules = Object.freeze([_LoneSchemaDefinitionRule.LoneSchemaDefinitionRule, _UniqueOperationTypesRule.UniqueOperationTypesRule, _UniqueTypeNamesRule.UniqueTypeNamesRule, _UniqueEnumValueNamesRule.UniqueEnumValueNamesRule, _UniqueFieldDefinitionNamesRule.UniqueFieldDefinitionNamesRule, _UniqueArgumentDefinitionNamesRule.UniqueArgumentDefinitionNamesRule, _UniqueDirectiveNamesRule.UniqueDirectiveNamesRule, _KnownTypeNamesRule.KnownTypeNamesRule, _KnownDirectivesRule.KnownDirectivesRule, _UniqueDirectivesPerLocationRule.UniqueDirectivesPerLocationRule, _PossibleTypeExtensionsRule.PossibleTypeExtensionsRule, _KnownArgumentNamesRule.KnownArgumentNamesOnDirectivesRule, _UniqueArgumentNamesRule.UniqueArgumentNamesRule, _UniqueInputFieldNamesRule.UniqueInputFieldNamesRule, _ProvidedRequiredArgumentsRule.ProvidedRequiredArgumentsOnDirectivesRule]); /***/ }), @@ -41840,6 +40934,7 @@ exports.assertValidSDL = assertValidSDL; exports.assertValidSDLExtension = assertValidSDLExtension; exports.validate = validate; exports.validateSDL = validateSDL; +var _devAssert = __webpack_require__(/*! ../jsutils/devAssert.mjs */ "../../../node_modules/graphql/jsutils/devAssert.mjs"); var _GraphQLError = __webpack_require__(/*! ../error/GraphQLError.mjs */ "../../../node_modules/graphql/error/GraphQLError.mjs"); var _visitor = __webpack_require__(/*! ../language/visitor.mjs */ "../../../node_modules/graphql/language/visitor.mjs"); var _validate = __webpack_require__(/*! ../type/validate.mjs */ "../../../node_modules/graphql/type/validate.mjs"); @@ -41866,30 +40961,32 @@ var _ValidationContext = __webpack_require__(/*! ./ValidationContext.mjs */ "../ * Optionally a custom TypeInfo instance may be provided. If not provided, one * will be created from the provided schema. */ + function validate(schema, documentAST, rules = _specifiedRules.specifiedRules, options, /** @deprecated will be removed in 17.0.0 */ typeInfo = new _TypeInfo.TypeInfo(schema)) { var _options$maxErrors; const maxErrors = (_options$maxErrors = options === null || options === void 0 ? void 0 : options.maxErrors) !== null && _options$maxErrors !== void 0 ? _options$maxErrors : 100; - // If the schema used for validation is invalid, throw an error. + documentAST || (0, _devAssert.devAssert)(false, 'Must provide document.'); // If the schema used for validation is invalid, throw an error. + (0, _validate.assertValidSchema)(schema); - const abortError = new _GraphQLError.GraphQLError('Too many validation errors, error limit reached. Validation aborted.'); + const abortObj = Object.freeze({}); const errors = []; const context = new _ValidationContext.ValidationContext(schema, documentAST, typeInfo, error => { if (errors.length >= maxErrors) { - throw abortError; + errors.push(new _GraphQLError.GraphQLError('Too many validation errors, error limit reached. Validation aborted.')); // eslint-disable-next-line @typescript-eslint/no-throw-literal + + throw abortObj; } errors.push(error); - }); - // This uses a specialized visitor which runs multiple visitors in parallel, + }); // This uses a specialized visitor which runs multiple visitors in parallel, // while maintaining the visitor skip and break API. - const visitor = (0, _visitor.visitInParallel)(rules.map(rule => rule(context))); - // Visit the whole document with each instance of all provided rules. + + const visitor = (0, _visitor.visitInParallel)(rules.map(rule => rule(context))); // Visit the whole document with each instance of all provided rules. + try { (0, _visitor.visit)(documentAST, (0, _TypeInfo.visitWithTypeInfo)(typeInfo, visitor)); } catch (e) { - if (e === abortError) { - errors.push(abortError); - } else { + if (e !== abortObj) { throw e; } } @@ -41898,6 +40995,7 @@ typeInfo = new _TypeInfo.TypeInfo(schema)) { /** * @internal */ + function validateSDL(documentAST, schemaToExtend, rules = _specifiedRules.specifiedSDLRules) { const errors = []; const context = new _ValidationContext.SDLValidationContext(documentAST, schemaToExtend, error => { @@ -41913,6 +41011,7 @@ function validateSDL(documentAST, schemaToExtend, rules = _specifiedRules.specif * * @internal */ + function assertValidSDL(documentAST) { const errors = validateSDL(documentAST); if (errors.length !== 0) { @@ -41925,6 +41024,7 @@ function assertValidSDL(documentAST) { * * @internal */ + function assertValidSDLExtension(documentAST, schema) { const errors = validateSDL(documentAST, schema); if (errors.length !== 0) { @@ -41948,18 +41048,20 @@ Object.defineProperty(exports, "__esModule", ({ exports.versionInfo = exports.version = void 0; // Note: This file is autogenerated using "resources/gen-version.js" script and // automatically updated by "npm version" command. + /** * A string containing the version of the GraphQL.js library */ -const version = exports.version = '17.0.0-alpha.7'; +const version = exports.version = '16.9.0'; /** * An object containing the components of the GraphQL.js version string */ + const versionInfo = exports.versionInfo = Object.freeze({ - major: 17, - minor: 0, + major: 16, + minor: 9, patch: 0, - preReleaseTag: 'alpha.7' + preReleaseTag: null }); /***/ }), @@ -69309,7 +68411,7 @@ function createContextHook(context) { var _a; const value = React.useContext(context); if (value === null && (options == null ? void 0 : options.nonNull)) { - throw new Error(`Tried to use \`${((_a = options.caller) == null ? void 0 : _a.name) || useGivenContext.caller.name}\` without the necessary context. Make sure to render the \`${context.displayName}Provider\` component higher up the tree.`); + throw new Error(`Tried to use \`${((_a = options.caller) == null ? void 0 : _a.name) || "a component"}\` without the necessary context. Make sure to render the \`${context.displayName}Provider\` component higher up the tree.`); } return value; } diff --git a/netbox/project-static/dist/netbox-external.css b/netbox/project-static/dist/netbox-external.css index 05e350aa8..72d40bc92 100644 --- a/netbox/project-static/dist/netbox-external.css +++ b/netbox/project-static/dist/netbox-external.css @@ -1 +1 @@ -@font-face{font-family:Material Design Icons;src:url(./materialdesignicons-webfont-A5TO3SM5.eot?v=7.4.47);src:url(./materialdesignicons-webfont-A5TO3SM5.eot?#iefix&v=7.4.47) format("embedded-opentype"),url(./materialdesignicons-webfont-7OXPFKOV.woff2?v=7.4.47) format("woff2"),url(./materialdesignicons-webfont-4ZM36VVG.woff?v=7.4.47) format("woff"),url(./materialdesignicons-webfont-OFFE53R5.ttf?v=7.4.47) format("truetype");font-weight:400;font-style:normal}.mdi:before,.mdi-set{display:inline-block;font: 24px/1 Material Design Icons;font-size:inherit;text-rendering:auto;line-height:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.mdi-ab-testing:before{content:"\f01c9"}.mdi-abacus:before{content:"\f16e0"}.mdi-abjad-arabic:before{content:"\f1328"}.mdi-abjad-hebrew:before{content:"\f1329"}.mdi-abugida-devanagari:before{content:"\f132a"}.mdi-abugida-thai:before{content:"\f132b"}.mdi-access-point:before{content:"\f0003"}.mdi-access-point-check:before{content:"\f1538"}.mdi-access-point-minus:before{content:"\f1539"}.mdi-access-point-network:before{content:"\f0002"}.mdi-access-point-network-off:before{content:"\f0be1"}.mdi-access-point-off:before{content:"\f1511"}.mdi-access-point-plus:before{content:"\f153a"}.mdi-access-point-remove:before{content:"\f153b"}.mdi-account:before{content:"\f0004"}.mdi-account-alert:before{content:"\f0005"}.mdi-account-alert-outline:before{content:"\f0b50"}.mdi-account-arrow-down:before{content:"\f1868"}.mdi-account-arrow-down-outline:before{content:"\f1869"}.mdi-account-arrow-left:before{content:"\f0b51"}.mdi-account-arrow-left-outline:before{content:"\f0b52"}.mdi-account-arrow-right:before{content:"\f0b53"}.mdi-account-arrow-right-outline:before{content:"\f0b54"}.mdi-account-arrow-up:before{content:"\f1867"}.mdi-account-arrow-up-outline:before{content:"\f186a"}.mdi-account-badge:before{content:"\f1b0a"}.mdi-account-badge-outline:before{content:"\f1b0b"}.mdi-account-box:before{content:"\f0006"}.mdi-account-box-edit-outline:before{content:"\f1cc8"}.mdi-account-box-minus-outline:before{content:"\f1cc9"}.mdi-account-box-multiple:before{content:"\f0934"}.mdi-account-box-multiple-outline:before{content:"\f100a"}.mdi-account-box-outline:before{content:"\f0007"}.mdi-account-box-plus-outline:before{content:"\f1cca"}.mdi-account-cancel:before{content:"\f12df"}.mdi-account-cancel-outline:before{content:"\f12e0"}.mdi-account-card:before{content:"\f1ba4"}.mdi-account-card-outline:before{content:"\f1ba5"}.mdi-account-cash:before{content:"\f1097"}.mdi-account-cash-outline:before{content:"\f1098"}.mdi-account-check:before{content:"\f0008"}.mdi-account-check-outline:before{content:"\f0be2"}.mdi-account-child:before{content:"\f0a89"}.mdi-account-child-circle:before{content:"\f0a8a"}.mdi-account-child-outline:before{content:"\f10c8"}.mdi-account-circle:before{content:"\f0009"}.mdi-account-circle-outline:before{content:"\f0b55"}.mdi-account-clock:before{content:"\f0b56"}.mdi-account-clock-outline:before{content:"\f0b57"}.mdi-account-cog:before{content:"\f1370"}.mdi-account-cog-outline:before{content:"\f1371"}.mdi-account-convert:before{content:"\f000a"}.mdi-account-convert-outline:before{content:"\f1301"}.mdi-account-cowboy-hat:before{content:"\f0e9b"}.mdi-account-cowboy-hat-outline:before{content:"\f17f3"}.mdi-account-credit-card:before{content:"\f1ba6"}.mdi-account-credit-card-outline:before{content:"\f1ba7"}.mdi-account-details:before{content:"\f0631"}.mdi-account-details-outline:before{content:"\f1372"}.mdi-account-edit:before{content:"\f06bc"}.mdi-account-edit-outline:before{content:"\f0ffb"}.mdi-account-eye:before{content:"\f0420"}.mdi-account-eye-outline:before{content:"\f127b"}.mdi-account-file:before{content:"\f1ca7"}.mdi-account-file-outline:before{content:"\f1ca8"}.mdi-account-file-text:before{content:"\f1ca9"}.mdi-account-file-text-outline:before{content:"\f1caa"}.mdi-account-filter:before{content:"\f0936"}.mdi-account-filter-outline:before{content:"\f0f9d"}.mdi-account-group:before{content:"\f0849"}.mdi-account-group-outline:before{content:"\f0b58"}.mdi-account-hard-hat:before{content:"\f05b5"}.mdi-account-hard-hat-outline:before{content:"\f1a1f"}.mdi-account-heart:before{content:"\f0899"}.mdi-account-heart-outline:before{content:"\f0be3"}.mdi-account-injury:before{content:"\f1815"}.mdi-account-injury-outline:before{content:"\f1816"}.mdi-account-key:before{content:"\f000b"}.mdi-account-key-outline:before{content:"\f0be4"}.mdi-account-lock:before{content:"\f115e"}.mdi-account-lock-open:before{content:"\f1960"}.mdi-account-lock-open-outline:before{content:"\f1961"}.mdi-account-lock-outline:before{content:"\f115f"}.mdi-account-minus:before{content:"\f000d"}.mdi-account-minus-outline:before{content:"\f0aec"}.mdi-account-multiple:before{content:"\f000e"}.mdi-account-multiple-check:before{content:"\f08c5"}.mdi-account-multiple-check-outline:before{content:"\f11fe"}.mdi-account-multiple-minus:before{content:"\f05d3"}.mdi-account-multiple-minus-outline:before{content:"\f0be5"}.mdi-account-multiple-outline:before{content:"\f000f"}.mdi-account-multiple-plus:before{content:"\f0010"}.mdi-account-multiple-plus-outline:before{content:"\f0800"}.mdi-account-multiple-remove:before{content:"\f120a"}.mdi-account-multiple-remove-outline:before{content:"\f120b"}.mdi-account-music:before{content:"\f0803"}.mdi-account-music-outline:before{content:"\f0ce9"}.mdi-account-network:before{content:"\f0011"}.mdi-account-network-off:before{content:"\f1af1"}.mdi-account-network-off-outline:before{content:"\f1af2"}.mdi-account-network-outline:before{content:"\f0be6"}.mdi-account-off:before{content:"\f0012"}.mdi-account-off-outline:before{content:"\f0be7"}.mdi-account-outline:before{content:"\f0013"}.mdi-account-plus:before{content:"\f0014"}.mdi-account-plus-outline:before{content:"\f0801"}.mdi-account-question:before{content:"\f0b59"}.mdi-account-question-outline:before{content:"\f0b5a"}.mdi-account-reactivate:before{content:"\f152b"}.mdi-account-reactivate-outline:before{content:"\f152c"}.mdi-account-remove:before{content:"\f0015"}.mdi-account-remove-outline:before{content:"\f0aed"}.mdi-account-school:before{content:"\f1a20"}.mdi-account-school-outline:before{content:"\f1a21"}.mdi-account-search:before{content:"\f0016"}.mdi-account-search-outline:before{content:"\f0935"}.mdi-account-settings:before{content:"\f0630"}.mdi-account-settings-outline:before{content:"\f10c9"}.mdi-account-star:before{content:"\f0017"}.mdi-account-star-outline:before{content:"\f0be8"}.mdi-account-supervisor:before{content:"\f0a8b"}.mdi-account-supervisor-circle:before{content:"\f0a8c"}.mdi-account-supervisor-circle-outline:before{content:"\f14ec"}.mdi-account-supervisor-outline:before{content:"\f112d"}.mdi-account-switch:before{content:"\f0019"}.mdi-account-switch-outline:before{content:"\f04cb"}.mdi-account-sync:before{content:"\f191b"}.mdi-account-sync-outline:before{content:"\f191c"}.mdi-account-tag:before{content:"\f1c1b"}.mdi-account-tag-outline:before{content:"\f1c1c"}.mdi-account-tie:before{content:"\f0ce3"}.mdi-account-tie-hat:before{content:"\f1898"}.mdi-account-tie-hat-outline:before{content:"\f1899"}.mdi-account-tie-outline:before{content:"\f10ca"}.mdi-account-tie-voice:before{content:"\f1308"}.mdi-account-tie-voice-off:before{content:"\f130a"}.mdi-account-tie-voice-off-outline:before{content:"\f130b"}.mdi-account-tie-voice-outline:before{content:"\f1309"}.mdi-account-tie-woman:before{content:"\f1a8c"}.mdi-account-voice:before{content:"\f05cb"}.mdi-account-voice-off:before{content:"\f0ed4"}.mdi-account-wrench:before{content:"\f189a"}.mdi-account-wrench-outline:before{content:"\f189b"}.mdi-adjust:before{content:"\f001a"}.mdi-advertisements:before{content:"\f192a"}.mdi-advertisements-off:before{content:"\f192b"}.mdi-air-conditioner:before{content:"\f001b"}.mdi-air-filter:before{content:"\f0d43"}.mdi-air-horn:before{content:"\f0dac"}.mdi-air-humidifier:before{content:"\f1099"}.mdi-air-humidifier-off:before{content:"\f1466"}.mdi-air-purifier:before{content:"\f0d44"}.mdi-air-purifier-off:before{content:"\f1b57"}.mdi-airbag:before{content:"\f0be9"}.mdi-airballoon:before{content:"\f001c"}.mdi-airballoon-outline:before{content:"\f100b"}.mdi-airplane:before{content:"\f001d"}.mdi-airplane-alert:before{content:"\f187a"}.mdi-airplane-check:before{content:"\f187b"}.mdi-airplane-clock:before{content:"\f187c"}.mdi-airplane-cog:before{content:"\f187d"}.mdi-airplane-edit:before{content:"\f187e"}.mdi-airplane-landing:before{content:"\f05d4"}.mdi-airplane-marker:before{content:"\f187f"}.mdi-airplane-minus:before{content:"\f1880"}.mdi-airplane-off:before{content:"\f001e"}.mdi-airplane-plus:before{content:"\f1881"}.mdi-airplane-remove:before{content:"\f1882"}.mdi-airplane-search:before{content:"\f1883"}.mdi-airplane-settings:before{content:"\f1884"}.mdi-airplane-takeoff:before{content:"\f05d5"}.mdi-airport:before{content:"\f084b"}.mdi-alarm:before{content:"\f0020"}.mdi-alarm-bell:before{content:"\f078e"}.mdi-alarm-check:before{content:"\f0021"}.mdi-alarm-light:before{content:"\f078f"}.mdi-alarm-light-off:before{content:"\f171e"}.mdi-alarm-light-off-outline:before{content:"\f171f"}.mdi-alarm-light-outline:before{content:"\f0bea"}.mdi-alarm-multiple:before{content:"\f0022"}.mdi-alarm-note:before{content:"\f0e71"}.mdi-alarm-note-off:before{content:"\f0e72"}.mdi-alarm-off:before{content:"\f0023"}.mdi-alarm-panel:before{content:"\f15c4"}.mdi-alarm-panel-outline:before{content:"\f15c5"}.mdi-alarm-plus:before{content:"\f0024"}.mdi-alarm-snooze:before{content:"\f068e"}.mdi-album:before{content:"\f0025"}.mdi-alert:before{content:"\f0026"}.mdi-alert-box:before{content:"\f0027"}.mdi-alert-box-outline:before{content:"\f0ce4"}.mdi-alert-circle:before{content:"\f0028"}.mdi-alert-circle-check:before{content:"\f11ed"}.mdi-alert-circle-check-outline:before{content:"\f11ee"}.mdi-alert-circle-outline:before{content:"\f05d6"}.mdi-alert-decagram:before{content:"\f06bd"}.mdi-alert-decagram-outline:before{content:"\f0ce5"}.mdi-alert-minus:before{content:"\f14bb"}.mdi-alert-minus-outline:before{content:"\f14be"}.mdi-alert-octagon:before{content:"\f0029"}.mdi-alert-octagon-outline:before{content:"\f0ce6"}.mdi-alert-octagram:before{content:"\f0767"}.mdi-alert-octagram-outline:before{content:"\f0ce7"}.mdi-alert-outline:before{content:"\f002a"}.mdi-alert-plus:before{content:"\f14ba"}.mdi-alert-plus-outline:before{content:"\f14bd"}.mdi-alert-remove:before{content:"\f14bc"}.mdi-alert-remove-outline:before{content:"\f14bf"}.mdi-alert-rhombus:before{content:"\f11ce"}.mdi-alert-rhombus-outline:before{content:"\f11cf"}.mdi-alien:before{content:"\f089a"}.mdi-alien-outline:before{content:"\f10cb"}.mdi-align-horizontal-center:before{content:"\f11c3"}.mdi-align-horizontal-distribute:before{content:"\f1962"}.mdi-align-horizontal-left:before{content:"\f11c2"}.mdi-align-horizontal-right:before{content:"\f11c4"}.mdi-align-vertical-bottom:before{content:"\f11c5"}.mdi-align-vertical-center:before{content:"\f11c6"}.mdi-align-vertical-distribute:before{content:"\f1963"}.mdi-align-vertical-top:before{content:"\f11c7"}.mdi-all-inclusive:before{content:"\f06be"}.mdi-all-inclusive-box:before{content:"\f188d"}.mdi-all-inclusive-box-outline:before{content:"\f188e"}.mdi-allergy:before{content:"\f1258"}.mdi-alpha:before{content:"\f002b"}.mdi-alpha-a:before{content:"\f0aee"}.mdi-alpha-a-box:before{content:"\f0b08"}.mdi-alpha-a-box-outline:before{content:"\f0beb"}.mdi-alpha-a-circle:before{content:"\f0bec"}.mdi-alpha-a-circle-outline:before{content:"\f0bed"}.mdi-alpha-b:before{content:"\f0aef"}.mdi-alpha-b-box:before{content:"\f0b09"}.mdi-alpha-b-box-outline:before{content:"\f0bee"}.mdi-alpha-b-circle:before{content:"\f0bef"}.mdi-alpha-b-circle-outline:before{content:"\f0bf0"}.mdi-alpha-c:before{content:"\f0af0"}.mdi-alpha-c-box:before{content:"\f0b0a"}.mdi-alpha-c-box-outline:before{content:"\f0bf1"}.mdi-alpha-c-circle:before{content:"\f0bf2"}.mdi-alpha-c-circle-outline:before{content:"\f0bf3"}.mdi-alpha-d:before{content:"\f0af1"}.mdi-alpha-d-box:before{content:"\f0b0b"}.mdi-alpha-d-box-outline:before{content:"\f0bf4"}.mdi-alpha-d-circle:before{content:"\f0bf5"}.mdi-alpha-d-circle-outline:before{content:"\f0bf6"}.mdi-alpha-e:before{content:"\f0af2"}.mdi-alpha-e-box:before{content:"\f0b0c"}.mdi-alpha-e-box-outline:before{content:"\f0bf7"}.mdi-alpha-e-circle:before{content:"\f0bf8"}.mdi-alpha-e-circle-outline:before{content:"\f0bf9"}.mdi-alpha-f:before{content:"\f0af3"}.mdi-alpha-f-box:before{content:"\f0b0d"}.mdi-alpha-f-box-outline:before{content:"\f0bfa"}.mdi-alpha-f-circle:before{content:"\f0bfb"}.mdi-alpha-f-circle-outline:before{content:"\f0bfc"}.mdi-alpha-g:before{content:"\f0af4"}.mdi-alpha-g-box:before{content:"\f0b0e"}.mdi-alpha-g-box-outline:before{content:"\f0bfd"}.mdi-alpha-g-circle:before{content:"\f0bfe"}.mdi-alpha-g-circle-outline:before{content:"\f0bff"}.mdi-alpha-h:before{content:"\f0af5"}.mdi-alpha-h-box:before{content:"\f0b0f"}.mdi-alpha-h-box-outline:before{content:"\f0c00"}.mdi-alpha-h-circle:before{content:"\f0c01"}.mdi-alpha-h-circle-outline:before{content:"\f0c02"}.mdi-alpha-i:before{content:"\f0af6"}.mdi-alpha-i-box:before{content:"\f0b10"}.mdi-alpha-i-box-outline:before{content:"\f0c03"}.mdi-alpha-i-circle:before{content:"\f0c04"}.mdi-alpha-i-circle-outline:before{content:"\f0c05"}.mdi-alpha-j:before{content:"\f0af7"}.mdi-alpha-j-box:before{content:"\f0b11"}.mdi-alpha-j-box-outline:before{content:"\f0c06"}.mdi-alpha-j-circle:before{content:"\f0c07"}.mdi-alpha-j-circle-outline:before{content:"\f0c08"}.mdi-alpha-k:before{content:"\f0af8"}.mdi-alpha-k-box:before{content:"\f0b12"}.mdi-alpha-k-box-outline:before{content:"\f0c09"}.mdi-alpha-k-circle:before{content:"\f0c0a"}.mdi-alpha-k-circle-outline:before{content:"\f0c0b"}.mdi-alpha-l:before{content:"\f0af9"}.mdi-alpha-l-box:before{content:"\f0b13"}.mdi-alpha-l-box-outline:before{content:"\f0c0c"}.mdi-alpha-l-circle:before{content:"\f0c0d"}.mdi-alpha-l-circle-outline:before{content:"\f0c0e"}.mdi-alpha-m:before{content:"\f0afa"}.mdi-alpha-m-box:before{content:"\f0b14"}.mdi-alpha-m-box-outline:before{content:"\f0c0f"}.mdi-alpha-m-circle:before{content:"\f0c10"}.mdi-alpha-m-circle-outline:before{content:"\f0c11"}.mdi-alpha-n:before{content:"\f0afb"}.mdi-alpha-n-box:before{content:"\f0b15"}.mdi-alpha-n-box-outline:before{content:"\f0c12"}.mdi-alpha-n-circle:before{content:"\f0c13"}.mdi-alpha-n-circle-outline:before{content:"\f0c14"}.mdi-alpha-o:before{content:"\f0afc"}.mdi-alpha-o-box:before{content:"\f0b16"}.mdi-alpha-o-box-outline:before{content:"\f0c15"}.mdi-alpha-o-circle:before{content:"\f0c16"}.mdi-alpha-o-circle-outline:before{content:"\f0c17"}.mdi-alpha-p:before{content:"\f0afd"}.mdi-alpha-p-box:before{content:"\f0b17"}.mdi-alpha-p-box-outline:before{content:"\f0c18"}.mdi-alpha-p-circle:before{content:"\f0c19"}.mdi-alpha-p-circle-outline:before{content:"\f0c1a"}.mdi-alpha-q:before{content:"\f0afe"}.mdi-alpha-q-box:before{content:"\f0b18"}.mdi-alpha-q-box-outline:before{content:"\f0c1b"}.mdi-alpha-q-circle:before{content:"\f0c1c"}.mdi-alpha-q-circle-outline:before{content:"\f0c1d"}.mdi-alpha-r:before{content:"\f0aff"}.mdi-alpha-r-box:before{content:"\f0b19"}.mdi-alpha-r-box-outline:before{content:"\f0c1e"}.mdi-alpha-r-circle:before{content:"\f0c1f"}.mdi-alpha-r-circle-outline:before{content:"\f0c20"}.mdi-alpha-s:before{content:"\f0b00"}.mdi-alpha-s-box:before{content:"\f0b1a"}.mdi-alpha-s-box-outline:before{content:"\f0c21"}.mdi-alpha-s-circle:before{content:"\f0c22"}.mdi-alpha-s-circle-outline:before{content:"\f0c23"}.mdi-alpha-t:before{content:"\f0b01"}.mdi-alpha-t-box:before{content:"\f0b1b"}.mdi-alpha-t-box-outline:before{content:"\f0c24"}.mdi-alpha-t-circle:before{content:"\f0c25"}.mdi-alpha-t-circle-outline:before{content:"\f0c26"}.mdi-alpha-u:before{content:"\f0b02"}.mdi-alpha-u-box:before{content:"\f0b1c"}.mdi-alpha-u-box-outline:before{content:"\f0c27"}.mdi-alpha-u-circle:before{content:"\f0c28"}.mdi-alpha-u-circle-outline:before{content:"\f0c29"}.mdi-alpha-v:before{content:"\f0b03"}.mdi-alpha-v-box:before{content:"\f0b1d"}.mdi-alpha-v-box-outline:before{content:"\f0c2a"}.mdi-alpha-v-circle:before{content:"\f0c2b"}.mdi-alpha-v-circle-outline:before{content:"\f0c2c"}.mdi-alpha-w:before{content:"\f0b04"}.mdi-alpha-w-box:before{content:"\f0b1e"}.mdi-alpha-w-box-outline:before{content:"\f0c2d"}.mdi-alpha-w-circle:before{content:"\f0c2e"}.mdi-alpha-w-circle-outline:before{content:"\f0c2f"}.mdi-alpha-x:before{content:"\f0b05"}.mdi-alpha-x-box:before{content:"\f0b1f"}.mdi-alpha-x-box-outline:before{content:"\f0c30"}.mdi-alpha-x-circle:before{content:"\f0c31"}.mdi-alpha-x-circle-outline:before{content:"\f0c32"}.mdi-alpha-y:before{content:"\f0b06"}.mdi-alpha-y-box:before{content:"\f0b20"}.mdi-alpha-y-box-outline:before{content:"\f0c33"}.mdi-alpha-y-circle:before{content:"\f0c34"}.mdi-alpha-y-circle-outline:before{content:"\f0c35"}.mdi-alpha-z:before{content:"\f0b07"}.mdi-alpha-z-box:before{content:"\f0b21"}.mdi-alpha-z-box-outline:before{content:"\f0c36"}.mdi-alpha-z-circle:before{content:"\f0c37"}.mdi-alpha-z-circle-outline:before{content:"\f0c38"}.mdi-alphabet-aurebesh:before{content:"\f132c"}.mdi-alphabet-cyrillic:before{content:"\f132d"}.mdi-alphabet-greek:before{content:"\f132e"}.mdi-alphabet-latin:before{content:"\f132f"}.mdi-alphabet-piqad:before{content:"\f1330"}.mdi-alphabet-tengwar:before{content:"\f1337"}.mdi-alphabetical:before{content:"\f002c"}.mdi-alphabetical-off:before{content:"\f100c"}.mdi-alphabetical-variant:before{content:"\f100d"}.mdi-alphabetical-variant-off:before{content:"\f100e"}.mdi-altimeter:before{content:"\f05d7"}.mdi-ambulance:before{content:"\f002f"}.mdi-ammunition:before{content:"\f0ce8"}.mdi-ampersand:before{content:"\f0a8d"}.mdi-amplifier:before{content:"\f0030"}.mdi-amplifier-off:before{content:"\f11b5"}.mdi-anchor:before{content:"\f0031"}.mdi-android:before{content:"\f0032"}.mdi-android-studio:before{content:"\f0034"}.mdi-angle-acute:before{content:"\f0937"}.mdi-angle-obtuse:before{content:"\f0938"}.mdi-angle-right:before{content:"\f0939"}.mdi-angular:before{content:"\f06b2"}.mdi-angularjs:before{content:"\f06bf"}.mdi-animation:before{content:"\f05d8"}.mdi-animation-outline:before{content:"\f0a8f"}.mdi-animation-play:before{content:"\f093a"}.mdi-animation-play-outline:before{content:"\f0a90"}.mdi-ansible:before{content:"\f109a"}.mdi-antenna:before{content:"\f1119"}.mdi-anvil:before{content:"\f089b"}.mdi-apache-kafka:before{content:"\f100f"}.mdi-api:before{content:"\f109b"}.mdi-api-off:before{content:"\f1257"}.mdi-apple:before{content:"\f0035"}.mdi-apple-finder:before{content:"\f0036"}.mdi-apple-icloud:before{content:"\f0038"}.mdi-apple-ios:before{content:"\f0037"}.mdi-apple-keyboard-caps:before{content:"\f0632"}.mdi-apple-keyboard-command:before{content:"\f0633"}.mdi-apple-keyboard-control:before{content:"\f0634"}.mdi-apple-keyboard-option:before{content:"\f0635"}.mdi-apple-keyboard-shift:before{content:"\f0636"}.mdi-apple-safari:before{content:"\f0039"}.mdi-application:before{content:"\f08c6"}.mdi-application-array:before{content:"\f10f5"}.mdi-application-array-outline:before{content:"\f10f6"}.mdi-application-braces:before{content:"\f10f7"}.mdi-application-braces-outline:before{content:"\f10f8"}.mdi-application-brackets:before{content:"\f0c8b"}.mdi-application-brackets-outline:before{content:"\f0c8c"}.mdi-application-cog:before{content:"\f0675"}.mdi-application-cog-outline:before{content:"\f1577"}.mdi-application-edit:before{content:"\f00ae"}.mdi-application-edit-outline:before{content:"\f0619"}.mdi-application-export:before{content:"\f0dad"}.mdi-application-import:before{content:"\f0dae"}.mdi-application-outline:before{content:"\f0614"}.mdi-application-parentheses:before{content:"\f10f9"}.mdi-application-parentheses-outline:before{content:"\f10fa"}.mdi-application-settings:before{content:"\f0b60"}.mdi-application-settings-outline:before{content:"\f1555"}.mdi-application-variable:before{content:"\f10fb"}.mdi-application-variable-outline:before{content:"\f10fc"}.mdi-approximately-equal:before{content:"\f0f9e"}.mdi-approximately-equal-box:before{content:"\f0f9f"}.mdi-apps:before{content:"\f003b"}.mdi-apps-box:before{content:"\f0d46"}.mdi-arch:before{content:"\f08c7"}.mdi-archive:before{content:"\f003c"}.mdi-archive-alert:before{content:"\f14fd"}.mdi-archive-alert-outline:before{content:"\f14fe"}.mdi-archive-arrow-down:before{content:"\f1259"}.mdi-archive-arrow-down-outline:before{content:"\f125a"}.mdi-archive-arrow-up:before{content:"\f125b"}.mdi-archive-arrow-up-outline:before{content:"\f125c"}.mdi-archive-cancel:before{content:"\f174b"}.mdi-archive-cancel-outline:before{content:"\f174c"}.mdi-archive-check:before{content:"\f174d"}.mdi-archive-check-outline:before{content:"\f174e"}.mdi-archive-clock:before{content:"\f174f"}.mdi-archive-clock-outline:before{content:"\f1750"}.mdi-archive-cog:before{content:"\f1751"}.mdi-archive-cog-outline:before{content:"\f1752"}.mdi-archive-edit:before{content:"\f1753"}.mdi-archive-edit-outline:before{content:"\f1754"}.mdi-archive-eye:before{content:"\f1755"}.mdi-archive-eye-outline:before{content:"\f1756"}.mdi-archive-lock:before{content:"\f1757"}.mdi-archive-lock-open:before{content:"\f1758"}.mdi-archive-lock-open-outline:before{content:"\f1759"}.mdi-archive-lock-outline:before{content:"\f175a"}.mdi-archive-marker:before{content:"\f175b"}.mdi-archive-marker-outline:before{content:"\f175c"}.mdi-archive-minus:before{content:"\f175d"}.mdi-archive-minus-outline:before{content:"\f175e"}.mdi-archive-music:before{content:"\f175f"}.mdi-archive-music-outline:before{content:"\f1760"}.mdi-archive-off:before{content:"\f1761"}.mdi-archive-off-outline:before{content:"\f1762"}.mdi-archive-outline:before{content:"\f120e"}.mdi-archive-plus:before{content:"\f1763"}.mdi-archive-plus-outline:before{content:"\f1764"}.mdi-archive-refresh:before{content:"\f1765"}.mdi-archive-refresh-outline:before{content:"\f1766"}.mdi-archive-remove:before{content:"\f1767"}.mdi-archive-remove-outline:before{content:"\f1768"}.mdi-archive-search:before{content:"\f1769"}.mdi-archive-search-outline:before{content:"\f176a"}.mdi-archive-settings:before{content:"\f176b"}.mdi-archive-settings-outline:before{content:"\f176c"}.mdi-archive-star:before{content:"\f176d"}.mdi-archive-star-outline:before{content:"\f176e"}.mdi-archive-sync:before{content:"\f176f"}.mdi-archive-sync-outline:before{content:"\f1770"}.mdi-arm-flex:before{content:"\f0fd7"}.mdi-arm-flex-outline:before{content:"\f0fd6"}.mdi-arrange-bring-forward:before{content:"\f003d"}.mdi-arrange-bring-to-front:before{content:"\f003e"}.mdi-arrange-send-backward:before{content:"\f003f"}.mdi-arrange-send-to-back:before{content:"\f0040"}.mdi-arrow-all:before{content:"\f0041"}.mdi-arrow-bottom-left:before{content:"\f0042"}.mdi-arrow-bottom-left-bold-box:before{content:"\f1964"}.mdi-arrow-bottom-left-bold-box-outline:before{content:"\f1965"}.mdi-arrow-bottom-left-bold-outline:before{content:"\f09b7"}.mdi-arrow-bottom-left-thick:before{content:"\f09b8"}.mdi-arrow-bottom-left-thin:before{content:"\f19b6"}.mdi-arrow-bottom-left-thin-circle-outline:before{content:"\f1596"}.mdi-arrow-bottom-right:before{content:"\f0043"}.mdi-arrow-bottom-right-bold-box:before{content:"\f1966"}.mdi-arrow-bottom-right-bold-box-outline:before{content:"\f1967"}.mdi-arrow-bottom-right-bold-outline:before{content:"\f09b9"}.mdi-arrow-bottom-right-thick:before{content:"\f09ba"}.mdi-arrow-bottom-right-thin:before{content:"\f19b7"}.mdi-arrow-bottom-right-thin-circle-outline:before{content:"\f1595"}.mdi-arrow-collapse:before{content:"\f0615"}.mdi-arrow-collapse-all:before{content:"\f0044"}.mdi-arrow-collapse-down:before{content:"\f0792"}.mdi-arrow-collapse-horizontal:before{content:"\f084c"}.mdi-arrow-collapse-left:before{content:"\f0793"}.mdi-arrow-collapse-right:before{content:"\f0794"}.mdi-arrow-collapse-up:before{content:"\f0795"}.mdi-arrow-collapse-vertical:before{content:"\f084d"}.mdi-arrow-decision:before{content:"\f09bb"}.mdi-arrow-decision-auto:before{content:"\f09bc"}.mdi-arrow-decision-auto-outline:before{content:"\f09bd"}.mdi-arrow-decision-outline:before{content:"\f09be"}.mdi-arrow-down:before{content:"\f0045"}.mdi-arrow-down-bold:before{content:"\f072e"}.mdi-arrow-down-bold-box:before{content:"\f072f"}.mdi-arrow-down-bold-box-outline:before{content:"\f0730"}.mdi-arrow-down-bold-circle:before{content:"\f0047"}.mdi-arrow-down-bold-circle-outline:before{content:"\f0048"}.mdi-arrow-down-bold-hexagon-outline:before{content:"\f0049"}.mdi-arrow-down-bold-outline:before{content:"\f09bf"}.mdi-arrow-down-box:before{content:"\f06c0"}.mdi-arrow-down-circle:before{content:"\f0cdb"}.mdi-arrow-down-circle-outline:before{content:"\f0cdc"}.mdi-arrow-down-drop-circle:before{content:"\f004a"}.mdi-arrow-down-drop-circle-outline:before{content:"\f004b"}.mdi-arrow-down-left:before{content:"\f17a1"}.mdi-arrow-down-left-bold:before{content:"\f17a2"}.mdi-arrow-down-right:before{content:"\f17a3"}.mdi-arrow-down-right-bold:before{content:"\f17a4"}.mdi-arrow-down-thick:before{content:"\f0046"}.mdi-arrow-down-thin:before{content:"\f19b3"}.mdi-arrow-down-thin-circle-outline:before{content:"\f1599"}.mdi-arrow-expand:before{content:"\f0616"}.mdi-arrow-expand-all:before{content:"\f004c"}.mdi-arrow-expand-down:before{content:"\f0796"}.mdi-arrow-expand-horizontal:before{content:"\f084e"}.mdi-arrow-expand-left:before{content:"\f0797"}.mdi-arrow-expand-right:before{content:"\f0798"}.mdi-arrow-expand-up:before{content:"\f0799"}.mdi-arrow-expand-vertical:before{content:"\f084f"}.mdi-arrow-horizontal-lock:before{content:"\f115b"}.mdi-arrow-left:before{content:"\f004d"}.mdi-arrow-left-bold:before{content:"\f0731"}.mdi-arrow-left-bold-box:before{content:"\f0732"}.mdi-arrow-left-bold-box-outline:before{content:"\f0733"}.mdi-arrow-left-bold-circle:before{content:"\f004f"}.mdi-arrow-left-bold-circle-outline:before{content:"\f0050"}.mdi-arrow-left-bold-hexagon-outline:before{content:"\f0051"}.mdi-arrow-left-bold-outline:before{content:"\f09c0"}.mdi-arrow-left-bottom:before{content:"\f17a5"}.mdi-arrow-left-bottom-bold:before{content:"\f17a6"}.mdi-arrow-left-box:before{content:"\f06c1"}.mdi-arrow-left-circle:before{content:"\f0cdd"}.mdi-arrow-left-circle-outline:before{content:"\f0cde"}.mdi-arrow-left-drop-circle:before{content:"\f0052"}.mdi-arrow-left-drop-circle-outline:before{content:"\f0053"}.mdi-arrow-left-right:before{content:"\f0e73"}.mdi-arrow-left-right-bold:before{content:"\f0e74"}.mdi-arrow-left-right-bold-outline:before{content:"\f09c1"}.mdi-arrow-left-thick:before{content:"\f004e"}.mdi-arrow-left-thin:before{content:"\f19b1"}.mdi-arrow-left-thin-circle-outline:before{content:"\f159a"}.mdi-arrow-left-top:before{content:"\f17a7"}.mdi-arrow-left-top-bold:before{content:"\f17a8"}.mdi-arrow-oscillating:before{content:"\f1c91"}.mdi-arrow-oscillating-off:before{content:"\f1c92"}.mdi-arrow-projectile:before{content:"\f1840"}.mdi-arrow-projectile-multiple:before{content:"\f183f"}.mdi-arrow-right:before{content:"\f0054"}.mdi-arrow-right-bold:before{content:"\f0734"}.mdi-arrow-right-bold-box:before{content:"\f0735"}.mdi-arrow-right-bold-box-outline:before{content:"\f0736"}.mdi-arrow-right-bold-circle:before{content:"\f0056"}.mdi-arrow-right-bold-circle-outline:before{content:"\f0057"}.mdi-arrow-right-bold-hexagon-outline:before{content:"\f0058"}.mdi-arrow-right-bold-outline:before{content:"\f09c2"}.mdi-arrow-right-bottom:before{content:"\f17a9"}.mdi-arrow-right-bottom-bold:before{content:"\f17aa"}.mdi-arrow-right-box:before{content:"\f06c2"}.mdi-arrow-right-circle:before{content:"\f0cdf"}.mdi-arrow-right-circle-outline:before{content:"\f0ce0"}.mdi-arrow-right-drop-circle:before{content:"\f0059"}.mdi-arrow-right-drop-circle-outline:before{content:"\f005a"}.mdi-arrow-right-thick:before{content:"\f0055"}.mdi-arrow-right-thin:before{content:"\f19b0"}.mdi-arrow-right-thin-circle-outline:before{content:"\f1598"}.mdi-arrow-right-top:before{content:"\f17ab"}.mdi-arrow-right-top-bold:before{content:"\f17ac"}.mdi-arrow-split-horizontal:before{content:"\f093b"}.mdi-arrow-split-vertical:before{content:"\f093c"}.mdi-arrow-top-left:before{content:"\f005b"}.mdi-arrow-top-left-bold-box:before{content:"\f1968"}.mdi-arrow-top-left-bold-box-outline:before{content:"\f1969"}.mdi-arrow-top-left-bold-outline:before{content:"\f09c3"}.mdi-arrow-top-left-bottom-right:before{content:"\f0e75"}.mdi-arrow-top-left-bottom-right-bold:before{content:"\f0e76"}.mdi-arrow-top-left-thick:before{content:"\f09c4"}.mdi-arrow-top-left-thin:before{content:"\f19b5"}.mdi-arrow-top-left-thin-circle-outline:before{content:"\f1593"}.mdi-arrow-top-right:before{content:"\f005c"}.mdi-arrow-top-right-bold-box:before{content:"\f196a"}.mdi-arrow-top-right-bold-box-outline:before{content:"\f196b"}.mdi-arrow-top-right-bold-outline:before{content:"\f09c5"}.mdi-arrow-top-right-bottom-left:before{content:"\f0e77"}.mdi-arrow-top-right-bottom-left-bold:before{content:"\f0e78"}.mdi-arrow-top-right-thick:before{content:"\f09c6"}.mdi-arrow-top-right-thin:before{content:"\f19b4"}.mdi-arrow-top-right-thin-circle-outline:before{content:"\f1594"}.mdi-arrow-u-down-left:before{content:"\f17ad"}.mdi-arrow-u-down-left-bold:before{content:"\f17ae"}.mdi-arrow-u-down-right:before{content:"\f17af"}.mdi-arrow-u-down-right-bold:before{content:"\f17b0"}.mdi-arrow-u-left-bottom:before{content:"\f17b1"}.mdi-arrow-u-left-bottom-bold:before{content:"\f17b2"}.mdi-arrow-u-left-top:before{content:"\f17b3"}.mdi-arrow-u-left-top-bold:before{content:"\f17b4"}.mdi-arrow-u-right-bottom:before{content:"\f17b5"}.mdi-arrow-u-right-bottom-bold:before{content:"\f17b6"}.mdi-arrow-u-right-top:before{content:"\f17b7"}.mdi-arrow-u-right-top-bold:before{content:"\f17b8"}.mdi-arrow-u-up-left:before{content:"\f17b9"}.mdi-arrow-u-up-left-bold:before{content:"\f17ba"}.mdi-arrow-u-up-right:before{content:"\f17bb"}.mdi-arrow-u-up-right-bold:before{content:"\f17bc"}.mdi-arrow-up:before{content:"\f005d"}.mdi-arrow-up-bold:before{content:"\f0737"}.mdi-arrow-up-bold-box:before{content:"\f0738"}.mdi-arrow-up-bold-box-outline:before{content:"\f0739"}.mdi-arrow-up-bold-circle:before{content:"\f005f"}.mdi-arrow-up-bold-circle-outline:before{content:"\f0060"}.mdi-arrow-up-bold-hexagon-outline:before{content:"\f0061"}.mdi-arrow-up-bold-outline:before{content:"\f09c7"}.mdi-arrow-up-box:before{content:"\f06c3"}.mdi-arrow-up-circle:before{content:"\f0ce1"}.mdi-arrow-up-circle-outline:before{content:"\f0ce2"}.mdi-arrow-up-down:before{content:"\f0e79"}.mdi-arrow-up-down-bold:before{content:"\f0e7a"}.mdi-arrow-up-down-bold-outline:before{content:"\f09c8"}.mdi-arrow-up-drop-circle:before{content:"\f0062"}.mdi-arrow-up-drop-circle-outline:before{content:"\f0063"}.mdi-arrow-up-left:before{content:"\f17bd"}.mdi-arrow-up-left-bold:before{content:"\f17be"}.mdi-arrow-up-right:before{content:"\f17bf"}.mdi-arrow-up-right-bold:before{content:"\f17c0"}.mdi-arrow-up-thick:before{content:"\f005e"}.mdi-arrow-up-thin:before{content:"\f19b2"}.mdi-arrow-up-thin-circle-outline:before{content:"\f1597"}.mdi-arrow-vertical-lock:before{content:"\f115c"}.mdi-artboard:before{content:"\f1b9a"}.mdi-artstation:before{content:"\f0b5b"}.mdi-aspect-ratio:before{content:"\f0a24"}.mdi-assistant:before{content:"\f0064"}.mdi-asterisk:before{content:"\f06c4"}.mdi-asterisk-circle-outline:before{content:"\f1a27"}.mdi-at:before{content:"\f0065"}.mdi-atlassian:before{content:"\f0804"}.mdi-atm:before{content:"\f0d47"}.mdi-atom:before{content:"\f0768"}.mdi-atom-variant:before{content:"\f0e7b"}.mdi-attachment:before{content:"\f0066"}.mdi-attachment-check:before{content:"\f1ac1"}.mdi-attachment-lock:before{content:"\f19c4"}.mdi-attachment-minus:before{content:"\f1ac2"}.mdi-attachment-off:before{content:"\f1ac3"}.mdi-attachment-plus:before{content:"\f1ac4"}.mdi-attachment-remove:before{content:"\f1ac5"}.mdi-atv:before{content:"\f1b70"}.mdi-audio-input-rca:before{content:"\f186b"}.mdi-audio-input-stereo-minijack:before{content:"\f186c"}.mdi-audio-input-xlr:before{content:"\f186d"}.mdi-audio-video:before{content:"\f093d"}.mdi-audio-video-off:before{content:"\f11b6"}.mdi-augmented-reality:before{content:"\f0850"}.mdi-aurora:before{content:"\f1bb9"}.mdi-auto-download:before{content:"\f137e"}.mdi-auto-fix:before{content:"\f0068"}.mdi-auto-mode:before{content:"\f1c20"}.mdi-auto-upload:before{content:"\f0069"}.mdi-autorenew:before{content:"\f006a"}.mdi-autorenew-off:before{content:"\f19e7"}.mdi-av-timer:before{content:"\f006b"}.mdi-awning:before{content:"\f1b87"}.mdi-awning-outline:before{content:"\f1b88"}.mdi-aws:before{content:"\f0e0f"}.mdi-axe:before{content:"\f08c8"}.mdi-axe-battle:before{content:"\f1842"}.mdi-axis:before{content:"\f0d48"}.mdi-axis-arrow:before{content:"\f0d49"}.mdi-axis-arrow-info:before{content:"\f140e"}.mdi-axis-arrow-lock:before{content:"\f0d4a"}.mdi-axis-lock:before{content:"\f0d4b"}.mdi-axis-x-arrow:before{content:"\f0d4c"}.mdi-axis-x-arrow-lock:before{content:"\f0d4d"}.mdi-axis-x-rotate-clockwise:before{content:"\f0d4e"}.mdi-axis-x-rotate-counterclockwise:before{content:"\f0d4f"}.mdi-axis-x-y-arrow-lock:before{content:"\f0d50"}.mdi-axis-y-arrow:before{content:"\f0d51"}.mdi-axis-y-arrow-lock:before{content:"\f0d52"}.mdi-axis-y-rotate-clockwise:before{content:"\f0d53"}.mdi-axis-y-rotate-counterclockwise:before{content:"\f0d54"}.mdi-axis-z-arrow:before{content:"\f0d55"}.mdi-axis-z-arrow-lock:before{content:"\f0d56"}.mdi-axis-z-rotate-clockwise:before{content:"\f0d57"}.mdi-axis-z-rotate-counterclockwise:before{content:"\f0d58"}.mdi-babel:before{content:"\f0a25"}.mdi-baby:before{content:"\f006c"}.mdi-baby-bottle:before{content:"\f0f39"}.mdi-baby-bottle-outline:before{content:"\f0f3a"}.mdi-baby-buggy:before{content:"\f13e0"}.mdi-baby-buggy-off:before{content:"\f1af3"}.mdi-baby-carriage:before{content:"\f068f"}.mdi-baby-carriage-off:before{content:"\f0fa0"}.mdi-baby-face:before{content:"\f0e7c"}.mdi-baby-face-outline:before{content:"\f0e7d"}.mdi-backburger:before{content:"\f006d"}.mdi-backspace:before{content:"\f006e"}.mdi-backspace-outline:before{content:"\f0b5c"}.mdi-backspace-reverse:before{content:"\f0e7e"}.mdi-backspace-reverse-outline:before{content:"\f0e7f"}.mdi-backup-restore:before{content:"\f006f"}.mdi-bacteria:before{content:"\f0ed5"}.mdi-bacteria-outline:before{content:"\f0ed6"}.mdi-badge-account:before{content:"\f0da7"}.mdi-badge-account-alert:before{content:"\f0da8"}.mdi-badge-account-alert-outline:before{content:"\f0da9"}.mdi-badge-account-horizontal:before{content:"\f0e0d"}.mdi-badge-account-horizontal-outline:before{content:"\f0e0e"}.mdi-badge-account-outline:before{content:"\f0daa"}.mdi-badminton:before{content:"\f0851"}.mdi-bag-carry-on:before{content:"\f0f3b"}.mdi-bag-carry-on-check:before{content:"\f0d65"}.mdi-bag-carry-on-off:before{content:"\f0f3c"}.mdi-bag-checked:before{content:"\f0f3d"}.mdi-bag-personal:before{content:"\f0e10"}.mdi-bag-personal-off:before{content:"\f0e11"}.mdi-bag-personal-off-outline:before{content:"\f0e12"}.mdi-bag-personal-outline:before{content:"\f0e13"}.mdi-bag-personal-plus:before{content:"\f1ca4"}.mdi-bag-personal-plus-outline:before{content:"\f1ca5"}.mdi-bag-personal-tag:before{content:"\f1b0c"}.mdi-bag-personal-tag-outline:before{content:"\f1b0d"}.mdi-bag-suitcase:before{content:"\f158b"}.mdi-bag-suitcase-off:before{content:"\f158d"}.mdi-bag-suitcase-off-outline:before{content:"\f158e"}.mdi-bag-suitcase-outline:before{content:"\f158c"}.mdi-baguette:before{content:"\f0f3e"}.mdi-balcony:before{content:"\f1817"}.mdi-balloon:before{content:"\f0a26"}.mdi-ballot:before{content:"\f09c9"}.mdi-ballot-outline:before{content:"\f09ca"}.mdi-ballot-recount:before{content:"\f0c39"}.mdi-ballot-recount-outline:before{content:"\f0c3a"}.mdi-bandage:before{content:"\f0daf"}.mdi-bank:before{content:"\f0070"}.mdi-bank-check:before{content:"\f1655"}.mdi-bank-circle:before{content:"\f1c03"}.mdi-bank-circle-outline:before{content:"\f1c04"}.mdi-bank-minus:before{content:"\f0db0"}.mdi-bank-off:before{content:"\f1656"}.mdi-bank-off-outline:before{content:"\f1657"}.mdi-bank-outline:before{content:"\f0e80"}.mdi-bank-plus:before{content:"\f0db1"}.mdi-bank-remove:before{content:"\f0db2"}.mdi-bank-transfer:before{content:"\f0a27"}.mdi-bank-transfer-in:before{content:"\f0a28"}.mdi-bank-transfer-out:before{content:"\f0a29"}.mdi-barcode:before{content:"\f0071"}.mdi-barcode-off:before{content:"\f1236"}.mdi-barcode-scan:before{content:"\f0072"}.mdi-barley:before{content:"\f0073"}.mdi-barley-off:before{content:"\f0b5d"}.mdi-barn:before{content:"\f0b5e"}.mdi-barrel:before{content:"\f0074"}.mdi-barrel-outline:before{content:"\f1a28"}.mdi-baseball:before{content:"\f0852"}.mdi-baseball-bat:before{content:"\f0853"}.mdi-baseball-diamond:before{content:"\f15ec"}.mdi-baseball-diamond-outline:before{content:"\f15ed"}.mdi-baseball-outline:before{content:"\f1c5a"}.mdi-bash:before{content:"\f1183"}.mdi-basket:before{content:"\f0076"}.mdi-basket-check:before{content:"\f18e5"}.mdi-basket-check-outline:before{content:"\f18e6"}.mdi-basket-fill:before{content:"\f0077"}.mdi-basket-minus:before{content:"\f1523"}.mdi-basket-minus-outline:before{content:"\f1524"}.mdi-basket-off:before{content:"\f1525"}.mdi-basket-off-outline:before{content:"\f1526"}.mdi-basket-outline:before{content:"\f1181"}.mdi-basket-plus:before{content:"\f1527"}.mdi-basket-plus-outline:before{content:"\f1528"}.mdi-basket-remove:before{content:"\f1529"}.mdi-basket-remove-outline:before{content:"\f152a"}.mdi-basket-unfill:before{content:"\f0078"}.mdi-basketball:before{content:"\f0806"}.mdi-basketball-hoop:before{content:"\f0c3b"}.mdi-basketball-hoop-outline:before{content:"\f0c3c"}.mdi-bat:before{content:"\f0b5f"}.mdi-bathtub:before{content:"\f1818"}.mdi-bathtub-outline:before{content:"\f1819"}.mdi-battery:before{content:"\f0079"}.mdi-battery-10:before{content:"\f007a"}.mdi-battery-10-bluetooth:before{content:"\f093e"}.mdi-battery-20:before{content:"\f007b"}.mdi-battery-20-bluetooth:before{content:"\f093f"}.mdi-battery-30:before{content:"\f007c"}.mdi-battery-30-bluetooth:before{content:"\f0940"}.mdi-battery-40:before{content:"\f007d"}.mdi-battery-40-bluetooth:before{content:"\f0941"}.mdi-battery-50:before{content:"\f007e"}.mdi-battery-50-bluetooth:before{content:"\f0942"}.mdi-battery-60:before{content:"\f007f"}.mdi-battery-60-bluetooth:before{content:"\f0943"}.mdi-battery-70:before{content:"\f0080"}.mdi-battery-70-bluetooth:before{content:"\f0944"}.mdi-battery-80:before{content:"\f0081"}.mdi-battery-80-bluetooth:before{content:"\f0945"}.mdi-battery-90:before{content:"\f0082"}.mdi-battery-90-bluetooth:before{content:"\f0946"}.mdi-battery-alert:before{content:"\f0083"}.mdi-battery-alert-bluetooth:before{content:"\f0947"}.mdi-battery-alert-variant:before{content:"\f10cc"}.mdi-battery-alert-variant-outline:before{content:"\f10cd"}.mdi-battery-arrow-down:before{content:"\f17de"}.mdi-battery-arrow-down-outline:before{content:"\f17df"}.mdi-battery-arrow-up:before{content:"\f17e0"}.mdi-battery-arrow-up-outline:before{content:"\f17e1"}.mdi-battery-bluetooth:before{content:"\f0948"}.mdi-battery-bluetooth-variant:before{content:"\f0949"}.mdi-battery-charging:before{content:"\f0084"}.mdi-battery-charging-10:before{content:"\f089c"}.mdi-battery-charging-100:before{content:"\f0085"}.mdi-battery-charging-20:before{content:"\f0086"}.mdi-battery-charging-30:before{content:"\f0087"}.mdi-battery-charging-40:before{content:"\f0088"}.mdi-battery-charging-50:before{content:"\f089d"}.mdi-battery-charging-60:before{content:"\f0089"}.mdi-battery-charging-70:before{content:"\f089e"}.mdi-battery-charging-80:before{content:"\f008a"}.mdi-battery-charging-90:before{content:"\f008b"}.mdi-battery-charging-high:before{content:"\f12a6"}.mdi-battery-charging-low:before{content:"\f12a4"}.mdi-battery-charging-medium:before{content:"\f12a5"}.mdi-battery-charging-outline:before{content:"\f089f"}.mdi-battery-charging-wireless:before{content:"\f0807"}.mdi-battery-charging-wireless-10:before{content:"\f0808"}.mdi-battery-charging-wireless-20:before{content:"\f0809"}.mdi-battery-charging-wireless-30:before{content:"\f080a"}.mdi-battery-charging-wireless-40:before{content:"\f080b"}.mdi-battery-charging-wireless-50:before{content:"\f080c"}.mdi-battery-charging-wireless-60:before{content:"\f080d"}.mdi-battery-charging-wireless-70:before{content:"\f080e"}.mdi-battery-charging-wireless-80:before{content:"\f080f"}.mdi-battery-charging-wireless-90:before{content:"\f0810"}.mdi-battery-charging-wireless-alert:before{content:"\f0811"}.mdi-battery-charging-wireless-outline:before{content:"\f0812"}.mdi-battery-check:before{content:"\f17e2"}.mdi-battery-check-outline:before{content:"\f17e3"}.mdi-battery-clock:before{content:"\f19e5"}.mdi-battery-clock-outline:before{content:"\f19e6"}.mdi-battery-heart:before{content:"\f120f"}.mdi-battery-heart-outline:before{content:"\f1210"}.mdi-battery-heart-variant:before{content:"\f1211"}.mdi-battery-high:before{content:"\f12a3"}.mdi-battery-lock:before{content:"\f179c"}.mdi-battery-lock-open:before{content:"\f179d"}.mdi-battery-low:before{content:"\f12a1"}.mdi-battery-medium:before{content:"\f12a2"}.mdi-battery-minus:before{content:"\f17e4"}.mdi-battery-minus-outline:before{content:"\f17e5"}.mdi-battery-minus-variant:before{content:"\f008c"}.mdi-battery-negative:before{content:"\f008d"}.mdi-battery-off:before{content:"\f125d"}.mdi-battery-off-outline:before{content:"\f125e"}.mdi-battery-outline:before{content:"\f008e"}.mdi-battery-plus:before{content:"\f17e6"}.mdi-battery-plus-outline:before{content:"\f17e7"}.mdi-battery-plus-variant:before{content:"\f008f"}.mdi-battery-positive:before{content:"\f0090"}.mdi-battery-remove:before{content:"\f17e8"}.mdi-battery-remove-outline:before{content:"\f17e9"}.mdi-battery-sync:before{content:"\f1834"}.mdi-battery-sync-outline:before{content:"\f1835"}.mdi-battery-unknown:before{content:"\f0091"}.mdi-battery-unknown-bluetooth:before{content:"\f094a"}.mdi-beach:before{content:"\f0092"}.mdi-beaker:before{content:"\f0cea"}.mdi-beaker-alert:before{content:"\f1229"}.mdi-beaker-alert-outline:before{content:"\f122a"}.mdi-beaker-check:before{content:"\f122b"}.mdi-beaker-check-outline:before{content:"\f122c"}.mdi-beaker-minus:before{content:"\f122d"}.mdi-beaker-minus-outline:before{content:"\f122e"}.mdi-beaker-outline:before{content:"\f0690"}.mdi-beaker-plus:before{content:"\f122f"}.mdi-beaker-plus-outline:before{content:"\f1230"}.mdi-beaker-question:before{content:"\f1231"}.mdi-beaker-question-outline:before{content:"\f1232"}.mdi-beaker-remove:before{content:"\f1233"}.mdi-beaker-remove-outline:before{content:"\f1234"}.mdi-bed:before{content:"\f02e3"}.mdi-bed-clock:before{content:"\f1b94"}.mdi-bed-double:before{content:"\f0fd4"}.mdi-bed-double-outline:before{content:"\f0fd3"}.mdi-bed-empty:before{content:"\f08a0"}.mdi-bed-king:before{content:"\f0fd2"}.mdi-bed-king-outline:before{content:"\f0fd1"}.mdi-bed-outline:before{content:"\f0099"}.mdi-bed-queen:before{content:"\f0fd0"}.mdi-bed-queen-outline:before{content:"\f0fdb"}.mdi-bed-single:before{content:"\f106d"}.mdi-bed-single-outline:before{content:"\f106e"}.mdi-bee:before{content:"\f0fa1"}.mdi-bee-flower:before{content:"\f0fa2"}.mdi-beehive-off-outline:before{content:"\f13ed"}.mdi-beehive-outline:before{content:"\f10ce"}.mdi-beekeeper:before{content:"\f14e2"}.mdi-beer:before{content:"\f0098"}.mdi-beer-outline:before{content:"\f130c"}.mdi-bell:before{content:"\f009a"}.mdi-bell-alert:before{content:"\f0d59"}.mdi-bell-alert-outline:before{content:"\f0e81"}.mdi-bell-badge:before{content:"\f116b"}.mdi-bell-badge-outline:before{content:"\f0178"}.mdi-bell-cancel:before{content:"\f13e7"}.mdi-bell-cancel-outline:before{content:"\f13e8"}.mdi-bell-check:before{content:"\f11e5"}.mdi-bell-check-outline:before{content:"\f11e6"}.mdi-bell-circle:before{content:"\f0d5a"}.mdi-bell-circle-outline:before{content:"\f0d5b"}.mdi-bell-cog:before{content:"\f1a29"}.mdi-bell-cog-outline:before{content:"\f1a2a"}.mdi-bell-minus:before{content:"\f13e9"}.mdi-bell-minus-outline:before{content:"\f13ea"}.mdi-bell-off:before{content:"\f009b"}.mdi-bell-off-outline:before{content:"\f0a91"}.mdi-bell-outline:before{content:"\f009c"}.mdi-bell-plus:before{content:"\f009d"}.mdi-bell-plus-outline:before{content:"\f0a92"}.mdi-bell-remove:before{content:"\f13eb"}.mdi-bell-remove-outline:before{content:"\f13ec"}.mdi-bell-ring:before{content:"\f009e"}.mdi-bell-ring-outline:before{content:"\f009f"}.mdi-bell-sleep:before{content:"\f00a0"}.mdi-bell-sleep-outline:before{content:"\f0a93"}.mdi-bench:before{content:"\f1c21"}.mdi-bench-back:before{content:"\f1c22"}.mdi-beta:before{content:"\f00a1"}.mdi-betamax:before{content:"\f09cb"}.mdi-biathlon:before{content:"\f0e14"}.mdi-bicycle:before{content:"\f109c"}.mdi-bicycle-basket:before{content:"\f1235"}.mdi-bicycle-cargo:before{content:"\f189c"}.mdi-bicycle-electric:before{content:"\f15b4"}.mdi-bicycle-penny-farthing:before{content:"\f15e9"}.mdi-bike:before{content:"\f00a3"}.mdi-bike-fast:before{content:"\f111f"}.mdi-bike-pedal:before{content:"\f1c23"}.mdi-bike-pedal-clipless:before{content:"\f1c24"}.mdi-bike-pedal-mountain:before{content:"\f1c25"}.mdi-billboard:before{content:"\f1010"}.mdi-billiards:before{content:"\f0b61"}.mdi-billiards-rack:before{content:"\f0b62"}.mdi-binoculars:before{content:"\f00a5"}.mdi-bio:before{content:"\f00a6"}.mdi-biohazard:before{content:"\f00a7"}.mdi-bird:before{content:"\f15c6"}.mdi-bitbucket:before{content:"\f00a8"}.mdi-bitcoin:before{content:"\f0813"}.mdi-black-mesa:before{content:"\f00a9"}.mdi-blender:before{content:"\f0ceb"}.mdi-blender-outline:before{content:"\f181a"}.mdi-blender-software:before{content:"\f00ab"}.mdi-blinds:before{content:"\f00ac"}.mdi-blinds-horizontal:before{content:"\f1a2b"}.mdi-blinds-horizontal-closed:before{content:"\f1a2c"}.mdi-blinds-open:before{content:"\f1011"}.mdi-blinds-vertical:before{content:"\f1a2d"}.mdi-blinds-vertical-closed:before{content:"\f1a2e"}.mdi-block-helper:before{content:"\f00ad"}.mdi-blood-bag:before{content:"\f0cec"}.mdi-bluetooth:before{content:"\f00af"}.mdi-bluetooth-audio:before{content:"\f00b0"}.mdi-bluetooth-connect:before{content:"\f00b1"}.mdi-bluetooth-off:before{content:"\f00b2"}.mdi-bluetooth-settings:before{content:"\f00b3"}.mdi-bluetooth-transfer:before{content:"\f00b4"}.mdi-blur:before{content:"\f00b5"}.mdi-blur-linear:before{content:"\f00b6"}.mdi-blur-off:before{content:"\f00b7"}.mdi-blur-radial:before{content:"\f00b8"}.mdi-bolt:before{content:"\f0db3"}.mdi-bomb:before{content:"\f0691"}.mdi-bomb-off:before{content:"\f06c5"}.mdi-bone:before{content:"\f00b9"}.mdi-bone-off:before{content:"\f19e0"}.mdi-book:before{content:"\f00ba"}.mdi-book-account:before{content:"\f13ad"}.mdi-book-account-outline:before{content:"\f13ae"}.mdi-book-alert:before{content:"\f167c"}.mdi-book-alert-outline:before{content:"\f167d"}.mdi-book-alphabet:before{content:"\f061d"}.mdi-book-arrow-down:before{content:"\f167e"}.mdi-book-arrow-down-outline:before{content:"\f167f"}.mdi-book-arrow-left:before{content:"\f1680"}.mdi-book-arrow-left-outline:before{content:"\f1681"}.mdi-book-arrow-right:before{content:"\f1682"}.mdi-book-arrow-right-outline:before{content:"\f1683"}.mdi-book-arrow-up:before{content:"\f1684"}.mdi-book-arrow-up-outline:before{content:"\f1685"}.mdi-book-cancel:before{content:"\f1686"}.mdi-book-cancel-outline:before{content:"\f1687"}.mdi-book-check:before{content:"\f14f3"}.mdi-book-check-outline:before{content:"\f14f4"}.mdi-book-clock:before{content:"\f1688"}.mdi-book-clock-outline:before{content:"\f1689"}.mdi-book-cog:before{content:"\f168a"}.mdi-book-cog-outline:before{content:"\f168b"}.mdi-book-cross:before{content:"\f00a2"}.mdi-book-edit:before{content:"\f168c"}.mdi-book-edit-outline:before{content:"\f168d"}.mdi-book-education:before{content:"\f16c9"}.mdi-book-education-outline:before{content:"\f16ca"}.mdi-book-heart:before{content:"\f1a1d"}.mdi-book-heart-outline:before{content:"\f1a1e"}.mdi-book-information-variant:before{content:"\f106f"}.mdi-book-lock:before{content:"\f079a"}.mdi-book-lock-open:before{content:"\f079b"}.mdi-book-lock-open-outline:before{content:"\f168e"}.mdi-book-lock-outline:before{content:"\f168f"}.mdi-book-marker:before{content:"\f1690"}.mdi-book-marker-outline:before{content:"\f1691"}.mdi-book-minus:before{content:"\f05d9"}.mdi-book-minus-multiple:before{content:"\f0a94"}.mdi-book-minus-multiple-outline:before{content:"\f090b"}.mdi-book-minus-outline:before{content:"\f1692"}.mdi-book-multiple:before{content:"\f00bb"}.mdi-book-multiple-outline:before{content:"\f0436"}.mdi-book-music:before{content:"\f0067"}.mdi-book-music-outline:before{content:"\f1693"}.mdi-book-off:before{content:"\f1694"}.mdi-book-off-outline:before{content:"\f1695"}.mdi-book-open:before{content:"\f00bd"}.mdi-book-open-blank-variant:before{content:"\f00be"}.mdi-book-open-blank-variant-outline:before{content:"\f1ccb"}.mdi-book-open-outline:before{content:"\f0b63"}.mdi-book-open-page-variant:before{content:"\f05da"}.mdi-book-open-page-variant-outline:before{content:"\f15d6"}.mdi-book-open-variant:before{content:"\f14f7"}.mdi-book-open-variant-outline:before{content:"\f1ccc"}.mdi-book-outline:before{content:"\f0b64"}.mdi-book-play:before{content:"\f0e82"}.mdi-book-play-outline:before{content:"\f0e83"}.mdi-book-plus:before{content:"\f05db"}.mdi-book-plus-multiple:before{content:"\f0a95"}.mdi-book-plus-multiple-outline:before{content:"\f0ade"}.mdi-book-plus-outline:before{content:"\f1696"}.mdi-book-refresh:before{content:"\f1697"}.mdi-book-refresh-outline:before{content:"\f1698"}.mdi-book-remove:before{content:"\f0a97"}.mdi-book-remove-multiple:before{content:"\f0a96"}.mdi-book-remove-multiple-outline:before{content:"\f04ca"}.mdi-book-remove-outline:before{content:"\f1699"}.mdi-book-search:before{content:"\f0e84"}.mdi-book-search-outline:before{content:"\f0e85"}.mdi-book-settings:before{content:"\f169a"}.mdi-book-settings-outline:before{content:"\f169b"}.mdi-book-sync:before{content:"\f169c"}.mdi-book-sync-outline:before{content:"\f16c8"}.mdi-book-variant:before{content:"\f00bf"}.mdi-bookmark:before{content:"\f00c0"}.mdi-bookmark-box:before{content:"\f1b75"}.mdi-bookmark-box-multiple:before{content:"\f196c"}.mdi-bookmark-box-multiple-outline:before{content:"\f196d"}.mdi-bookmark-box-outline:before{content:"\f1b76"}.mdi-bookmark-check:before{content:"\f00c1"}.mdi-bookmark-check-outline:before{content:"\f137b"}.mdi-bookmark-minus:before{content:"\f09cc"}.mdi-bookmark-minus-outline:before{content:"\f09cd"}.mdi-bookmark-multiple:before{content:"\f0e15"}.mdi-bookmark-multiple-outline:before{content:"\f0e16"}.mdi-bookmark-music:before{content:"\f00c2"}.mdi-bookmark-music-outline:before{content:"\f1379"}.mdi-bookmark-off:before{content:"\f09ce"}.mdi-bookmark-off-outline:before{content:"\f09cf"}.mdi-bookmark-outline:before{content:"\f00c3"}.mdi-bookmark-plus:before{content:"\f00c5"}.mdi-bookmark-plus-outline:before{content:"\f00c4"}.mdi-bookmark-remove:before{content:"\f00c6"}.mdi-bookmark-remove-outline:before{content:"\f137a"}.mdi-bookshelf:before{content:"\f125f"}.mdi-boom-gate:before{content:"\f0e86"}.mdi-boom-gate-alert:before{content:"\f0e87"}.mdi-boom-gate-alert-outline:before{content:"\f0e88"}.mdi-boom-gate-arrow-down:before{content:"\f0e89"}.mdi-boom-gate-arrow-down-outline:before{content:"\f0e8a"}.mdi-boom-gate-arrow-up:before{content:"\f0e8c"}.mdi-boom-gate-arrow-up-outline:before{content:"\f0e8d"}.mdi-boom-gate-outline:before{content:"\f0e8b"}.mdi-boom-gate-up:before{content:"\f17f9"}.mdi-boom-gate-up-outline:before{content:"\f17fa"}.mdi-boombox:before{content:"\f05dc"}.mdi-boomerang:before{content:"\f10cf"}.mdi-bootstrap:before{content:"\f06c6"}.mdi-border-all:before{content:"\f00c7"}.mdi-border-all-variant:before{content:"\f08a1"}.mdi-border-bottom:before{content:"\f00c8"}.mdi-border-bottom-variant:before{content:"\f08a2"}.mdi-border-color:before{content:"\f00c9"}.mdi-border-horizontal:before{content:"\f00ca"}.mdi-border-inside:before{content:"\f00cb"}.mdi-border-left:before{content:"\f00cc"}.mdi-border-left-variant:before{content:"\f08a3"}.mdi-border-none:before{content:"\f00cd"}.mdi-border-none-variant:before{content:"\f08a4"}.mdi-border-outside:before{content:"\f00ce"}.mdi-border-radius:before{content:"\f1af4"}.mdi-border-right:before{content:"\f00cf"}.mdi-border-right-variant:before{content:"\f08a5"}.mdi-border-style:before{content:"\f00d0"}.mdi-border-top:before{content:"\f00d1"}.mdi-border-top-variant:before{content:"\f08a6"}.mdi-border-vertical:before{content:"\f00d2"}.mdi-bottle-soda:before{content:"\f1070"}.mdi-bottle-soda-classic:before{content:"\f1071"}.mdi-bottle-soda-classic-outline:before{content:"\f1363"}.mdi-bottle-soda-outline:before{content:"\f1072"}.mdi-bottle-tonic:before{content:"\f112e"}.mdi-bottle-tonic-outline:before{content:"\f112f"}.mdi-bottle-tonic-plus:before{content:"\f1130"}.mdi-bottle-tonic-plus-outline:before{content:"\f1131"}.mdi-bottle-tonic-skull:before{content:"\f1132"}.mdi-bottle-tonic-skull-outline:before{content:"\f1133"}.mdi-bottle-wine:before{content:"\f0854"}.mdi-bottle-wine-outline:before{content:"\f1310"}.mdi-bow-arrow:before{content:"\f1841"}.mdi-bow-tie:before{content:"\f0678"}.mdi-bowl:before{content:"\f028e"}.mdi-bowl-mix:before{content:"\f0617"}.mdi-bowl-mix-outline:before{content:"\f02e4"}.mdi-bowl-outline:before{content:"\f02a9"}.mdi-bowling:before{content:"\f00d3"}.mdi-box:before{content:"\f00d4"}.mdi-box-cutter:before{content:"\f00d5"}.mdi-box-cutter-off:before{content:"\f0b4a"}.mdi-box-shadow:before{content:"\f0637"}.mdi-boxing-glove:before{content:"\f0b65"}.mdi-braille:before{content:"\f09d0"}.mdi-brain:before{content:"\f09d1"}.mdi-bread-slice:before{content:"\f0cee"}.mdi-bread-slice-outline:before{content:"\f0cef"}.mdi-bridge:before{content:"\f0618"}.mdi-briefcase:before{content:"\f00d6"}.mdi-briefcase-account:before{content:"\f0cf0"}.mdi-briefcase-account-outline:before{content:"\f0cf1"}.mdi-briefcase-arrow-left-right:before{content:"\f1a8d"}.mdi-briefcase-arrow-left-right-outline:before{content:"\f1a8e"}.mdi-briefcase-arrow-up-down:before{content:"\f1a8f"}.mdi-briefcase-arrow-up-down-outline:before{content:"\f1a90"}.mdi-briefcase-check:before{content:"\f00d7"}.mdi-briefcase-check-outline:before{content:"\f131e"}.mdi-briefcase-clock:before{content:"\f10d0"}.mdi-briefcase-clock-outline:before{content:"\f10d1"}.mdi-briefcase-download:before{content:"\f00d8"}.mdi-briefcase-download-outline:before{content:"\f0c3d"}.mdi-briefcase-edit:before{content:"\f0a98"}.mdi-briefcase-edit-outline:before{content:"\f0c3e"}.mdi-briefcase-eye:before{content:"\f17d9"}.mdi-briefcase-eye-outline:before{content:"\f17da"}.mdi-briefcase-minus:before{content:"\f0a2a"}.mdi-briefcase-minus-outline:before{content:"\f0c3f"}.mdi-briefcase-off:before{content:"\f1658"}.mdi-briefcase-off-outline:before{content:"\f1659"}.mdi-briefcase-outline:before{content:"\f0814"}.mdi-briefcase-plus:before{content:"\f0a2b"}.mdi-briefcase-plus-outline:before{content:"\f0c40"}.mdi-briefcase-remove:before{content:"\f0a2c"}.mdi-briefcase-remove-outline:before{content:"\f0c41"}.mdi-briefcase-search:before{content:"\f0a2d"}.mdi-briefcase-search-outline:before{content:"\f0c42"}.mdi-briefcase-upload:before{content:"\f00d9"}.mdi-briefcase-upload-outline:before{content:"\f0c43"}.mdi-briefcase-variant:before{content:"\f1494"}.mdi-briefcase-variant-off:before{content:"\f165a"}.mdi-briefcase-variant-off-outline:before{content:"\f165b"}.mdi-briefcase-variant-outline:before{content:"\f1495"}.mdi-brightness-1:before{content:"\f00da"}.mdi-brightness-2:before{content:"\f00db"}.mdi-brightness-3:before{content:"\f00dc"}.mdi-brightness-4:before{content:"\f00dd"}.mdi-brightness-5:before{content:"\f00de"}.mdi-brightness-6:before{content:"\f00df"}.mdi-brightness-7:before{content:"\f00e0"}.mdi-brightness-auto:before{content:"\f00e1"}.mdi-brightness-percent:before{content:"\f0cf2"}.mdi-broadcast:before{content:"\f1720"}.mdi-broadcast-off:before{content:"\f1721"}.mdi-broom:before{content:"\f00e2"}.mdi-brush:before{content:"\f00e3"}.mdi-brush-off:before{content:"\f1771"}.mdi-brush-outline:before{content:"\f1a0d"}.mdi-brush-variant:before{content:"\f1813"}.mdi-bucket:before{content:"\f1415"}.mdi-bucket-outline:before{content:"\f1416"}.mdi-buffet:before{content:"\f0578"}.mdi-bug:before{content:"\f00e4"}.mdi-bug-check:before{content:"\f0a2e"}.mdi-bug-check-outline:before{content:"\f0a2f"}.mdi-bug-outline:before{content:"\f0a30"}.mdi-bug-pause:before{content:"\f1af5"}.mdi-bug-pause-outline:before{content:"\f1af6"}.mdi-bug-play:before{content:"\f1af7"}.mdi-bug-play-outline:before{content:"\f1af8"}.mdi-bug-stop:before{content:"\f1af9"}.mdi-bug-stop-outline:before{content:"\f1afa"}.mdi-bugle:before{content:"\f0db4"}.mdi-bulkhead-light:before{content:"\f1a2f"}.mdi-bulldozer:before{content:"\f0b22"}.mdi-bullet:before{content:"\f0cf3"}.mdi-bulletin-board:before{content:"\f00e5"}.mdi-bullhorn:before{content:"\f00e6"}.mdi-bullhorn-outline:before{content:"\f0b23"}.mdi-bullhorn-variant:before{content:"\f196e"}.mdi-bullhorn-variant-outline:before{content:"\f196f"}.mdi-bullseye:before{content:"\f05dd"}.mdi-bullseye-arrow:before{content:"\f08c9"}.mdi-bulma:before{content:"\f12e7"}.mdi-bunk-bed:before{content:"\f1302"}.mdi-bunk-bed-outline:before{content:"\f0097"}.mdi-bus:before{content:"\f00e7"}.mdi-bus-alert:before{content:"\f0a99"}.mdi-bus-articulated-end:before{content:"\f079c"}.mdi-bus-articulated-front:before{content:"\f079d"}.mdi-bus-clock:before{content:"\f08ca"}.mdi-bus-double-decker:before{content:"\f079e"}.mdi-bus-electric:before{content:"\f191d"}.mdi-bus-marker:before{content:"\f1212"}.mdi-bus-multiple:before{content:"\f0f3f"}.mdi-bus-school:before{content:"\f079f"}.mdi-bus-side:before{content:"\f07a0"}.mdi-bus-sign:before{content:"\f1cc1"}.mdi-bus-stop:before{content:"\f1012"}.mdi-bus-stop-covered:before{content:"\f1013"}.mdi-bus-stop-uncovered:before{content:"\f1014"}.mdi-bus-wrench:before{content:"\f1cc2"}.mdi-butterfly:before{content:"\f1589"}.mdi-butterfly-outline:before{content:"\f158a"}.mdi-button-cursor:before{content:"\f1b4f"}.mdi-button-pointer:before{content:"\f1b50"}.mdi-cabin-a-frame:before{content:"\f188c"}.mdi-cable-data:before{content:"\f1394"}.mdi-cached:before{content:"\f00e8"}.mdi-cactus:before{content:"\f0db5"}.mdi-cake:before{content:"\f00e9"}.mdi-cake-layered:before{content:"\f00ea"}.mdi-cake-variant:before{content:"\f00eb"}.mdi-cake-variant-outline:before{content:"\f17f0"}.mdi-calculator:before{content:"\f00ec"}.mdi-calculator-variant:before{content:"\f0a9a"}.mdi-calculator-variant-outline:before{content:"\f15a6"}.mdi-calendar:before{content:"\f00ed"}.mdi-calendar-account:before{content:"\f0ed7"}.mdi-calendar-account-outline:before{content:"\f0ed8"}.mdi-calendar-alert:before{content:"\f0a31"}.mdi-calendar-alert-outline:before{content:"\f1b62"}.mdi-calendar-arrow-left:before{content:"\f1134"}.mdi-calendar-arrow-right:before{content:"\f1135"}.mdi-calendar-badge:before{content:"\f1b9d"}.mdi-calendar-badge-outline:before{content:"\f1b9e"}.mdi-calendar-blank:before{content:"\f00ee"}.mdi-calendar-blank-multiple:before{content:"\f1073"}.mdi-calendar-blank-outline:before{content:"\f0b66"}.mdi-calendar-check:before{content:"\f00ef"}.mdi-calendar-check-outline:before{content:"\f0c44"}.mdi-calendar-clock:before{content:"\f00f0"}.mdi-calendar-clock-outline:before{content:"\f16e1"}.mdi-calendar-collapse-horizontal:before{content:"\f189d"}.mdi-calendar-collapse-horizontal-outline:before{content:"\f1b63"}.mdi-calendar-cursor:before{content:"\f157b"}.mdi-calendar-cursor-outline:before{content:"\f1b64"}.mdi-calendar-edit:before{content:"\f08a7"}.mdi-calendar-edit-outline:before{content:"\f1b65"}.mdi-calendar-end:before{content:"\f166c"}.mdi-calendar-end-outline:before{content:"\f1b66"}.mdi-calendar-expand-horizontal:before{content:"\f189e"}.mdi-calendar-expand-horizontal-outline:before{content:"\f1b67"}.mdi-calendar-export:before{content:"\f0b24"}.mdi-calendar-export-outline:before{content:"\f1b68"}.mdi-calendar-filter:before{content:"\f1a32"}.mdi-calendar-filter-outline:before{content:"\f1a33"}.mdi-calendar-heart:before{content:"\f09d2"}.mdi-calendar-heart-outline:before{content:"\f1b69"}.mdi-calendar-import:before{content:"\f0b25"}.mdi-calendar-import-outline:before{content:"\f1b6a"}.mdi-calendar-lock:before{content:"\f1641"}.mdi-calendar-lock-open:before{content:"\f1b5b"}.mdi-calendar-lock-open-outline:before{content:"\f1b5c"}.mdi-calendar-lock-outline:before{content:"\f1642"}.mdi-calendar-minus:before{content:"\f0d5c"}.mdi-calendar-minus-outline:before{content:"\f1b6b"}.mdi-calendar-month:before{content:"\f0e17"}.mdi-calendar-month-outline:before{content:"\f0e18"}.mdi-calendar-multiple:before{content:"\f00f1"}.mdi-calendar-multiple-check:before{content:"\f00f2"}.mdi-calendar-multiselect:before{content:"\f0a32"}.mdi-calendar-multiselect-outline:before{content:"\f1b55"}.mdi-calendar-outline:before{content:"\f0b67"}.mdi-calendar-plus:before{content:"\f00f3"}.mdi-calendar-plus-outline:before{content:"\f1b6c"}.mdi-calendar-question:before{content:"\f0692"}.mdi-calendar-question-outline:before{content:"\f1b6d"}.mdi-calendar-range:before{content:"\f0679"}.mdi-calendar-range-outline:before{content:"\f0b68"}.mdi-calendar-refresh:before{content:"\f01e1"}.mdi-calendar-refresh-outline:before{content:"\f0203"}.mdi-calendar-remove:before{content:"\f00f4"}.mdi-calendar-remove-outline:before{content:"\f0c45"}.mdi-calendar-search:before{content:"\f094c"}.mdi-calendar-search-outline:before{content:"\f1b6e"}.mdi-calendar-star:before{content:"\f09d3"}.mdi-calendar-star-four-points:before{content:"\f1c1f"}.mdi-calendar-star-outline:before{content:"\f1b53"}.mdi-calendar-start:before{content:"\f166d"}.mdi-calendar-start-outline:before{content:"\f1b6f"}.mdi-calendar-sync:before{content:"\f0e8e"}.mdi-calendar-sync-outline:before{content:"\f0e8f"}.mdi-calendar-text:before{content:"\f00f5"}.mdi-calendar-text-outline:before{content:"\f0c46"}.mdi-calendar-today:before{content:"\f00f6"}.mdi-calendar-today-outline:before{content:"\f1a30"}.mdi-calendar-week:before{content:"\f0a33"}.mdi-calendar-week-begin:before{content:"\f0a34"}.mdi-calendar-week-begin-outline:before{content:"\f1a31"}.mdi-calendar-week-outline:before{content:"\f1a34"}.mdi-calendar-weekend:before{content:"\f0ed9"}.mdi-calendar-weekend-outline:before{content:"\f0eda"}.mdi-call-made:before{content:"\f00f7"}.mdi-call-merge:before{content:"\f00f8"}.mdi-call-missed:before{content:"\f00f9"}.mdi-call-received:before{content:"\f00fa"}.mdi-call-split:before{content:"\f00fb"}.mdi-camcorder:before{content:"\f00fc"}.mdi-camcorder-off:before{content:"\f00ff"}.mdi-camera:before{content:"\f0100"}.mdi-camera-account:before{content:"\f08cb"}.mdi-camera-burst:before{content:"\f0693"}.mdi-camera-control:before{content:"\f0b69"}.mdi-camera-document:before{content:"\f1871"}.mdi-camera-document-off:before{content:"\f1872"}.mdi-camera-enhance:before{content:"\f0101"}.mdi-camera-enhance-outline:before{content:"\f0b6a"}.mdi-camera-flip:before{content:"\f15d9"}.mdi-camera-flip-outline:before{content:"\f15da"}.mdi-camera-front:before{content:"\f0102"}.mdi-camera-front-variant:before{content:"\f0103"}.mdi-camera-gopro:before{content:"\f07a1"}.mdi-camera-image:before{content:"\f08cc"}.mdi-camera-iris:before{content:"\f0104"}.mdi-camera-lock:before{content:"\f1a14"}.mdi-camera-lock-open:before{content:"\f1c0d"}.mdi-camera-lock-open-outline:before{content:"\f1c0e"}.mdi-camera-lock-outline:before{content:"\f1a15"}.mdi-camera-marker:before{content:"\f19a7"}.mdi-camera-marker-outline:before{content:"\f19a8"}.mdi-camera-metering-center:before{content:"\f07a2"}.mdi-camera-metering-matrix:before{content:"\f07a3"}.mdi-camera-metering-partial:before{content:"\f07a4"}.mdi-camera-metering-spot:before{content:"\f07a5"}.mdi-camera-off:before{content:"\f05df"}.mdi-camera-off-outline:before{content:"\f19bf"}.mdi-camera-outline:before{content:"\f0d5d"}.mdi-camera-party-mode:before{content:"\f0105"}.mdi-camera-plus:before{content:"\f0edb"}.mdi-camera-plus-outline:before{content:"\f0edc"}.mdi-camera-rear:before{content:"\f0106"}.mdi-camera-rear-variant:before{content:"\f0107"}.mdi-camera-retake:before{content:"\f0e19"}.mdi-camera-retake-outline:before{content:"\f0e1a"}.mdi-camera-switch:before{content:"\f0108"}.mdi-camera-switch-outline:before{content:"\f084a"}.mdi-camera-timer:before{content:"\f0109"}.mdi-camera-wireless:before{content:"\f0db6"}.mdi-camera-wireless-outline:before{content:"\f0db7"}.mdi-campfire:before{content:"\f0edd"}.mdi-cancel:before{content:"\f073a"}.mdi-candelabra:before{content:"\f17d2"}.mdi-candelabra-fire:before{content:"\f17d3"}.mdi-candle:before{content:"\f05e2"}.mdi-candy:before{content:"\f1970"}.mdi-candy-off:before{content:"\f1971"}.mdi-candy-off-outline:before{content:"\f1972"}.mdi-candy-outline:before{content:"\f1973"}.mdi-candycane:before{content:"\f010a"}.mdi-cannabis:before{content:"\f07a6"}.mdi-cannabis-off:before{content:"\f166e"}.mdi-caps-lock:before{content:"\f0a9b"}.mdi-car:before{content:"\f010b"}.mdi-car-2-plus:before{content:"\f1015"}.mdi-car-3-plus:before{content:"\f1016"}.mdi-car-arrow-left:before{content:"\f13b2"}.mdi-car-arrow-right:before{content:"\f13b3"}.mdi-car-back:before{content:"\f0e1b"}.mdi-car-battery:before{content:"\f010c"}.mdi-car-brake-abs:before{content:"\f0c47"}.mdi-car-brake-alert:before{content:"\f0c48"}.mdi-car-brake-fluid-level:before{content:"\f1909"}.mdi-car-brake-hold:before{content:"\f0d5e"}.mdi-car-brake-low-pressure:before{content:"\f190a"}.mdi-car-brake-parking:before{content:"\f0d5f"}.mdi-car-brake-retarder:before{content:"\f1017"}.mdi-car-brake-temperature:before{content:"\f190b"}.mdi-car-brake-worn-linings:before{content:"\f190c"}.mdi-car-child-seat:before{content:"\f0fa3"}.mdi-car-clock:before{content:"\f1974"}.mdi-car-clutch:before{content:"\f1018"}.mdi-car-cog:before{content:"\f13cc"}.mdi-car-connected:before{content:"\f010d"}.mdi-car-convertible:before{content:"\f07a7"}.mdi-car-coolant-level:before{content:"\f1019"}.mdi-car-cruise-control:before{content:"\f0d60"}.mdi-car-defrost-front:before{content:"\f0d61"}.mdi-car-defrost-rear:before{content:"\f0d62"}.mdi-car-door:before{content:"\f0b6b"}.mdi-car-door-lock:before{content:"\f109d"}.mdi-car-door-lock-open:before{content:"\f1c81"}.mdi-car-electric:before{content:"\f0b6c"}.mdi-car-electric-outline:before{content:"\f15b5"}.mdi-car-emergency:before{content:"\f160f"}.mdi-car-esp:before{content:"\f0c49"}.mdi-car-estate:before{content:"\f07a8"}.mdi-car-hatchback:before{content:"\f07a9"}.mdi-car-info:before{content:"\f11be"}.mdi-car-key:before{content:"\f0b6d"}.mdi-car-lifted-pickup:before{content:"\f152d"}.mdi-car-light-alert:before{content:"\f190d"}.mdi-car-light-dimmed:before{content:"\f0c4a"}.mdi-car-light-fog:before{content:"\f0c4b"}.mdi-car-light-high:before{content:"\f0c4c"}.mdi-car-limousine:before{content:"\f08cd"}.mdi-car-multiple:before{content:"\f0b6e"}.mdi-car-off:before{content:"\f0e1c"}.mdi-car-outline:before{content:"\f14ed"}.mdi-car-parking-lights:before{content:"\f0d63"}.mdi-car-pickup:before{content:"\f07aa"}.mdi-car-search:before{content:"\f1b8d"}.mdi-car-search-outline:before{content:"\f1b8e"}.mdi-car-seat:before{content:"\f0fa4"}.mdi-car-seat-cooler:before{content:"\f0fa5"}.mdi-car-seat-heater:before{content:"\f0fa6"}.mdi-car-select:before{content:"\f1879"}.mdi-car-settings:before{content:"\f13cd"}.mdi-car-shift-pattern:before{content:"\f0f40"}.mdi-car-side:before{content:"\f07ab"}.mdi-car-speed-limiter:before{content:"\f190e"}.mdi-car-sports:before{content:"\f07ac"}.mdi-car-tire-alert:before{content:"\f0c4d"}.mdi-car-traction-control:before{content:"\f0d64"}.mdi-car-turbocharger:before{content:"\f101a"}.mdi-car-wash:before{content:"\f010e"}.mdi-car-windshield:before{content:"\f101b"}.mdi-car-windshield-outline:before{content:"\f101c"}.mdi-car-wireless:before{content:"\f1878"}.mdi-car-wrench:before{content:"\f1814"}.mdi-carabiner:before{content:"\f14c0"}.mdi-caravan:before{content:"\f07ad"}.mdi-card:before{content:"\f0b6f"}.mdi-card-account-details:before{content:"\f05d2"}.mdi-card-account-details-outline:before{content:"\f0dab"}.mdi-card-account-details-star:before{content:"\f02a3"}.mdi-card-account-details-star-outline:before{content:"\f06db"}.mdi-card-account-mail:before{content:"\f018e"}.mdi-card-account-mail-outline:before{content:"\f0e98"}.mdi-card-account-phone:before{content:"\f0e99"}.mdi-card-account-phone-outline:before{content:"\f0e9a"}.mdi-card-bulleted:before{content:"\f0b70"}.mdi-card-bulleted-off:before{content:"\f0b71"}.mdi-card-bulleted-off-outline:before{content:"\f0b72"}.mdi-card-bulleted-outline:before{content:"\f0b73"}.mdi-card-bulleted-settings:before{content:"\f0b74"}.mdi-card-bulleted-settings-outline:before{content:"\f0b75"}.mdi-card-minus:before{content:"\f1600"}.mdi-card-minus-outline:before{content:"\f1601"}.mdi-card-multiple:before{content:"\f17f1"}.mdi-card-multiple-outline:before{content:"\f17f2"}.mdi-card-off:before{content:"\f1602"}.mdi-card-off-outline:before{content:"\f1603"}.mdi-card-outline:before{content:"\f0b76"}.mdi-card-plus:before{content:"\f11ff"}.mdi-card-plus-outline:before{content:"\f1200"}.mdi-card-remove:before{content:"\f1604"}.mdi-card-remove-outline:before{content:"\f1605"}.mdi-card-search:before{content:"\f1074"}.mdi-card-search-outline:before{content:"\f1075"}.mdi-card-text:before{content:"\f0b77"}.mdi-card-text-outline:before{content:"\f0b78"}.mdi-cards:before{content:"\f0638"}.mdi-cards-club:before{content:"\f08ce"}.mdi-cards-club-outline:before{content:"\f189f"}.mdi-cards-diamond:before{content:"\f08cf"}.mdi-cards-diamond-outline:before{content:"\f101d"}.mdi-cards-heart:before{content:"\f08d0"}.mdi-cards-heart-outline:before{content:"\f18a0"}.mdi-cards-outline:before{content:"\f0639"}.mdi-cards-playing:before{content:"\f18a1"}.mdi-cards-playing-club:before{content:"\f18a2"}.mdi-cards-playing-club-multiple:before{content:"\f18a3"}.mdi-cards-playing-club-multiple-outline:before{content:"\f18a4"}.mdi-cards-playing-club-outline:before{content:"\f18a5"}.mdi-cards-playing-diamond:before{content:"\f18a6"}.mdi-cards-playing-diamond-multiple:before{content:"\f18a7"}.mdi-cards-playing-diamond-multiple-outline:before{content:"\f18a8"}.mdi-cards-playing-diamond-outline:before{content:"\f18a9"}.mdi-cards-playing-heart:before{content:"\f18aa"}.mdi-cards-playing-heart-multiple:before{content:"\f18ab"}.mdi-cards-playing-heart-multiple-outline:before{content:"\f18ac"}.mdi-cards-playing-heart-outline:before{content:"\f18ad"}.mdi-cards-playing-outline:before{content:"\f063a"}.mdi-cards-playing-spade:before{content:"\f18ae"}.mdi-cards-playing-spade-multiple:before{content:"\f18af"}.mdi-cards-playing-spade-multiple-outline:before{content:"\f18b0"}.mdi-cards-playing-spade-outline:before{content:"\f18b1"}.mdi-cards-spade:before{content:"\f08d1"}.mdi-cards-spade-outline:before{content:"\f18b2"}.mdi-cards-variant:before{content:"\f06c7"}.mdi-carrot:before{content:"\f010f"}.mdi-cart:before{content:"\f0110"}.mdi-cart-arrow-down:before{content:"\f0d66"}.mdi-cart-arrow-right:before{content:"\f0c4e"}.mdi-cart-arrow-up:before{content:"\f0d67"}.mdi-cart-check:before{content:"\f15ea"}.mdi-cart-heart:before{content:"\f18e0"}.mdi-cart-minus:before{content:"\f0d68"}.mdi-cart-off:before{content:"\f066b"}.mdi-cart-outline:before{content:"\f0111"}.mdi-cart-percent:before{content:"\f1bae"}.mdi-cart-plus:before{content:"\f0112"}.mdi-cart-remove:before{content:"\f0d69"}.mdi-cart-variant:before{content:"\f15eb"}.mdi-case-sensitive-alt:before{content:"\f0113"}.mdi-cash:before{content:"\f0114"}.mdi-cash-100:before{content:"\f0115"}.mdi-cash-check:before{content:"\f14ee"}.mdi-cash-clock:before{content:"\f1a91"}.mdi-cash-edit:before{content:"\f1cab"}.mdi-cash-fast:before{content:"\f185c"}.mdi-cash-lock:before{content:"\f14ea"}.mdi-cash-lock-open:before{content:"\f14eb"}.mdi-cash-marker:before{content:"\f0db8"}.mdi-cash-minus:before{content:"\f1260"}.mdi-cash-multiple:before{content:"\f0116"}.mdi-cash-off:before{content:"\f1c79"}.mdi-cash-plus:before{content:"\f1261"}.mdi-cash-refund:before{content:"\f0a9c"}.mdi-cash-register:before{content:"\f0cf4"}.mdi-cash-remove:before{content:"\f1262"}.mdi-cash-sync:before{content:"\f1a92"}.mdi-cassette:before{content:"\f09d4"}.mdi-cast:before{content:"\f0118"}.mdi-cast-audio:before{content:"\f101e"}.mdi-cast-audio-variant:before{content:"\f1749"}.mdi-cast-connected:before{content:"\f0119"}.mdi-cast-education:before{content:"\f0e1d"}.mdi-cast-off:before{content:"\f078a"}.mdi-cast-variant:before{content:"\f001f"}.mdi-castle:before{content:"\f011a"}.mdi-cat:before{content:"\f011b"}.mdi-cctv:before{content:"\f07ae"}.mdi-cctv-off:before{content:"\f185f"}.mdi-ceiling-fan:before{content:"\f1797"}.mdi-ceiling-fan-light:before{content:"\f1798"}.mdi-ceiling-light:before{content:"\f0769"}.mdi-ceiling-light-multiple:before{content:"\f18dd"}.mdi-ceiling-light-multiple-outline:before{content:"\f18de"}.mdi-ceiling-light-outline:before{content:"\f17c7"}.mdi-cellphone:before{content:"\f011c"}.mdi-cellphone-arrow-down:before{content:"\f09d5"}.mdi-cellphone-arrow-down-variant:before{content:"\f19c5"}.mdi-cellphone-basic:before{content:"\f011e"}.mdi-cellphone-charging:before{content:"\f1397"}.mdi-cellphone-check:before{content:"\f17fd"}.mdi-cellphone-cog:before{content:"\f0951"}.mdi-cellphone-dock:before{content:"\f011f"}.mdi-cellphone-information:before{content:"\f0f41"}.mdi-cellphone-key:before{content:"\f094e"}.mdi-cellphone-link:before{content:"\f0121"}.mdi-cellphone-link-off:before{content:"\f0122"}.mdi-cellphone-lock:before{content:"\f094f"}.mdi-cellphone-marker:before{content:"\f183a"}.mdi-cellphone-message:before{content:"\f08d3"}.mdi-cellphone-message-off:before{content:"\f10d2"}.mdi-cellphone-nfc:before{content:"\f0e90"}.mdi-cellphone-nfc-off:before{content:"\f12d8"}.mdi-cellphone-off:before{content:"\f0950"}.mdi-cellphone-play:before{content:"\f101f"}.mdi-cellphone-remove:before{content:"\f094d"}.mdi-cellphone-screenshot:before{content:"\f0a35"}.mdi-cellphone-settings:before{content:"\f0123"}.mdi-cellphone-sound:before{content:"\f0952"}.mdi-cellphone-text:before{content:"\f08d2"}.mdi-cellphone-wireless:before{content:"\f0815"}.mdi-centos:before{content:"\f111a"}.mdi-certificate:before{content:"\f0124"}.mdi-certificate-outline:before{content:"\f1188"}.mdi-chair-rolling:before{content:"\f0f48"}.mdi-chair-school:before{content:"\f0125"}.mdi-chandelier:before{content:"\f1793"}.mdi-charity:before{content:"\f0c4f"}.mdi-charity-search:before{content:"\f1c82"}.mdi-chart-arc:before{content:"\f0126"}.mdi-chart-areaspline:before{content:"\f0127"}.mdi-chart-areaspline-variant:before{content:"\f0e91"}.mdi-chart-bar:before{content:"\f0128"}.mdi-chart-bar-stacked:before{content:"\f076a"}.mdi-chart-bell-curve:before{content:"\f0c50"}.mdi-chart-bell-curve-cumulative:before{content:"\f0fa7"}.mdi-chart-box:before{content:"\f154d"}.mdi-chart-box-multiple:before{content:"\f1ccd"}.mdi-chart-box-multiple-outline:before{content:"\f1cce"}.mdi-chart-box-outline:before{content:"\f154e"}.mdi-chart-box-plus-outline:before{content:"\f154f"}.mdi-chart-bubble:before{content:"\f05e3"}.mdi-chart-donut:before{content:"\f07af"}.mdi-chart-donut-variant:before{content:"\f07b0"}.mdi-chart-gantt:before{content:"\f066c"}.mdi-chart-histogram:before{content:"\f0129"}.mdi-chart-line:before{content:"\f012a"}.mdi-chart-line-stacked:before{content:"\f076b"}.mdi-chart-line-variant:before{content:"\f07b1"}.mdi-chart-multiline:before{content:"\f08d4"}.mdi-chart-multiple:before{content:"\f1213"}.mdi-chart-pie:before{content:"\f012b"}.mdi-chart-pie-outline:before{content:"\f1bdf"}.mdi-chart-ppf:before{content:"\f1380"}.mdi-chart-sankey:before{content:"\f11df"}.mdi-chart-sankey-variant:before{content:"\f11e0"}.mdi-chart-scatter-plot:before{content:"\f0e92"}.mdi-chart-scatter-plot-hexbin:before{content:"\f066d"}.mdi-chart-timeline:before{content:"\f066e"}.mdi-chart-timeline-variant:before{content:"\f0e93"}.mdi-chart-timeline-variant-shimmer:before{content:"\f15b6"}.mdi-chart-tree:before{content:"\f0e94"}.mdi-chart-waterfall:before{content:"\f1918"}.mdi-chat:before{content:"\f0b79"}.mdi-chat-alert:before{content:"\f0b7a"}.mdi-chat-alert-outline:before{content:"\f12c9"}.mdi-chat-minus:before{content:"\f1410"}.mdi-chat-minus-outline:before{content:"\f1413"}.mdi-chat-outline:before{content:"\f0ede"}.mdi-chat-plus:before{content:"\f140f"}.mdi-chat-plus-outline:before{content:"\f1412"}.mdi-chat-processing:before{content:"\f0b7b"}.mdi-chat-processing-outline:before{content:"\f12ca"}.mdi-chat-question:before{content:"\f1738"}.mdi-chat-question-outline:before{content:"\f1739"}.mdi-chat-remove:before{content:"\f1411"}.mdi-chat-remove-outline:before{content:"\f1414"}.mdi-chat-sleep:before{content:"\f12d1"}.mdi-chat-sleep-outline:before{content:"\f12d2"}.mdi-check:before{content:"\f012c"}.mdi-check-all:before{content:"\f012d"}.mdi-check-bold:before{content:"\f0e1e"}.mdi-check-circle:before{content:"\f05e0"}.mdi-check-circle-outline:before{content:"\f05e1"}.mdi-check-decagram:before{content:"\f0791"}.mdi-check-decagram-outline:before{content:"\f1740"}.mdi-check-network:before{content:"\f0c53"}.mdi-check-network-outline:before{content:"\f0c54"}.mdi-check-outline:before{content:"\f0855"}.mdi-check-underline:before{content:"\f0e1f"}.mdi-check-underline-circle:before{content:"\f0e20"}.mdi-check-underline-circle-outline:before{content:"\f0e21"}.mdi-checkbook:before{content:"\f0a9d"}.mdi-checkbook-arrow-left:before{content:"\f1c1d"}.mdi-checkbook-arrow-right:before{content:"\f1c1e"}.mdi-checkbox-blank:before{content:"\f012e"}.mdi-checkbox-blank-badge:before{content:"\f1176"}.mdi-checkbox-blank-badge-outline:before{content:"\f0117"}.mdi-checkbox-blank-circle:before{content:"\f012f"}.mdi-checkbox-blank-circle-outline:before{content:"\f0130"}.mdi-checkbox-blank-off:before{content:"\f12ec"}.mdi-checkbox-blank-off-outline:before{content:"\f12ed"}.mdi-checkbox-blank-outline:before{content:"\f0131"}.mdi-checkbox-intermediate:before{content:"\f0856"}.mdi-checkbox-intermediate-variant:before{content:"\f1b54"}.mdi-checkbox-marked:before{content:"\f0132"}.mdi-checkbox-marked-circle:before{content:"\f0133"}.mdi-checkbox-marked-circle-auto-outline:before{content:"\f1c26"}.mdi-checkbox-marked-circle-minus-outline:before{content:"\f1c27"}.mdi-checkbox-marked-circle-outline:before{content:"\f0134"}.mdi-checkbox-marked-circle-plus-outline:before{content:"\f1927"}.mdi-checkbox-marked-outline:before{content:"\f0135"}.mdi-checkbox-multiple-blank:before{content:"\f0136"}.mdi-checkbox-multiple-blank-circle:before{content:"\f063b"}.mdi-checkbox-multiple-blank-circle-outline:before{content:"\f063c"}.mdi-checkbox-multiple-blank-outline:before{content:"\f0137"}.mdi-checkbox-multiple-marked:before{content:"\f0138"}.mdi-checkbox-multiple-marked-circle:before{content:"\f063d"}.mdi-checkbox-multiple-marked-circle-outline:before{content:"\f063e"}.mdi-checkbox-multiple-marked-outline:before{content:"\f0139"}.mdi-checkbox-multiple-outline:before{content:"\f0c51"}.mdi-checkbox-outline:before{content:"\f0c52"}.mdi-checkerboard:before{content:"\f013a"}.mdi-checkerboard-minus:before{content:"\f1202"}.mdi-checkerboard-plus:before{content:"\f1201"}.mdi-checkerboard-remove:before{content:"\f1203"}.mdi-cheese:before{content:"\f12b9"}.mdi-cheese-off:before{content:"\f13ee"}.mdi-chef-hat:before{content:"\f0b7c"}.mdi-chemical-weapon:before{content:"\f013b"}.mdi-chess-bishop:before{content:"\f085c"}.mdi-chess-king:before{content:"\f0857"}.mdi-chess-knight:before{content:"\f0858"}.mdi-chess-pawn:before{content:"\f0859"}.mdi-chess-queen:before{content:"\f085a"}.mdi-chess-rook:before{content:"\f085b"}.mdi-chevron-double-down:before{content:"\f013c"}.mdi-chevron-double-left:before{content:"\f013d"}.mdi-chevron-double-right:before{content:"\f013e"}.mdi-chevron-double-up:before{content:"\f013f"}.mdi-chevron-down:before{content:"\f0140"}.mdi-chevron-down-box:before{content:"\f09d6"}.mdi-chevron-down-box-outline:before{content:"\f09d7"}.mdi-chevron-down-circle:before{content:"\f0b26"}.mdi-chevron-down-circle-outline:before{content:"\f0b27"}.mdi-chevron-left:before{content:"\f0141"}.mdi-chevron-left-box:before{content:"\f09d8"}.mdi-chevron-left-box-outline:before{content:"\f09d9"}.mdi-chevron-left-circle:before{content:"\f0b28"}.mdi-chevron-left-circle-outline:before{content:"\f0b29"}.mdi-chevron-right:before{content:"\f0142"}.mdi-chevron-right-box:before{content:"\f09da"}.mdi-chevron-right-box-outline:before{content:"\f09db"}.mdi-chevron-right-circle:before{content:"\f0b2a"}.mdi-chevron-right-circle-outline:before{content:"\f0b2b"}.mdi-chevron-triple-down:before{content:"\f0db9"}.mdi-chevron-triple-left:before{content:"\f0dba"}.mdi-chevron-triple-right:before{content:"\f0dbb"}.mdi-chevron-triple-up:before{content:"\f0dbc"}.mdi-chevron-up:before{content:"\f0143"}.mdi-chevron-up-box:before{content:"\f09dc"}.mdi-chevron-up-box-outline:before{content:"\f09dd"}.mdi-chevron-up-circle:before{content:"\f0b2c"}.mdi-chevron-up-circle-outline:before{content:"\f0b2d"}.mdi-chili-alert:before{content:"\f17ea"}.mdi-chili-alert-outline:before{content:"\f17eb"}.mdi-chili-hot:before{content:"\f07b2"}.mdi-chili-hot-outline:before{content:"\f17ec"}.mdi-chili-medium:before{content:"\f07b3"}.mdi-chili-medium-outline:before{content:"\f17ed"}.mdi-chili-mild:before{content:"\f07b4"}.mdi-chili-mild-outline:before{content:"\f17ee"}.mdi-chili-off:before{content:"\f1467"}.mdi-chili-off-outline:before{content:"\f17ef"}.mdi-chip:before{content:"\f061a"}.mdi-church:before{content:"\f0144"}.mdi-church-outline:before{content:"\f1b02"}.mdi-cigar:before{content:"\f1189"}.mdi-cigar-off:before{content:"\f141b"}.mdi-circle:before{content:"\f0765"}.mdi-circle-box:before{content:"\f15dc"}.mdi-circle-box-outline:before{content:"\f15dd"}.mdi-circle-double:before{content:"\f0e95"}.mdi-circle-edit-outline:before{content:"\f08d5"}.mdi-circle-expand:before{content:"\f0e96"}.mdi-circle-half:before{content:"\f1395"}.mdi-circle-half-full:before{content:"\f1396"}.mdi-circle-medium:before{content:"\f09de"}.mdi-circle-multiple:before{content:"\f0b38"}.mdi-circle-multiple-outline:before{content:"\f0695"}.mdi-circle-off-outline:before{content:"\f10d3"}.mdi-circle-opacity:before{content:"\f1853"}.mdi-circle-outline:before{content:"\f0766"}.mdi-circle-slice-1:before{content:"\f0a9e"}.mdi-circle-slice-2:before{content:"\f0a9f"}.mdi-circle-slice-3:before{content:"\f0aa0"}.mdi-circle-slice-4:before{content:"\f0aa1"}.mdi-circle-slice-5:before{content:"\f0aa2"}.mdi-circle-slice-6:before{content:"\f0aa3"}.mdi-circle-slice-7:before{content:"\f0aa4"}.mdi-circle-slice-8:before{content:"\f0aa5"}.mdi-circle-small:before{content:"\f09df"}.mdi-circular-saw:before{content:"\f0e22"}.mdi-city:before{content:"\f0146"}.mdi-city-switch:before{content:"\f1c28"}.mdi-city-variant:before{content:"\f0a36"}.mdi-city-variant-outline:before{content:"\f0a37"}.mdi-clipboard:before{content:"\f0147"}.mdi-clipboard-account:before{content:"\f0148"}.mdi-clipboard-account-outline:before{content:"\f0c55"}.mdi-clipboard-alert:before{content:"\f0149"}.mdi-clipboard-alert-outline:before{content:"\f0cf7"}.mdi-clipboard-arrow-down:before{content:"\f014a"}.mdi-clipboard-arrow-down-outline:before{content:"\f0c56"}.mdi-clipboard-arrow-left:before{content:"\f014b"}.mdi-clipboard-arrow-left-outline:before{content:"\f0cf8"}.mdi-clipboard-arrow-right:before{content:"\f0cf9"}.mdi-clipboard-arrow-right-outline:before{content:"\f0cfa"}.mdi-clipboard-arrow-up:before{content:"\f0c57"}.mdi-clipboard-arrow-up-outline:before{content:"\f0c58"}.mdi-clipboard-check:before{content:"\f014e"}.mdi-clipboard-check-multiple:before{content:"\f1263"}.mdi-clipboard-check-multiple-outline:before{content:"\f1264"}.mdi-clipboard-check-outline:before{content:"\f08a8"}.mdi-clipboard-clock:before{content:"\f16e2"}.mdi-clipboard-clock-outline:before{content:"\f16e3"}.mdi-clipboard-edit:before{content:"\f14e5"}.mdi-clipboard-edit-outline:before{content:"\f14e6"}.mdi-clipboard-file:before{content:"\f1265"}.mdi-clipboard-file-outline:before{content:"\f1266"}.mdi-clipboard-flow:before{content:"\f06c8"}.mdi-clipboard-flow-outline:before{content:"\f1117"}.mdi-clipboard-list:before{content:"\f10d4"}.mdi-clipboard-list-outline:before{content:"\f10d5"}.mdi-clipboard-minus:before{content:"\f1618"}.mdi-clipboard-minus-outline:before{content:"\f1619"}.mdi-clipboard-multiple:before{content:"\f1267"}.mdi-clipboard-multiple-outline:before{content:"\f1268"}.mdi-clipboard-off:before{content:"\f161a"}.mdi-clipboard-off-outline:before{content:"\f161b"}.mdi-clipboard-outline:before{content:"\f014c"}.mdi-clipboard-play:before{content:"\f0c59"}.mdi-clipboard-play-multiple:before{content:"\f1269"}.mdi-clipboard-play-multiple-outline:before{content:"\f126a"}.mdi-clipboard-play-outline:before{content:"\f0c5a"}.mdi-clipboard-plus:before{content:"\f0751"}.mdi-clipboard-plus-outline:before{content:"\f131f"}.mdi-clipboard-pulse:before{content:"\f085d"}.mdi-clipboard-pulse-outline:before{content:"\f085e"}.mdi-clipboard-remove:before{content:"\f161c"}.mdi-clipboard-remove-outline:before{content:"\f161d"}.mdi-clipboard-search:before{content:"\f161e"}.mdi-clipboard-search-outline:before{content:"\f161f"}.mdi-clipboard-text:before{content:"\f014d"}.mdi-clipboard-text-clock:before{content:"\f18f9"}.mdi-clipboard-text-clock-outline:before{content:"\f18fa"}.mdi-clipboard-text-multiple:before{content:"\f126b"}.mdi-clipboard-text-multiple-outline:before{content:"\f126c"}.mdi-clipboard-text-off:before{content:"\f1620"}.mdi-clipboard-text-off-outline:before{content:"\f1621"}.mdi-clipboard-text-outline:before{content:"\f0a38"}.mdi-clipboard-text-play:before{content:"\f0c5b"}.mdi-clipboard-text-play-outline:before{content:"\f0c5c"}.mdi-clipboard-text-search:before{content:"\f1622"}.mdi-clipboard-text-search-outline:before{content:"\f1623"}.mdi-clippy:before{content:"\f014f"}.mdi-clock:before{content:"\f0954"}.mdi-clock-alert:before{content:"\f0955"}.mdi-clock-alert-outline:before{content:"\f05ce"}.mdi-clock-check:before{content:"\f0fa8"}.mdi-clock-check-outline:before{content:"\f0fa9"}.mdi-clock-digital:before{content:"\f0e97"}.mdi-clock-edit:before{content:"\f19ba"}.mdi-clock-edit-outline:before{content:"\f19bb"}.mdi-clock-end:before{content:"\f0151"}.mdi-clock-fast:before{content:"\f0152"}.mdi-clock-in:before{content:"\f0153"}.mdi-clock-minus:before{content:"\f1863"}.mdi-clock-minus-outline:before{content:"\f1864"}.mdi-clock-out:before{content:"\f0154"}.mdi-clock-outline:before{content:"\f0150"}.mdi-clock-plus:before{content:"\f1861"}.mdi-clock-plus-outline:before{content:"\f1862"}.mdi-clock-remove:before{content:"\f1865"}.mdi-clock-remove-outline:before{content:"\f1866"}.mdi-clock-star-four-points:before{content:"\f1c29"}.mdi-clock-star-four-points-outline:before{content:"\f1c2a"}.mdi-clock-start:before{content:"\f0155"}.mdi-clock-time-eight:before{content:"\f1446"}.mdi-clock-time-eight-outline:before{content:"\f1452"}.mdi-clock-time-eleven:before{content:"\f1449"}.mdi-clock-time-eleven-outline:before{content:"\f1455"}.mdi-clock-time-five:before{content:"\f1443"}.mdi-clock-time-five-outline:before{content:"\f144f"}.mdi-clock-time-four:before{content:"\f1442"}.mdi-clock-time-four-outline:before{content:"\f144e"}.mdi-clock-time-nine:before{content:"\f1447"}.mdi-clock-time-nine-outline:before{content:"\f1453"}.mdi-clock-time-one:before{content:"\f143f"}.mdi-clock-time-one-outline:before{content:"\f144b"}.mdi-clock-time-seven:before{content:"\f1445"}.mdi-clock-time-seven-outline:before{content:"\f1451"}.mdi-clock-time-six:before{content:"\f1444"}.mdi-clock-time-six-outline:before{content:"\f1450"}.mdi-clock-time-ten:before{content:"\f1448"}.mdi-clock-time-ten-outline:before{content:"\f1454"}.mdi-clock-time-three:before{content:"\f1441"}.mdi-clock-time-three-outline:before{content:"\f144d"}.mdi-clock-time-twelve:before{content:"\f144a"}.mdi-clock-time-twelve-outline:before{content:"\f1456"}.mdi-clock-time-two:before{content:"\f1440"}.mdi-clock-time-two-outline:before{content:"\f144c"}.mdi-close:before{content:"\f0156"}.mdi-close-box:before{content:"\f0157"}.mdi-close-box-multiple:before{content:"\f0c5d"}.mdi-close-box-multiple-outline:before{content:"\f0c5e"}.mdi-close-box-outline:before{content:"\f0158"}.mdi-close-circle:before{content:"\f0159"}.mdi-close-circle-multiple:before{content:"\f062a"}.mdi-close-circle-multiple-outline:before{content:"\f0883"}.mdi-close-circle-outline:before{content:"\f015a"}.mdi-close-network:before{content:"\f015b"}.mdi-close-network-outline:before{content:"\f0c5f"}.mdi-close-octagon:before{content:"\f015c"}.mdi-close-octagon-outline:before{content:"\f015d"}.mdi-close-outline:before{content:"\f06c9"}.mdi-close-thick:before{content:"\f1398"}.mdi-closed-caption:before{content:"\f015e"}.mdi-closed-caption-outline:before{content:"\f0dbd"}.mdi-cloud:before{content:"\f015f"}.mdi-cloud-alert:before{content:"\f09e0"}.mdi-cloud-alert-outline:before{content:"\f1be0"}.mdi-cloud-arrow-down:before{content:"\f1be1"}.mdi-cloud-arrow-down-outline:before{content:"\f1be2"}.mdi-cloud-arrow-left:before{content:"\f1be3"}.mdi-cloud-arrow-left-outline:before{content:"\f1be4"}.mdi-cloud-arrow-right:before{content:"\f1be5"}.mdi-cloud-arrow-right-outline:before{content:"\f1be6"}.mdi-cloud-arrow-up:before{content:"\f1be7"}.mdi-cloud-arrow-up-outline:before{content:"\f1be8"}.mdi-cloud-braces:before{content:"\f07b5"}.mdi-cloud-cancel:before{content:"\f1be9"}.mdi-cloud-cancel-outline:before{content:"\f1bea"}.mdi-cloud-check:before{content:"\f1beb"}.mdi-cloud-check-outline:before{content:"\f1bec"}.mdi-cloud-check-variant:before{content:"\f0160"}.mdi-cloud-check-variant-outline:before{content:"\f12cc"}.mdi-cloud-circle:before{content:"\f0161"}.mdi-cloud-circle-outline:before{content:"\f1bed"}.mdi-cloud-clock:before{content:"\f1bee"}.mdi-cloud-clock-outline:before{content:"\f1bef"}.mdi-cloud-cog:before{content:"\f1bf0"}.mdi-cloud-cog-outline:before{content:"\f1bf1"}.mdi-cloud-download:before{content:"\f0162"}.mdi-cloud-download-outline:before{content:"\f0b7d"}.mdi-cloud-key:before{content:"\f1ca1"}.mdi-cloud-key-outline:before{content:"\f1ca2"}.mdi-cloud-lock:before{content:"\f11f1"}.mdi-cloud-lock-open:before{content:"\f1bf2"}.mdi-cloud-lock-open-outline:before{content:"\f1bf3"}.mdi-cloud-lock-outline:before{content:"\f11f2"}.mdi-cloud-minus:before{content:"\f1bf4"}.mdi-cloud-minus-outline:before{content:"\f1bf5"}.mdi-cloud-off:before{content:"\f1bf6"}.mdi-cloud-off-outline:before{content:"\f0164"}.mdi-cloud-outline:before{content:"\f0163"}.mdi-cloud-percent:before{content:"\f1a35"}.mdi-cloud-percent-outline:before{content:"\f1a36"}.mdi-cloud-plus:before{content:"\f1bf7"}.mdi-cloud-plus-outline:before{content:"\f1bf8"}.mdi-cloud-print:before{content:"\f0165"}.mdi-cloud-print-outline:before{content:"\f0166"}.mdi-cloud-question:before{content:"\f0a39"}.mdi-cloud-question-outline:before{content:"\f1bf9"}.mdi-cloud-refresh:before{content:"\f1bfa"}.mdi-cloud-refresh-outline:before{content:"\f1bfb"}.mdi-cloud-refresh-variant:before{content:"\f052a"}.mdi-cloud-refresh-variant-outline:before{content:"\f1bfc"}.mdi-cloud-remove:before{content:"\f1bfd"}.mdi-cloud-remove-outline:before{content:"\f1bfe"}.mdi-cloud-search:before{content:"\f0956"}.mdi-cloud-search-outline:before{content:"\f0957"}.mdi-cloud-sync:before{content:"\f063f"}.mdi-cloud-sync-outline:before{content:"\f12d6"}.mdi-cloud-tags:before{content:"\f07b6"}.mdi-cloud-upload:before{content:"\f0167"}.mdi-cloud-upload-outline:before{content:"\f0b7e"}.mdi-clouds:before{content:"\f1b95"}.mdi-clover:before{content:"\f0816"}.mdi-clover-outline:before{content:"\f1c62"}.mdi-coach-lamp:before{content:"\f1020"}.mdi-coach-lamp-variant:before{content:"\f1a37"}.mdi-coat-rack:before{content:"\f109e"}.mdi-code-array:before{content:"\f0168"}.mdi-code-block-braces:before{content:"\f1c83"}.mdi-code-block-brackets:before{content:"\f1c84"}.mdi-code-block-parentheses:before{content:"\f1c85"}.mdi-code-block-tags:before{content:"\f1c86"}.mdi-code-braces:before{content:"\f0169"}.mdi-code-braces-box:before{content:"\f10d6"}.mdi-code-brackets:before{content:"\f016a"}.mdi-code-equal:before{content:"\f016b"}.mdi-code-greater-than:before{content:"\f016c"}.mdi-code-greater-than-or-equal:before{content:"\f016d"}.mdi-code-json:before{content:"\f0626"}.mdi-code-less-than:before{content:"\f016e"}.mdi-code-less-than-or-equal:before{content:"\f016f"}.mdi-code-not-equal:before{content:"\f0170"}.mdi-code-not-equal-variant:before{content:"\f0171"}.mdi-code-parentheses:before{content:"\f0172"}.mdi-code-parentheses-box:before{content:"\f10d7"}.mdi-code-string:before{content:"\f0173"}.mdi-code-tags:before{content:"\f0174"}.mdi-code-tags-check:before{content:"\f0694"}.mdi-codepen:before{content:"\f0175"}.mdi-coffee:before{content:"\f0176"}.mdi-coffee-maker:before{content:"\f109f"}.mdi-coffee-maker-check:before{content:"\f1931"}.mdi-coffee-maker-check-outline:before{content:"\f1932"}.mdi-coffee-maker-outline:before{content:"\f181b"}.mdi-coffee-off:before{content:"\f0faa"}.mdi-coffee-off-outline:before{content:"\f0fab"}.mdi-coffee-outline:before{content:"\f06ca"}.mdi-coffee-to-go:before{content:"\f0177"}.mdi-coffee-to-go-outline:before{content:"\f130e"}.mdi-coffin:before{content:"\f0b7f"}.mdi-cog:before{content:"\f0493"}.mdi-cog-box:before{content:"\f0494"}.mdi-cog-clockwise:before{content:"\f11dd"}.mdi-cog-counterclockwise:before{content:"\f11de"}.mdi-cog-off:before{content:"\f13ce"}.mdi-cog-off-outline:before{content:"\f13cf"}.mdi-cog-outline:before{content:"\f08bb"}.mdi-cog-pause:before{content:"\f1933"}.mdi-cog-pause-outline:before{content:"\f1934"}.mdi-cog-play:before{content:"\f1935"}.mdi-cog-play-outline:before{content:"\f1936"}.mdi-cog-refresh:before{content:"\f145e"}.mdi-cog-refresh-outline:before{content:"\f145f"}.mdi-cog-stop:before{content:"\f1937"}.mdi-cog-stop-outline:before{content:"\f1938"}.mdi-cog-sync:before{content:"\f1460"}.mdi-cog-sync-outline:before{content:"\f1461"}.mdi-cog-transfer:before{content:"\f105b"}.mdi-cog-transfer-outline:before{content:"\f105c"}.mdi-cogs:before{content:"\f08d6"}.mdi-collage:before{content:"\f0640"}.mdi-collapse-all:before{content:"\f0aa6"}.mdi-collapse-all-outline:before{content:"\f0aa7"}.mdi-color-helper:before{content:"\f0179"}.mdi-comma:before{content:"\f0e23"}.mdi-comma-box:before{content:"\f0e2b"}.mdi-comma-box-outline:before{content:"\f0e24"}.mdi-comma-circle:before{content:"\f0e25"}.mdi-comma-circle-outline:before{content:"\f0e26"}.mdi-comment:before{content:"\f017a"}.mdi-comment-account:before{content:"\f017b"}.mdi-comment-account-outline:before{content:"\f017c"}.mdi-comment-alert:before{content:"\f017d"}.mdi-comment-alert-outline:before{content:"\f017e"}.mdi-comment-arrow-left:before{content:"\f09e1"}.mdi-comment-arrow-left-outline:before{content:"\f09e2"}.mdi-comment-arrow-right:before{content:"\f09e3"}.mdi-comment-arrow-right-outline:before{content:"\f09e4"}.mdi-comment-bookmark:before{content:"\f15ae"}.mdi-comment-bookmark-outline:before{content:"\f15af"}.mdi-comment-check:before{content:"\f017f"}.mdi-comment-check-outline:before{content:"\f0180"}.mdi-comment-edit:before{content:"\f11bf"}.mdi-comment-edit-outline:before{content:"\f12c4"}.mdi-comment-eye:before{content:"\f0a3a"}.mdi-comment-eye-outline:before{content:"\f0a3b"}.mdi-comment-flash:before{content:"\f15b0"}.mdi-comment-flash-outline:before{content:"\f15b1"}.mdi-comment-minus:before{content:"\f15df"}.mdi-comment-minus-outline:before{content:"\f15e0"}.mdi-comment-multiple:before{content:"\f085f"}.mdi-comment-multiple-outline:before{content:"\f0181"}.mdi-comment-off:before{content:"\f15e1"}.mdi-comment-off-outline:before{content:"\f15e2"}.mdi-comment-outline:before{content:"\f0182"}.mdi-comment-plus:before{content:"\f09e5"}.mdi-comment-plus-outline:before{content:"\f0183"}.mdi-comment-processing:before{content:"\f0184"}.mdi-comment-processing-outline:before{content:"\f0185"}.mdi-comment-question:before{content:"\f0817"}.mdi-comment-question-outline:before{content:"\f0186"}.mdi-comment-quote:before{content:"\f1021"}.mdi-comment-quote-outline:before{content:"\f1022"}.mdi-comment-remove:before{content:"\f05de"}.mdi-comment-remove-outline:before{content:"\f0187"}.mdi-comment-search:before{content:"\f0a3c"}.mdi-comment-search-outline:before{content:"\f0a3d"}.mdi-comment-text:before{content:"\f0188"}.mdi-comment-text-multiple:before{content:"\f0860"}.mdi-comment-text-multiple-outline:before{content:"\f0861"}.mdi-comment-text-outline:before{content:"\f0189"}.mdi-compare:before{content:"\f018a"}.mdi-compare-horizontal:before{content:"\f1492"}.mdi-compare-remove:before{content:"\f18b3"}.mdi-compare-vertical:before{content:"\f1493"}.mdi-compass:before{content:"\f018b"}.mdi-compass-off:before{content:"\f0b80"}.mdi-compass-off-outline:before{content:"\f0b81"}.mdi-compass-outline:before{content:"\f018c"}.mdi-compass-rose:before{content:"\f1382"}.mdi-compost:before{content:"\f1a38"}.mdi-cone:before{content:"\f194c"}.mdi-cone-off:before{content:"\f194d"}.mdi-connection:before{content:"\f1616"}.mdi-console:before{content:"\f018d"}.mdi-console-line:before{content:"\f07b7"}.mdi-console-network:before{content:"\f08a9"}.mdi-console-network-outline:before{content:"\f0c60"}.mdi-consolidate:before{content:"\f10d8"}.mdi-contactless-payment:before{content:"\f0d6a"}.mdi-contactless-payment-circle:before{content:"\f0321"}.mdi-contactless-payment-circle-outline:before{content:"\f0408"}.mdi-contacts:before{content:"\f06cb"}.mdi-contacts-outline:before{content:"\f05b8"}.mdi-contain:before{content:"\f0a3e"}.mdi-contain-end:before{content:"\f0a3f"}.mdi-contain-start:before{content:"\f0a40"}.mdi-content-copy:before{content:"\f018f"}.mdi-content-cut:before{content:"\f0190"}.mdi-content-duplicate:before{content:"\f0191"}.mdi-content-paste:before{content:"\f0192"}.mdi-content-save:before{content:"\f0193"}.mdi-content-save-alert:before{content:"\f0f42"}.mdi-content-save-alert-outline:before{content:"\f0f43"}.mdi-content-save-all:before{content:"\f0194"}.mdi-content-save-all-outline:before{content:"\f0f44"}.mdi-content-save-check:before{content:"\f18ea"}.mdi-content-save-check-outline:before{content:"\f18eb"}.mdi-content-save-cog:before{content:"\f145b"}.mdi-content-save-cog-outline:before{content:"\f145c"}.mdi-content-save-edit:before{content:"\f0cfb"}.mdi-content-save-edit-outline:before{content:"\f0cfc"}.mdi-content-save-minus:before{content:"\f1b43"}.mdi-content-save-minus-outline:before{content:"\f1b44"}.mdi-content-save-move:before{content:"\f0e27"}.mdi-content-save-move-outline:before{content:"\f0e28"}.mdi-content-save-off:before{content:"\f1643"}.mdi-content-save-off-outline:before{content:"\f1644"}.mdi-content-save-outline:before{content:"\f0818"}.mdi-content-save-plus:before{content:"\f1b41"}.mdi-content-save-plus-outline:before{content:"\f1b42"}.mdi-content-save-settings:before{content:"\f061b"}.mdi-content-save-settings-outline:before{content:"\f0b2e"}.mdi-contrast:before{content:"\f0195"}.mdi-contrast-box:before{content:"\f0196"}.mdi-contrast-circle:before{content:"\f0197"}.mdi-controller:before{content:"\f02b4"}.mdi-controller-classic:before{content:"\f0b82"}.mdi-controller-classic-outline:before{content:"\f0b83"}.mdi-controller-off:before{content:"\f02b5"}.mdi-cookie:before{content:"\f0198"}.mdi-cookie-alert:before{content:"\f16d0"}.mdi-cookie-alert-outline:before{content:"\f16d1"}.mdi-cookie-check:before{content:"\f16d2"}.mdi-cookie-check-outline:before{content:"\f16d3"}.mdi-cookie-clock:before{content:"\f16e4"}.mdi-cookie-clock-outline:before{content:"\f16e5"}.mdi-cookie-cog:before{content:"\f16d4"}.mdi-cookie-cog-outline:before{content:"\f16d5"}.mdi-cookie-edit:before{content:"\f16e6"}.mdi-cookie-edit-outline:before{content:"\f16e7"}.mdi-cookie-lock:before{content:"\f16e8"}.mdi-cookie-lock-outline:before{content:"\f16e9"}.mdi-cookie-minus:before{content:"\f16da"}.mdi-cookie-minus-outline:before{content:"\f16db"}.mdi-cookie-off:before{content:"\f16ea"}.mdi-cookie-off-outline:before{content:"\f16eb"}.mdi-cookie-outline:before{content:"\f16de"}.mdi-cookie-plus:before{content:"\f16d6"}.mdi-cookie-plus-outline:before{content:"\f16d7"}.mdi-cookie-refresh:before{content:"\f16ec"}.mdi-cookie-refresh-outline:before{content:"\f16ed"}.mdi-cookie-remove:before{content:"\f16d8"}.mdi-cookie-remove-outline:before{content:"\f16d9"}.mdi-cookie-settings:before{content:"\f16dc"}.mdi-cookie-settings-outline:before{content:"\f16dd"}.mdi-coolant-temperature:before{content:"\f03c8"}.mdi-copyleft:before{content:"\f1939"}.mdi-copyright:before{content:"\f05e6"}.mdi-cordova:before{content:"\f0958"}.mdi-corn:before{content:"\f07b8"}.mdi-corn-off:before{content:"\f13ef"}.mdi-cosine-wave:before{content:"\f1479"}.mdi-counter:before{content:"\f0199"}.mdi-countertop:before{content:"\f181c"}.mdi-countertop-outline:before{content:"\f181d"}.mdi-cow:before{content:"\f019a"}.mdi-cow-off:before{content:"\f18fc"}.mdi-cpu-32-bit:before{content:"\f0edf"}.mdi-cpu-64-bit:before{content:"\f0ee0"}.mdi-cradle:before{content:"\f198b"}.mdi-cradle-outline:before{content:"\f1991"}.mdi-crane:before{content:"\f0862"}.mdi-creation:before{content:"\f0674"}.mdi-creation-outline:before{content:"\f1c2b"}.mdi-creative-commons:before{content:"\f0d6b"}.mdi-credit-card:before{content:"\f0fef"}.mdi-credit-card-check:before{content:"\f13d0"}.mdi-credit-card-check-outline:before{content:"\f13d1"}.mdi-credit-card-chip:before{content:"\f190f"}.mdi-credit-card-chip-outline:before{content:"\f1910"}.mdi-credit-card-clock:before{content:"\f0ee1"}.mdi-credit-card-clock-outline:before{content:"\f0ee2"}.mdi-credit-card-edit:before{content:"\f17d7"}.mdi-credit-card-edit-outline:before{content:"\f17d8"}.mdi-credit-card-fast:before{content:"\f1911"}.mdi-credit-card-fast-outline:before{content:"\f1912"}.mdi-credit-card-lock:before{content:"\f18e7"}.mdi-credit-card-lock-outline:before{content:"\f18e8"}.mdi-credit-card-marker:before{content:"\f06a8"}.mdi-credit-card-marker-outline:before{content:"\f0dbe"}.mdi-credit-card-minus:before{content:"\f0fac"}.mdi-credit-card-minus-outline:before{content:"\f0fad"}.mdi-credit-card-multiple:before{content:"\f0ff0"}.mdi-credit-card-multiple-outline:before{content:"\f019c"}.mdi-credit-card-off:before{content:"\f0ff1"}.mdi-credit-card-off-outline:before{content:"\f05e4"}.mdi-credit-card-outline:before{content:"\f019b"}.mdi-credit-card-plus:before{content:"\f0ff2"}.mdi-credit-card-plus-outline:before{content:"\f0676"}.mdi-credit-card-refresh:before{content:"\f1645"}.mdi-credit-card-refresh-outline:before{content:"\f1646"}.mdi-credit-card-refund:before{content:"\f0ff3"}.mdi-credit-card-refund-outline:before{content:"\f0aa8"}.mdi-credit-card-remove:before{content:"\f0fae"}.mdi-credit-card-remove-outline:before{content:"\f0faf"}.mdi-credit-card-scan:before{content:"\f0ff4"}.mdi-credit-card-scan-outline:before{content:"\f019d"}.mdi-credit-card-search:before{content:"\f1647"}.mdi-credit-card-search-outline:before{content:"\f1648"}.mdi-credit-card-settings:before{content:"\f0ff5"}.mdi-credit-card-settings-outline:before{content:"\f08d7"}.mdi-credit-card-sync:before{content:"\f1649"}.mdi-credit-card-sync-outline:before{content:"\f164a"}.mdi-credit-card-wireless:before{content:"\f0802"}.mdi-credit-card-wireless-off:before{content:"\f057a"}.mdi-credit-card-wireless-off-outline:before{content:"\f057b"}.mdi-credit-card-wireless-outline:before{content:"\f0d6c"}.mdi-cricket:before{content:"\f0d6d"}.mdi-crop:before{content:"\f019e"}.mdi-crop-free:before{content:"\f019f"}.mdi-crop-landscape:before{content:"\f01a0"}.mdi-crop-portrait:before{content:"\f01a1"}.mdi-crop-rotate:before{content:"\f0696"}.mdi-crop-square:before{content:"\f01a2"}.mdi-cross:before{content:"\f0953"}.mdi-cross-bolnisi:before{content:"\f0ced"}.mdi-cross-celtic:before{content:"\f0cf5"}.mdi-cross-outline:before{content:"\f0cf6"}.mdi-crosshairs:before{content:"\f01a3"}.mdi-crosshairs-gps:before{content:"\f01a4"}.mdi-crosshairs-off:before{content:"\f0f45"}.mdi-crosshairs-question:before{content:"\f1136"}.mdi-crowd:before{content:"\f1975"}.mdi-crown:before{content:"\f01a5"}.mdi-crown-circle:before{content:"\f17dc"}.mdi-crown-circle-outline:before{content:"\f17dd"}.mdi-crown-outline:before{content:"\f11d0"}.mdi-cryengine:before{content:"\f0959"}.mdi-crystal-ball:before{content:"\f0b2f"}.mdi-cube:before{content:"\f01a6"}.mdi-cube-off:before{content:"\f141c"}.mdi-cube-off-outline:before{content:"\f141d"}.mdi-cube-outline:before{content:"\f01a7"}.mdi-cube-scan:before{content:"\f0b84"}.mdi-cube-send:before{content:"\f01a8"}.mdi-cube-unfolded:before{content:"\f01a9"}.mdi-cup:before{content:"\f01aa"}.mdi-cup-off:before{content:"\f05e5"}.mdi-cup-off-outline:before{content:"\f137d"}.mdi-cup-outline:before{content:"\f130f"}.mdi-cup-water:before{content:"\f01ab"}.mdi-cupboard:before{content:"\f0f46"}.mdi-cupboard-outline:before{content:"\f0f47"}.mdi-cupcake:before{content:"\f095a"}.mdi-curling:before{content:"\f0863"}.mdi-currency-bdt:before{content:"\f0864"}.mdi-currency-brl:before{content:"\f0b85"}.mdi-currency-btc:before{content:"\f01ac"}.mdi-currency-cny:before{content:"\f07ba"}.mdi-currency-eth:before{content:"\f07bb"}.mdi-currency-eur:before{content:"\f01ad"}.mdi-currency-eur-off:before{content:"\f1315"}.mdi-currency-fra:before{content:"\f1a39"}.mdi-currency-gbp:before{content:"\f01ae"}.mdi-currency-ils:before{content:"\f0c61"}.mdi-currency-inr:before{content:"\f01af"}.mdi-currency-jpy:before{content:"\f07bc"}.mdi-currency-krw:before{content:"\f07bd"}.mdi-currency-kzt:before{content:"\f0865"}.mdi-currency-mnt:before{content:"\f1512"}.mdi-currency-ngn:before{content:"\f01b0"}.mdi-currency-php:before{content:"\f09e6"}.mdi-currency-rial:before{content:"\f0e9c"}.mdi-currency-rub:before{content:"\f01b1"}.mdi-currency-rupee:before{content:"\f1976"}.mdi-currency-sign:before{content:"\f07be"}.mdi-currency-thb:before{content:"\f1c05"}.mdi-currency-try:before{content:"\f01b2"}.mdi-currency-twd:before{content:"\f07bf"}.mdi-currency-uah:before{content:"\f1b9b"}.mdi-currency-usd:before{content:"\f01c1"}.mdi-currency-usd-off:before{content:"\f067a"}.mdi-current-ac:before{content:"\f1480"}.mdi-current-dc:before{content:"\f095c"}.mdi-cursor-default:before{content:"\f01c0"}.mdi-cursor-default-click:before{content:"\f0cfd"}.mdi-cursor-default-click-outline:before{content:"\f0cfe"}.mdi-cursor-default-gesture:before{content:"\f1127"}.mdi-cursor-default-gesture-outline:before{content:"\f1128"}.mdi-cursor-default-outline:before{content:"\f01bf"}.mdi-cursor-move:before{content:"\f01be"}.mdi-cursor-pointer:before{content:"\f01bd"}.mdi-cursor-text:before{content:"\f05e7"}.mdi-curtains:before{content:"\f1846"}.mdi-curtains-closed:before{content:"\f1847"}.mdi-cylinder:before{content:"\f194e"}.mdi-cylinder-off:before{content:"\f194f"}.mdi-dance-ballroom:before{content:"\f15fb"}.mdi-dance-pole:before{content:"\f1578"}.mdi-data-matrix:before{content:"\f153c"}.mdi-data-matrix-edit:before{content:"\f153d"}.mdi-data-matrix-minus:before{content:"\f153e"}.mdi-data-matrix-plus:before{content:"\f153f"}.mdi-data-matrix-remove:before{content:"\f1540"}.mdi-data-matrix-scan:before{content:"\f1541"}.mdi-database:before{content:"\f01bc"}.mdi-database-alert:before{content:"\f163a"}.mdi-database-alert-outline:before{content:"\f1624"}.mdi-database-arrow-down:before{content:"\f163b"}.mdi-database-arrow-down-outline:before{content:"\f1625"}.mdi-database-arrow-left:before{content:"\f163c"}.mdi-database-arrow-left-outline:before{content:"\f1626"}.mdi-database-arrow-right:before{content:"\f163d"}.mdi-database-arrow-right-outline:before{content:"\f1627"}.mdi-database-arrow-up:before{content:"\f163e"}.mdi-database-arrow-up-outline:before{content:"\f1628"}.mdi-database-check:before{content:"\f0aa9"}.mdi-database-check-outline:before{content:"\f1629"}.mdi-database-clock:before{content:"\f163f"}.mdi-database-clock-outline:before{content:"\f162a"}.mdi-database-cog:before{content:"\f164b"}.mdi-database-cog-outline:before{content:"\f164c"}.mdi-database-edit:before{content:"\f0b86"}.mdi-database-edit-outline:before{content:"\f162b"}.mdi-database-export:before{content:"\f095e"}.mdi-database-export-outline:before{content:"\f162c"}.mdi-database-eye:before{content:"\f191f"}.mdi-database-eye-off:before{content:"\f1920"}.mdi-database-eye-off-outline:before{content:"\f1921"}.mdi-database-eye-outline:before{content:"\f1922"}.mdi-database-import:before{content:"\f095d"}.mdi-database-import-outline:before{content:"\f162d"}.mdi-database-lock:before{content:"\f0aaa"}.mdi-database-lock-outline:before{content:"\f162e"}.mdi-database-marker:before{content:"\f12f6"}.mdi-database-marker-outline:before{content:"\f162f"}.mdi-database-minus:before{content:"\f01bb"}.mdi-database-minus-outline:before{content:"\f1630"}.mdi-database-off:before{content:"\f1640"}.mdi-database-off-outline:before{content:"\f1631"}.mdi-database-outline:before{content:"\f1632"}.mdi-database-plus:before{content:"\f01ba"}.mdi-database-plus-outline:before{content:"\f1633"}.mdi-database-refresh:before{content:"\f05c2"}.mdi-database-refresh-outline:before{content:"\f1634"}.mdi-database-remove:before{content:"\f0d00"}.mdi-database-remove-outline:before{content:"\f1635"}.mdi-database-search:before{content:"\f0866"}.mdi-database-search-outline:before{content:"\f1636"}.mdi-database-settings:before{content:"\f0d01"}.mdi-database-settings-outline:before{content:"\f1637"}.mdi-database-sync:before{content:"\f0cff"}.mdi-database-sync-outline:before{content:"\f1638"}.mdi-death-star:before{content:"\f08d8"}.mdi-death-star-variant:before{content:"\f08d9"}.mdi-deathly-hallows:before{content:"\f0b87"}.mdi-debian:before{content:"\f08da"}.mdi-debug-step-into:before{content:"\f01b9"}.mdi-debug-step-out:before{content:"\f01b8"}.mdi-debug-step-over:before{content:"\f01b7"}.mdi-decagram:before{content:"\f076c"}.mdi-decagram-outline:before{content:"\f076d"}.mdi-decimal:before{content:"\f10a1"}.mdi-decimal-comma:before{content:"\f10a2"}.mdi-decimal-comma-decrease:before{content:"\f10a3"}.mdi-decimal-comma-increase:before{content:"\f10a4"}.mdi-decimal-decrease:before{content:"\f01b6"}.mdi-decimal-increase:before{content:"\f01b5"}.mdi-delete:before{content:"\f01b4"}.mdi-delete-alert:before{content:"\f10a5"}.mdi-delete-alert-outline:before{content:"\f10a6"}.mdi-delete-circle:before{content:"\f0683"}.mdi-delete-circle-outline:before{content:"\f0b88"}.mdi-delete-clock:before{content:"\f1556"}.mdi-delete-clock-outline:before{content:"\f1557"}.mdi-delete-empty:before{content:"\f06cc"}.mdi-delete-empty-outline:before{content:"\f0e9d"}.mdi-delete-forever:before{content:"\f05e8"}.mdi-delete-forever-outline:before{content:"\f0b89"}.mdi-delete-off:before{content:"\f10a7"}.mdi-delete-off-outline:before{content:"\f10a8"}.mdi-delete-outline:before{content:"\f09e7"}.mdi-delete-restore:before{content:"\f0819"}.mdi-delete-sweep:before{content:"\f05e9"}.mdi-delete-sweep-outline:before{content:"\f0c62"}.mdi-delete-variant:before{content:"\f01b3"}.mdi-delta:before{content:"\f01c2"}.mdi-desk:before{content:"\f1239"}.mdi-desk-lamp:before{content:"\f095f"}.mdi-desk-lamp-off:before{content:"\f1b1f"}.mdi-desk-lamp-on:before{content:"\f1b20"}.mdi-deskphone:before{content:"\f01c3"}.mdi-desktop-classic:before{content:"\f07c0"}.mdi-desktop-tower:before{content:"\f01c5"}.mdi-desktop-tower-monitor:before{content:"\f0aab"}.mdi-details:before{content:"\f01c6"}.mdi-dev-to:before{content:"\f0d6e"}.mdi-developer-board:before{content:"\f0697"}.mdi-deviantart:before{content:"\f01c7"}.mdi-devices:before{content:"\f0fb0"}.mdi-dharmachakra:before{content:"\f094b"}.mdi-diabetes:before{content:"\f1126"}.mdi-dialpad:before{content:"\f061c"}.mdi-diameter:before{content:"\f0c63"}.mdi-diameter-outline:before{content:"\f0c64"}.mdi-diameter-variant:before{content:"\f0c65"}.mdi-diamond:before{content:"\f0b8a"}.mdi-diamond-outline:before{content:"\f0b8b"}.mdi-diamond-stone:before{content:"\f01c8"}.mdi-diaper-outline:before{content:"\f1ccf"}.mdi-dice-1:before{content:"\f01ca"}.mdi-dice-1-outline:before{content:"\f114a"}.mdi-dice-2:before{content:"\f01cb"}.mdi-dice-2-outline:before{content:"\f114b"}.mdi-dice-3:before{content:"\f01cc"}.mdi-dice-3-outline:before{content:"\f114c"}.mdi-dice-4:before{content:"\f01cd"}.mdi-dice-4-outline:before{content:"\f114d"}.mdi-dice-5:before{content:"\f01ce"}.mdi-dice-5-outline:before{content:"\f114e"}.mdi-dice-6:before{content:"\f01cf"}.mdi-dice-6-outline:before{content:"\f114f"}.mdi-dice-d10:before{content:"\f1153"}.mdi-dice-d10-outline:before{content:"\f076f"}.mdi-dice-d12:before{content:"\f1154"}.mdi-dice-d12-outline:before{content:"\f0867"}.mdi-dice-d20:before{content:"\f1155"}.mdi-dice-d20-outline:before{content:"\f05ea"}.mdi-dice-d4:before{content:"\f1150"}.mdi-dice-d4-outline:before{content:"\f05eb"}.mdi-dice-d6:before{content:"\f1151"}.mdi-dice-d6-outline:before{content:"\f05ed"}.mdi-dice-d8:before{content:"\f1152"}.mdi-dice-d8-outline:before{content:"\f05ec"}.mdi-dice-multiple:before{content:"\f076e"}.mdi-dice-multiple-outline:before{content:"\f1156"}.mdi-digital-ocean:before{content:"\f1237"}.mdi-dip-switch:before{content:"\f07c1"}.mdi-directions:before{content:"\f01d0"}.mdi-directions-fork:before{content:"\f0641"}.mdi-disc:before{content:"\f05ee"}.mdi-disc-alert:before{content:"\f01d1"}.mdi-disc-player:before{content:"\f0960"}.mdi-dishwasher:before{content:"\f0aac"}.mdi-dishwasher-alert:before{content:"\f11b8"}.mdi-dishwasher-off:before{content:"\f11b9"}.mdi-disqus:before{content:"\f01d2"}.mdi-distribute-horizontal-center:before{content:"\f11c9"}.mdi-distribute-horizontal-left:before{content:"\f11c8"}.mdi-distribute-horizontal-right:before{content:"\f11ca"}.mdi-distribute-vertical-bottom:before{content:"\f11cb"}.mdi-distribute-vertical-center:before{content:"\f11cc"}.mdi-distribute-vertical-top:before{content:"\f11cd"}.mdi-diversify:before{content:"\f1877"}.mdi-diving:before{content:"\f1977"}.mdi-diving-flippers:before{content:"\f0dbf"}.mdi-diving-helmet:before{content:"\f0dc0"}.mdi-diving-scuba:before{content:"\f1b77"}.mdi-diving-scuba-flag:before{content:"\f0dc2"}.mdi-diving-scuba-mask:before{content:"\f0dc1"}.mdi-diving-scuba-tank:before{content:"\f0dc3"}.mdi-diving-scuba-tank-multiple:before{content:"\f0dc4"}.mdi-diving-snorkel:before{content:"\f0dc5"}.mdi-division:before{content:"\f01d4"}.mdi-division-box:before{content:"\f01d5"}.mdi-dlna:before{content:"\f0a41"}.mdi-dna:before{content:"\f0684"}.mdi-dns:before{content:"\f01d6"}.mdi-dns-outline:before{content:"\f0b8c"}.mdi-dock-bottom:before{content:"\f10a9"}.mdi-dock-left:before{content:"\f10aa"}.mdi-dock-right:before{content:"\f10ab"}.mdi-dock-top:before{content:"\f1513"}.mdi-dock-window:before{content:"\f10ac"}.mdi-docker:before{content:"\f0868"}.mdi-doctor:before{content:"\f0a42"}.mdi-dog:before{content:"\f0a43"}.mdi-dog-service:before{content:"\f0aad"}.mdi-dog-side:before{content:"\f0a44"}.mdi-dog-side-off:before{content:"\f16ee"}.mdi-dolby:before{content:"\f06b3"}.mdi-dolly:before{content:"\f0e9e"}.mdi-dolphin:before{content:"\f18b4"}.mdi-domain:before{content:"\f01d7"}.mdi-domain-off:before{content:"\f0d6f"}.mdi-domain-plus:before{content:"\f10ad"}.mdi-domain-remove:before{content:"\f10ae"}.mdi-domain-switch:before{content:"\f1c2c"}.mdi-dome-light:before{content:"\f141e"}.mdi-domino-mask:before{content:"\f1023"}.mdi-donkey:before{content:"\f07c2"}.mdi-door:before{content:"\f081a"}.mdi-door-closed:before{content:"\f081b"}.mdi-door-closed-cancel:before{content:"\f1c93"}.mdi-door-closed-lock:before{content:"\f10af"}.mdi-door-open:before{content:"\f081c"}.mdi-door-sliding:before{content:"\f181e"}.mdi-door-sliding-lock:before{content:"\f181f"}.mdi-door-sliding-open:before{content:"\f1820"}.mdi-doorbell:before{content:"\f12e6"}.mdi-doorbell-video:before{content:"\f0869"}.mdi-dot-net:before{content:"\f0aae"}.mdi-dots-circle:before{content:"\f1978"}.mdi-dots-grid:before{content:"\f15fc"}.mdi-dots-hexagon:before{content:"\f15ff"}.mdi-dots-horizontal:before{content:"\f01d8"}.mdi-dots-horizontal-circle:before{content:"\f07c3"}.mdi-dots-horizontal-circle-outline:before{content:"\f0b8d"}.mdi-dots-square:before{content:"\f15fd"}.mdi-dots-triangle:before{content:"\f15fe"}.mdi-dots-vertical:before{content:"\f01d9"}.mdi-dots-vertical-circle:before{content:"\f07c4"}.mdi-dots-vertical-circle-outline:before{content:"\f0b8e"}.mdi-download:before{content:"\f01da"}.mdi-download-box:before{content:"\f1462"}.mdi-download-box-outline:before{content:"\f1463"}.mdi-download-circle:before{content:"\f1464"}.mdi-download-circle-outline:before{content:"\f1465"}.mdi-download-lock:before{content:"\f1320"}.mdi-download-lock-outline:before{content:"\f1321"}.mdi-download-multiple:before{content:"\f09e9"}.mdi-download-multiple-outline:before{content:"\f1cd0"}.mdi-download-network:before{content:"\f06f4"}.mdi-download-network-outline:before{content:"\f0c66"}.mdi-download-off:before{content:"\f10b0"}.mdi-download-off-outline:before{content:"\f10b1"}.mdi-download-outline:before{content:"\f0b8f"}.mdi-drag:before{content:"\f01db"}.mdi-drag-horizontal:before{content:"\f01dc"}.mdi-drag-horizontal-variant:before{content:"\f12f0"}.mdi-drag-variant:before{content:"\f0b90"}.mdi-drag-vertical:before{content:"\f01dd"}.mdi-drag-vertical-variant:before{content:"\f12f1"}.mdi-drama-masks:before{content:"\f0d02"}.mdi-draw:before{content:"\f0f49"}.mdi-draw-pen:before{content:"\f19b9"}.mdi-drawing:before{content:"\f01de"}.mdi-drawing-box:before{content:"\f01df"}.mdi-dresser:before{content:"\f0f4a"}.mdi-dresser-outline:before{content:"\f0f4b"}.mdi-drone:before{content:"\f01e2"}.mdi-dropbox:before{content:"\f01e3"}.mdi-drupal:before{content:"\f01e4"}.mdi-duck:before{content:"\f01e5"}.mdi-dumbbell:before{content:"\f01e6"}.mdi-dump-truck:before{content:"\f0c67"}.mdi-ear-hearing:before{content:"\f07c5"}.mdi-ear-hearing-loop:before{content:"\f1aee"}.mdi-ear-hearing-off:before{content:"\f0a45"}.mdi-earbuds:before{content:"\f184f"}.mdi-earbuds-off:before{content:"\f1850"}.mdi-earbuds-off-outline:before{content:"\f1851"}.mdi-earbuds-outline:before{content:"\f1852"}.mdi-earth:before{content:"\f01e7"}.mdi-earth-arrow-down:before{content:"\f1c87"}.mdi-earth-arrow-left:before{content:"\f1c88"}.mdi-earth-arrow-right:before{content:"\f1311"}.mdi-earth-arrow-up:before{content:"\f1c89"}.mdi-earth-box:before{content:"\f06cd"}.mdi-earth-box-minus:before{content:"\f1407"}.mdi-earth-box-off:before{content:"\f06ce"}.mdi-earth-box-plus:before{content:"\f1406"}.mdi-earth-box-remove:before{content:"\f1408"}.mdi-earth-minus:before{content:"\f1404"}.mdi-earth-off:before{content:"\f01e8"}.mdi-earth-plus:before{content:"\f1403"}.mdi-earth-remove:before{content:"\f1405"}.mdi-egg:before{content:"\f0aaf"}.mdi-egg-easter:before{content:"\f0ab0"}.mdi-egg-fried:before{content:"\f184a"}.mdi-egg-off:before{content:"\f13f0"}.mdi-egg-off-outline:before{content:"\f13f1"}.mdi-egg-outline:before{content:"\f13f2"}.mdi-eiffel-tower:before{content:"\f156b"}.mdi-eight-track:before{content:"\f09ea"}.mdi-eject:before{content:"\f01ea"}.mdi-eject-circle:before{content:"\f1b23"}.mdi-eject-circle-outline:before{content:"\f1b24"}.mdi-eject-outline:before{content:"\f0b91"}.mdi-electric-switch:before{content:"\f0e9f"}.mdi-electric-switch-closed:before{content:"\f10d9"}.mdi-electron-framework:before{content:"\f1024"}.mdi-elephant:before{content:"\f07c6"}.mdi-elevation-decline:before{content:"\f01eb"}.mdi-elevation-rise:before{content:"\f01ec"}.mdi-elevator:before{content:"\f01ed"}.mdi-elevator-down:before{content:"\f12c2"}.mdi-elevator-passenger:before{content:"\f1381"}.mdi-elevator-passenger-off:before{content:"\f1979"}.mdi-elevator-passenger-off-outline:before{content:"\f197a"}.mdi-elevator-passenger-outline:before{content:"\f197b"}.mdi-elevator-up:before{content:"\f12c1"}.mdi-ellipse:before{content:"\f0ea0"}.mdi-ellipse-outline:before{content:"\f0ea1"}.mdi-email:before{content:"\f01ee"}.mdi-email-alert:before{content:"\f06cf"}.mdi-email-alert-outline:before{content:"\f0d42"}.mdi-email-arrow-left:before{content:"\f10da"}.mdi-email-arrow-left-outline:before{content:"\f10db"}.mdi-email-arrow-right:before{content:"\f10dc"}.mdi-email-arrow-right-outline:before{content:"\f10dd"}.mdi-email-box:before{content:"\f0d03"}.mdi-email-check:before{content:"\f0ab1"}.mdi-email-check-outline:before{content:"\f0ab2"}.mdi-email-edit:before{content:"\f0ee3"}.mdi-email-edit-outline:before{content:"\f0ee4"}.mdi-email-fast:before{content:"\f186f"}.mdi-email-fast-outline:before{content:"\f1870"}.mdi-email-heart-outline:before{content:"\f1c5b"}.mdi-email-lock:before{content:"\f01f1"}.mdi-email-lock-outline:before{content:"\f1b61"}.mdi-email-mark-as-unread:before{content:"\f0b92"}.mdi-email-minus:before{content:"\f0ee5"}.mdi-email-minus-outline:before{content:"\f0ee6"}.mdi-email-multiple:before{content:"\f0ee7"}.mdi-email-multiple-outline:before{content:"\f0ee8"}.mdi-email-newsletter:before{content:"\f0fb1"}.mdi-email-off:before{content:"\f13e3"}.mdi-email-off-outline:before{content:"\f13e4"}.mdi-email-open:before{content:"\f01ef"}.mdi-email-open-heart-outline:before{content:"\f1c5c"}.mdi-email-open-multiple:before{content:"\f0ee9"}.mdi-email-open-multiple-outline:before{content:"\f0eea"}.mdi-email-open-outline:before{content:"\f05ef"}.mdi-email-outline:before{content:"\f01f0"}.mdi-email-plus:before{content:"\f09eb"}.mdi-email-plus-outline:before{content:"\f09ec"}.mdi-email-remove:before{content:"\f1661"}.mdi-email-remove-outline:before{content:"\f1662"}.mdi-email-seal:before{content:"\f195b"}.mdi-email-seal-outline:before{content:"\f195c"}.mdi-email-search:before{content:"\f0961"}.mdi-email-search-outline:before{content:"\f0962"}.mdi-email-sync:before{content:"\f12c7"}.mdi-email-sync-outline:before{content:"\f12c8"}.mdi-email-variant:before{content:"\f05f0"}.mdi-ember:before{content:"\f0b30"}.mdi-emby:before{content:"\f06b4"}.mdi-emoticon:before{content:"\f0c68"}.mdi-emoticon-angry:before{content:"\f0c69"}.mdi-emoticon-angry-outline:before{content:"\f0c6a"}.mdi-emoticon-confused:before{content:"\f10de"}.mdi-emoticon-confused-outline:before{content:"\f10df"}.mdi-emoticon-cool:before{content:"\f0c6b"}.mdi-emoticon-cool-outline:before{content:"\f01f3"}.mdi-emoticon-cry:before{content:"\f0c6c"}.mdi-emoticon-cry-outline:before{content:"\f0c6d"}.mdi-emoticon-dead:before{content:"\f0c6e"}.mdi-emoticon-dead-outline:before{content:"\f069b"}.mdi-emoticon-devil:before{content:"\f0c6f"}.mdi-emoticon-devil-outline:before{content:"\f01f4"}.mdi-emoticon-excited:before{content:"\f0c70"}.mdi-emoticon-excited-outline:before{content:"\f069c"}.mdi-emoticon-frown:before{content:"\f0f4c"}.mdi-emoticon-frown-outline:before{content:"\f0f4d"}.mdi-emoticon-happy:before{content:"\f0c71"}.mdi-emoticon-happy-outline:before{content:"\f01f5"}.mdi-emoticon-kiss:before{content:"\f0c72"}.mdi-emoticon-kiss-outline:before{content:"\f0c73"}.mdi-emoticon-lol:before{content:"\f1214"}.mdi-emoticon-lol-outline:before{content:"\f1215"}.mdi-emoticon-minus:before{content:"\f1cb2"}.mdi-emoticon-minus-outline:before{content:"\f1cb3"}.mdi-emoticon-neutral:before{content:"\f0c74"}.mdi-emoticon-neutral-outline:before{content:"\f01f6"}.mdi-emoticon-outline:before{content:"\f01f2"}.mdi-emoticon-plus:before{content:"\f1cb4"}.mdi-emoticon-plus-outline:before{content:"\f1cb5"}.mdi-emoticon-poop:before{content:"\f01f7"}.mdi-emoticon-poop-outline:before{content:"\f0c75"}.mdi-emoticon-remove:before{content:"\f1cb6"}.mdi-emoticon-remove-outline:before{content:"\f1cb7"}.mdi-emoticon-sad:before{content:"\f0c76"}.mdi-emoticon-sad-outline:before{content:"\f01f8"}.mdi-emoticon-sick:before{content:"\f157c"}.mdi-emoticon-sick-outline:before{content:"\f157d"}.mdi-emoticon-tongue:before{content:"\f01f9"}.mdi-emoticon-tongue-outline:before{content:"\f0c77"}.mdi-emoticon-wink:before{content:"\f0c78"}.mdi-emoticon-wink-outline:before{content:"\f0c79"}.mdi-engine:before{content:"\f01fa"}.mdi-engine-off:before{content:"\f0a46"}.mdi-engine-off-outline:before{content:"\f0a47"}.mdi-engine-outline:before{content:"\f01fb"}.mdi-epsilon:before{content:"\f10e0"}.mdi-equal:before{content:"\f01fc"}.mdi-equal-box:before{content:"\f01fd"}.mdi-equalizer:before{content:"\f0ea2"}.mdi-equalizer-outline:before{content:"\f0ea3"}.mdi-eraser:before{content:"\f01fe"}.mdi-eraser-variant:before{content:"\f0642"}.mdi-escalator:before{content:"\f01ff"}.mdi-escalator-box:before{content:"\f1399"}.mdi-escalator-down:before{content:"\f12c0"}.mdi-escalator-up:before{content:"\f12bf"}.mdi-eslint:before{content:"\f0c7a"}.mdi-et:before{content:"\f0ab3"}.mdi-ethereum:before{content:"\f086a"}.mdi-ethernet:before{content:"\f0200"}.mdi-ethernet-cable:before{content:"\f0201"}.mdi-ethernet-cable-off:before{content:"\f0202"}.mdi-ethernet-off:before{content:"\f1cd1"}.mdi-ev-plug-ccs1:before{content:"\f1519"}.mdi-ev-plug-ccs2:before{content:"\f151a"}.mdi-ev-plug-chademo:before{content:"\f151b"}.mdi-ev-plug-tesla:before{content:"\f151c"}.mdi-ev-plug-type1:before{content:"\f151d"}.mdi-ev-plug-type2:before{content:"\f151e"}.mdi-ev-station:before{content:"\f05f1"}.mdi-evernote:before{content:"\f0204"}.mdi-excavator:before{content:"\f1025"}.mdi-exclamation:before{content:"\f0205"}.mdi-exclamation-thick:before{content:"\f1238"}.mdi-exit-run:before{content:"\f0a48"}.mdi-exit-to-app:before{content:"\f0206"}.mdi-expand-all:before{content:"\f0ab4"}.mdi-expand-all-outline:before{content:"\f0ab5"}.mdi-expansion-card:before{content:"\f08ae"}.mdi-expansion-card-variant:before{content:"\f0fb2"}.mdi-exponent:before{content:"\f0963"}.mdi-exponent-box:before{content:"\f0964"}.mdi-export:before{content:"\f0207"}.mdi-export-variant:before{content:"\f0b93"}.mdi-eye:before{content:"\f0208"}.mdi-eye-arrow-left:before{content:"\f18fd"}.mdi-eye-arrow-left-outline:before{content:"\f18fe"}.mdi-eye-arrow-right:before{content:"\f18ff"}.mdi-eye-arrow-right-outline:before{content:"\f1900"}.mdi-eye-check:before{content:"\f0d04"}.mdi-eye-check-outline:before{content:"\f0d05"}.mdi-eye-circle:before{content:"\f0b94"}.mdi-eye-circle-outline:before{content:"\f0b95"}.mdi-eye-closed:before{content:"\f1ca3"}.mdi-eye-lock:before{content:"\f1c06"}.mdi-eye-lock-open:before{content:"\f1c07"}.mdi-eye-lock-open-outline:before{content:"\f1c08"}.mdi-eye-lock-outline:before{content:"\f1c09"}.mdi-eye-minus:before{content:"\f1026"}.mdi-eye-minus-outline:before{content:"\f1027"}.mdi-eye-off:before{content:"\f0209"}.mdi-eye-off-outline:before{content:"\f06d1"}.mdi-eye-outline:before{content:"\f06d0"}.mdi-eye-plus:before{content:"\f086b"}.mdi-eye-plus-outline:before{content:"\f086c"}.mdi-eye-refresh:before{content:"\f197c"}.mdi-eye-refresh-outline:before{content:"\f197d"}.mdi-eye-remove:before{content:"\f15e3"}.mdi-eye-remove-outline:before{content:"\f15e4"}.mdi-eye-settings:before{content:"\f086d"}.mdi-eye-settings-outline:before{content:"\f086e"}.mdi-eyedropper:before{content:"\f020a"}.mdi-eyedropper-minus:before{content:"\f13dd"}.mdi-eyedropper-off:before{content:"\f13df"}.mdi-eyedropper-plus:before{content:"\f13dc"}.mdi-eyedropper-remove:before{content:"\f13de"}.mdi-eyedropper-variant:before{content:"\f020b"}.mdi-face-agent:before{content:"\f0d70"}.mdi-face-man:before{content:"\f0643"}.mdi-face-man-outline:before{content:"\f0b96"}.mdi-face-man-profile:before{content:"\f0644"}.mdi-face-man-shimmer:before{content:"\f15cc"}.mdi-face-man-shimmer-outline:before{content:"\f15cd"}.mdi-face-mask:before{content:"\f1586"}.mdi-face-mask-outline:before{content:"\f1587"}.mdi-face-recognition:before{content:"\f0c7b"}.mdi-face-woman:before{content:"\f1077"}.mdi-face-woman-outline:before{content:"\f1078"}.mdi-face-woman-profile:before{content:"\f1076"}.mdi-face-woman-shimmer:before{content:"\f15ce"}.mdi-face-woman-shimmer-outline:before{content:"\f15cf"}.mdi-facebook:before{content:"\f020c"}.mdi-facebook-gaming:before{content:"\f07dd"}.mdi-facebook-messenger:before{content:"\f020e"}.mdi-facebook-workplace:before{content:"\f0b31"}.mdi-factory:before{content:"\f020f"}.mdi-family-tree:before{content:"\f160e"}.mdi-fan:before{content:"\f0210"}.mdi-fan-alert:before{content:"\f146c"}.mdi-fan-auto:before{content:"\f171d"}.mdi-fan-chevron-down:before{content:"\f146d"}.mdi-fan-chevron-up:before{content:"\f146e"}.mdi-fan-clock:before{content:"\f1a3a"}.mdi-fan-minus:before{content:"\f1470"}.mdi-fan-off:before{content:"\f081d"}.mdi-fan-plus:before{content:"\f146f"}.mdi-fan-remove:before{content:"\f1471"}.mdi-fan-speed-1:before{content:"\f1472"}.mdi-fan-speed-2:before{content:"\f1473"}.mdi-fan-speed-3:before{content:"\f1474"}.mdi-fast-forward:before{content:"\f0211"}.mdi-fast-forward-10:before{content:"\f0d71"}.mdi-fast-forward-15:before{content:"\f193a"}.mdi-fast-forward-30:before{content:"\f0d06"}.mdi-fast-forward-45:before{content:"\f1b12"}.mdi-fast-forward-5:before{content:"\f11f8"}.mdi-fast-forward-60:before{content:"\f160b"}.mdi-fast-forward-outline:before{content:"\f06d2"}.mdi-faucet:before{content:"\f1b29"}.mdi-faucet-variant:before{content:"\f1b2a"}.mdi-fax:before{content:"\f0212"}.mdi-feather:before{content:"\f06d3"}.mdi-feature-search:before{content:"\f0a49"}.mdi-feature-search-outline:before{content:"\f0a4a"}.mdi-fedora:before{content:"\f08db"}.mdi-fence:before{content:"\f179a"}.mdi-fence-electric:before{content:"\f17f6"}.mdi-fencing:before{content:"\f14c1"}.mdi-ferris-wheel:before{content:"\f0ea4"}.mdi-ferry:before{content:"\f0213"}.mdi-file:before{content:"\f0214"}.mdi-file-account:before{content:"\f073b"}.mdi-file-account-outline:before{content:"\f1028"}.mdi-file-alert:before{content:"\f0a4b"}.mdi-file-alert-outline:before{content:"\f0a4c"}.mdi-file-arrow-left-right:before{content:"\f1a93"}.mdi-file-arrow-left-right-outline:before{content:"\f1a94"}.mdi-file-arrow-up-down:before{content:"\f1a95"}.mdi-file-arrow-up-down-outline:before{content:"\f1a96"}.mdi-file-cabinet:before{content:"\f0ab6"}.mdi-file-cad:before{content:"\f0eeb"}.mdi-file-cad-box:before{content:"\f0eec"}.mdi-file-cancel:before{content:"\f0dc6"}.mdi-file-cancel-outline:before{content:"\f0dc7"}.mdi-file-certificate:before{content:"\f1186"}.mdi-file-certificate-outline:before{content:"\f1187"}.mdi-file-chart:before{content:"\f0215"}.mdi-file-chart-check:before{content:"\f19c6"}.mdi-file-chart-check-outline:before{content:"\f19c7"}.mdi-file-chart-outline:before{content:"\f1029"}.mdi-file-check:before{content:"\f0216"}.mdi-file-check-outline:before{content:"\f0e29"}.mdi-file-clock:before{content:"\f12e1"}.mdi-file-clock-outline:before{content:"\f12e2"}.mdi-file-cloud:before{content:"\f0217"}.mdi-file-cloud-outline:before{content:"\f102a"}.mdi-file-code:before{content:"\f022e"}.mdi-file-code-outline:before{content:"\f102b"}.mdi-file-cog:before{content:"\f107b"}.mdi-file-cog-outline:before{content:"\f107c"}.mdi-file-compare:before{content:"\f08aa"}.mdi-file-delimited:before{content:"\f0218"}.mdi-file-delimited-outline:before{content:"\f0ea5"}.mdi-file-document:before{content:"\f0219"}.mdi-file-document-alert:before{content:"\f1a97"}.mdi-file-document-alert-outline:before{content:"\f1a98"}.mdi-file-document-arrow-right:before{content:"\f1c0f"}.mdi-file-document-arrow-right-outline:before{content:"\f1c10"}.mdi-file-document-check:before{content:"\f1a99"}.mdi-file-document-check-outline:before{content:"\f1a9a"}.mdi-file-document-edit:before{content:"\f0dc8"}.mdi-file-document-edit-outline:before{content:"\f0dc9"}.mdi-file-document-minus:before{content:"\f1a9b"}.mdi-file-document-minus-outline:before{content:"\f1a9c"}.mdi-file-document-multiple:before{content:"\f1517"}.mdi-file-document-multiple-outline:before{content:"\f1518"}.mdi-file-document-outline:before{content:"\f09ee"}.mdi-file-document-plus:before{content:"\f1a9d"}.mdi-file-document-plus-outline:before{content:"\f1a9e"}.mdi-file-document-refresh:before{content:"\f1c7a"}.mdi-file-document-refresh-outline:before{content:"\f1c7b"}.mdi-file-document-remove:before{content:"\f1a9f"}.mdi-file-document-remove-outline:before{content:"\f1aa0"}.mdi-file-download:before{content:"\f0965"}.mdi-file-download-outline:before{content:"\f0966"}.mdi-file-edit:before{content:"\f11e7"}.mdi-file-edit-outline:before{content:"\f11e8"}.mdi-file-excel:before{content:"\f021b"}.mdi-file-excel-box:before{content:"\f021c"}.mdi-file-excel-box-outline:before{content:"\f102c"}.mdi-file-excel-outline:before{content:"\f102d"}.mdi-file-export:before{content:"\f021d"}.mdi-file-export-outline:before{content:"\f102e"}.mdi-file-eye:before{content:"\f0dca"}.mdi-file-eye-outline:before{content:"\f0dcb"}.mdi-file-find:before{content:"\f021e"}.mdi-file-find-outline:before{content:"\f0b97"}.mdi-file-gif-box:before{content:"\f0d78"}.mdi-file-hidden:before{content:"\f0613"}.mdi-file-image:before{content:"\f021f"}.mdi-file-image-marker:before{content:"\f1772"}.mdi-file-image-marker-outline:before{content:"\f1773"}.mdi-file-image-minus:before{content:"\f193b"}.mdi-file-image-minus-outline:before{content:"\f193c"}.mdi-file-image-outline:before{content:"\f0eb0"}.mdi-file-image-plus:before{content:"\f193d"}.mdi-file-image-plus-outline:before{content:"\f193e"}.mdi-file-image-remove:before{content:"\f193f"}.mdi-file-image-remove-outline:before{content:"\f1940"}.mdi-file-import:before{content:"\f0220"}.mdi-file-import-outline:before{content:"\f102f"}.mdi-file-jpg-box:before{content:"\f0225"}.mdi-file-key:before{content:"\f1184"}.mdi-file-key-outline:before{content:"\f1185"}.mdi-file-link:before{content:"\f1177"}.mdi-file-link-outline:before{content:"\f1178"}.mdi-file-lock:before{content:"\f0221"}.mdi-file-lock-open:before{content:"\f19c8"}.mdi-file-lock-open-outline:before{content:"\f19c9"}.mdi-file-lock-outline:before{content:"\f1030"}.mdi-file-marker:before{content:"\f1774"}.mdi-file-marker-outline:before{content:"\f1775"}.mdi-file-minus:before{content:"\f1aa1"}.mdi-file-minus-outline:before{content:"\f1aa2"}.mdi-file-move:before{content:"\f0ab9"}.mdi-file-move-outline:before{content:"\f1031"}.mdi-file-multiple:before{content:"\f0222"}.mdi-file-multiple-outline:before{content:"\f1032"}.mdi-file-music:before{content:"\f0223"}.mdi-file-music-outline:before{content:"\f0e2a"}.mdi-file-outline:before{content:"\f0224"}.mdi-file-pdf-box:before{content:"\f0226"}.mdi-file-percent:before{content:"\f081e"}.mdi-file-percent-outline:before{content:"\f1033"}.mdi-file-phone:before{content:"\f1179"}.mdi-file-phone-outline:before{content:"\f117a"}.mdi-file-plus:before{content:"\f0752"}.mdi-file-plus-outline:before{content:"\f0eed"}.mdi-file-png-box:before{content:"\f0e2d"}.mdi-file-powerpoint:before{content:"\f0227"}.mdi-file-powerpoint-box:before{content:"\f0228"}.mdi-file-powerpoint-box-outline:before{content:"\f1034"}.mdi-file-powerpoint-outline:before{content:"\f1035"}.mdi-file-presentation-box:before{content:"\f0229"}.mdi-file-question:before{content:"\f086f"}.mdi-file-question-outline:before{content:"\f1036"}.mdi-file-refresh:before{content:"\f0918"}.mdi-file-refresh-outline:before{content:"\f0541"}.mdi-file-remove:before{content:"\f0b98"}.mdi-file-remove-outline:before{content:"\f1037"}.mdi-file-replace:before{content:"\f0b32"}.mdi-file-replace-outline:before{content:"\f0b33"}.mdi-file-restore:before{content:"\f0670"}.mdi-file-restore-outline:before{content:"\f1038"}.mdi-file-rotate-left:before{content:"\f1a3b"}.mdi-file-rotate-left-outline:before{content:"\f1a3c"}.mdi-file-rotate-right:before{content:"\f1a3d"}.mdi-file-rotate-right-outline:before{content:"\f1a3e"}.mdi-file-search:before{content:"\f0c7c"}.mdi-file-search-outline:before{content:"\f0c7d"}.mdi-file-send:before{content:"\f022a"}.mdi-file-send-outline:before{content:"\f1039"}.mdi-file-settings:before{content:"\f1079"}.mdi-file-settings-outline:before{content:"\f107a"}.mdi-file-sign:before{content:"\f19c3"}.mdi-file-star:before{content:"\f103a"}.mdi-file-star-four-points:before{content:"\f1c2d"}.mdi-file-star-four-points-outline:before{content:"\f1c2e"}.mdi-file-star-outline:before{content:"\f103b"}.mdi-file-swap:before{content:"\f0fb4"}.mdi-file-swap-outline:before{content:"\f0fb5"}.mdi-file-sync:before{content:"\f1216"}.mdi-file-sync-outline:before{content:"\f1217"}.mdi-file-table:before{content:"\f0c7e"}.mdi-file-table-box:before{content:"\f10e1"}.mdi-file-table-box-multiple:before{content:"\f10e2"}.mdi-file-table-box-multiple-outline:before{content:"\f10e3"}.mdi-file-table-box-outline:before{content:"\f10e4"}.mdi-file-table-outline:before{content:"\f0c7f"}.mdi-file-tree:before{content:"\f0645"}.mdi-file-tree-outline:before{content:"\f13d2"}.mdi-file-undo:before{content:"\f08dc"}.mdi-file-undo-outline:before{content:"\f103c"}.mdi-file-upload:before{content:"\f0a4d"}.mdi-file-upload-outline:before{content:"\f0a4e"}.mdi-file-video:before{content:"\f022b"}.mdi-file-video-outline:before{content:"\f0e2c"}.mdi-file-word:before{content:"\f022c"}.mdi-file-word-box:before{content:"\f022d"}.mdi-file-word-box-outline:before{content:"\f103d"}.mdi-file-word-outline:before{content:"\f103e"}.mdi-file-xml-box:before{content:"\f1b4b"}.mdi-film:before{content:"\f022f"}.mdi-filmstrip:before{content:"\f0230"}.mdi-filmstrip-box:before{content:"\f0332"}.mdi-filmstrip-box-multiple:before{content:"\f0d18"}.mdi-filmstrip-off:before{content:"\f0231"}.mdi-filter:before{content:"\f0232"}.mdi-filter-check:before{content:"\f18ec"}.mdi-filter-check-outline:before{content:"\f18ed"}.mdi-filter-cog:before{content:"\f1aa3"}.mdi-filter-cog-outline:before{content:"\f1aa4"}.mdi-filter-menu:before{content:"\f10e5"}.mdi-filter-menu-outline:before{content:"\f10e6"}.mdi-filter-minus:before{content:"\f0eee"}.mdi-filter-minus-outline:before{content:"\f0eef"}.mdi-filter-multiple:before{content:"\f1a3f"}.mdi-filter-multiple-outline:before{content:"\f1a40"}.mdi-filter-off:before{content:"\f14ef"}.mdi-filter-off-outline:before{content:"\f14f0"}.mdi-filter-outline:before{content:"\f0233"}.mdi-filter-plus:before{content:"\f0ef0"}.mdi-filter-plus-outline:before{content:"\f0ef1"}.mdi-filter-remove:before{content:"\f0234"}.mdi-filter-remove-outline:before{content:"\f0235"}.mdi-filter-settings:before{content:"\f1aa5"}.mdi-filter-settings-outline:before{content:"\f1aa6"}.mdi-filter-variant:before{content:"\f0236"}.mdi-filter-variant-minus:before{content:"\f1112"}.mdi-filter-variant-plus:before{content:"\f1113"}.mdi-filter-variant-remove:before{content:"\f103f"}.mdi-finance:before{content:"\f081f"}.mdi-find-replace:before{content:"\f06d4"}.mdi-fingerprint:before{content:"\f0237"}.mdi-fingerprint-off:before{content:"\f0eb1"}.mdi-fire:before{content:"\f0238"}.mdi-fire-alert:before{content:"\f15d7"}.mdi-fire-circle:before{content:"\f1807"}.mdi-fire-extinguisher:before{content:"\f0ef2"}.mdi-fire-hydrant:before{content:"\f1137"}.mdi-fire-hydrant-alert:before{content:"\f1138"}.mdi-fire-hydrant-off:before{content:"\f1139"}.mdi-fire-off:before{content:"\f1722"}.mdi-fire-station:before{content:"\f1cc3"}.mdi-fire-truck:before{content:"\f08ab"}.mdi-firebase:before{content:"\f0967"}.mdi-firefox:before{content:"\f0239"}.mdi-fireplace:before{content:"\f0e2e"}.mdi-fireplace-off:before{content:"\f0e2f"}.mdi-firewire:before{content:"\f05be"}.mdi-firework:before{content:"\f0e30"}.mdi-firework-off:before{content:"\f1723"}.mdi-fish:before{content:"\f023a"}.mdi-fish-off:before{content:"\f13f3"}.mdi-fishbowl:before{content:"\f0ef3"}.mdi-fishbowl-outline:before{content:"\f0ef4"}.mdi-fit-to-page:before{content:"\f0ef5"}.mdi-fit-to-page-outline:before{content:"\f0ef6"}.mdi-fit-to-screen:before{content:"\f18f4"}.mdi-fit-to-screen-outline:before{content:"\f18f5"}.mdi-flag:before{content:"\f023b"}.mdi-flag-checkered:before{content:"\f023c"}.mdi-flag-minus:before{content:"\f0b99"}.mdi-flag-minus-outline:before{content:"\f10b2"}.mdi-flag-off:before{content:"\f18ee"}.mdi-flag-off-outline:before{content:"\f18ef"}.mdi-flag-outline:before{content:"\f023d"}.mdi-flag-plus:before{content:"\f0b9a"}.mdi-flag-plus-outline:before{content:"\f10b3"}.mdi-flag-remove:before{content:"\f0b9b"}.mdi-flag-remove-outline:before{content:"\f10b4"}.mdi-flag-triangle:before{content:"\f023f"}.mdi-flag-variant:before{content:"\f0240"}.mdi-flag-variant-minus:before{content:"\f1bb4"}.mdi-flag-variant-minus-outline:before{content:"\f1bb5"}.mdi-flag-variant-off:before{content:"\f1bb0"}.mdi-flag-variant-off-outline:before{content:"\f1bb1"}.mdi-flag-variant-outline:before{content:"\f023e"}.mdi-flag-variant-plus:before{content:"\f1bb2"}.mdi-flag-variant-plus-outline:before{content:"\f1bb3"}.mdi-flag-variant-remove:before{content:"\f1bb6"}.mdi-flag-variant-remove-outline:before{content:"\f1bb7"}.mdi-flare:before{content:"\f0d72"}.mdi-flash:before{content:"\f0241"}.mdi-flash-alert:before{content:"\f0ef7"}.mdi-flash-alert-outline:before{content:"\f0ef8"}.mdi-flash-auto:before{content:"\f0242"}.mdi-flash-off:before{content:"\f0243"}.mdi-flash-off-outline:before{content:"\f1b45"}.mdi-flash-outline:before{content:"\f06d5"}.mdi-flash-red-eye:before{content:"\f067b"}.mdi-flash-triangle:before{content:"\f1b1d"}.mdi-flash-triangle-outline:before{content:"\f1b1e"}.mdi-flashlight:before{content:"\f0244"}.mdi-flashlight-off:before{content:"\f0245"}.mdi-flask:before{content:"\f0093"}.mdi-flask-empty:before{content:"\f0094"}.mdi-flask-empty-minus:before{content:"\f123a"}.mdi-flask-empty-minus-outline:before{content:"\f123b"}.mdi-flask-empty-off:before{content:"\f13f4"}.mdi-flask-empty-off-outline:before{content:"\f13f5"}.mdi-flask-empty-outline:before{content:"\f0095"}.mdi-flask-empty-plus:before{content:"\f123c"}.mdi-flask-empty-plus-outline:before{content:"\f123d"}.mdi-flask-empty-remove:before{content:"\f123e"}.mdi-flask-empty-remove-outline:before{content:"\f123f"}.mdi-flask-minus:before{content:"\f1240"}.mdi-flask-minus-outline:before{content:"\f1241"}.mdi-flask-off:before{content:"\f13f6"}.mdi-flask-off-outline:before{content:"\f13f7"}.mdi-flask-outline:before{content:"\f0096"}.mdi-flask-plus:before{content:"\f1242"}.mdi-flask-plus-outline:before{content:"\f1243"}.mdi-flask-remove:before{content:"\f1244"}.mdi-flask-remove-outline:before{content:"\f1245"}.mdi-flask-round-bottom:before{content:"\f124b"}.mdi-flask-round-bottom-empty:before{content:"\f124c"}.mdi-flask-round-bottom-empty-outline:before{content:"\f124d"}.mdi-flask-round-bottom-outline:before{content:"\f124e"}.mdi-fleur-de-lis:before{content:"\f1303"}.mdi-flip-horizontal:before{content:"\f10e7"}.mdi-flip-to-back:before{content:"\f0247"}.mdi-flip-to-front:before{content:"\f0248"}.mdi-flip-vertical:before{content:"\f10e8"}.mdi-floor-lamp:before{content:"\f08dd"}.mdi-floor-lamp-dual:before{content:"\f1040"}.mdi-floor-lamp-dual-outline:before{content:"\f17ce"}.mdi-floor-lamp-outline:before{content:"\f17c8"}.mdi-floor-lamp-torchiere:before{content:"\f1747"}.mdi-floor-lamp-torchiere-outline:before{content:"\f17d6"}.mdi-floor-lamp-torchiere-variant:before{content:"\f1041"}.mdi-floor-lamp-torchiere-variant-outline:before{content:"\f17cf"}.mdi-floor-plan:before{content:"\f0821"}.mdi-floppy:before{content:"\f0249"}.mdi-floppy-variant:before{content:"\f09ef"}.mdi-flower:before{content:"\f024a"}.mdi-flower-outline:before{content:"\f09f0"}.mdi-flower-pollen:before{content:"\f1885"}.mdi-flower-pollen-outline:before{content:"\f1886"}.mdi-flower-poppy:before{content:"\f0d08"}.mdi-flower-tulip:before{content:"\f09f1"}.mdi-flower-tulip-outline:before{content:"\f09f2"}.mdi-focus-auto:before{content:"\f0f4e"}.mdi-focus-field:before{content:"\f0f4f"}.mdi-focus-field-horizontal:before{content:"\f0f50"}.mdi-focus-field-vertical:before{content:"\f0f51"}.mdi-folder:before{content:"\f024b"}.mdi-folder-account:before{content:"\f024c"}.mdi-folder-account-outline:before{content:"\f0b9c"}.mdi-folder-alert:before{content:"\f0dcc"}.mdi-folder-alert-outline:before{content:"\f0dcd"}.mdi-folder-arrow-down:before{content:"\f19e8"}.mdi-folder-arrow-down-outline:before{content:"\f19e9"}.mdi-folder-arrow-left:before{content:"\f19ea"}.mdi-folder-arrow-left-outline:before{content:"\f19eb"}.mdi-folder-arrow-left-right:before{content:"\f19ec"}.mdi-folder-arrow-left-right-outline:before{content:"\f19ed"}.mdi-folder-arrow-right:before{content:"\f19ee"}.mdi-folder-arrow-right-outline:before{content:"\f19ef"}.mdi-folder-arrow-up:before{content:"\f19f0"}.mdi-folder-arrow-up-down:before{content:"\f19f1"}.mdi-folder-arrow-up-down-outline:before{content:"\f19f2"}.mdi-folder-arrow-up-outline:before{content:"\f19f3"}.mdi-folder-cancel:before{content:"\f19f4"}.mdi-folder-cancel-outline:before{content:"\f19f5"}.mdi-folder-check:before{content:"\f197e"}.mdi-folder-check-outline:before{content:"\f197f"}.mdi-folder-clock:before{content:"\f0aba"}.mdi-folder-clock-outline:before{content:"\f0abb"}.mdi-folder-cog:before{content:"\f107f"}.mdi-folder-cog-outline:before{content:"\f1080"}.mdi-folder-download:before{content:"\f024d"}.mdi-folder-download-outline:before{content:"\f10e9"}.mdi-folder-edit:before{content:"\f08de"}.mdi-folder-edit-outline:before{content:"\f0dce"}.mdi-folder-eye:before{content:"\f178a"}.mdi-folder-eye-outline:before{content:"\f178b"}.mdi-folder-file:before{content:"\f19f6"}.mdi-folder-file-outline:before{content:"\f19f7"}.mdi-folder-google-drive:before{content:"\f024e"}.mdi-folder-heart:before{content:"\f10ea"}.mdi-folder-heart-outline:before{content:"\f10eb"}.mdi-folder-hidden:before{content:"\f179e"}.mdi-folder-home:before{content:"\f10b5"}.mdi-folder-home-outline:before{content:"\f10b6"}.mdi-folder-image:before{content:"\f024f"}.mdi-folder-information:before{content:"\f10b7"}.mdi-folder-information-outline:before{content:"\f10b8"}.mdi-folder-key:before{content:"\f08ac"}.mdi-folder-key-network:before{content:"\f08ad"}.mdi-folder-key-network-outline:before{content:"\f0c80"}.mdi-folder-key-outline:before{content:"\f10ec"}.mdi-folder-lock:before{content:"\f0250"}.mdi-folder-lock-open:before{content:"\f0251"}.mdi-folder-lock-open-outline:before{content:"\f1aa7"}.mdi-folder-lock-outline:before{content:"\f1aa8"}.mdi-folder-marker:before{content:"\f126d"}.mdi-folder-marker-outline:before{content:"\f126e"}.mdi-folder-minus:before{content:"\f1b49"}.mdi-folder-minus-outline:before{content:"\f1b4a"}.mdi-folder-move:before{content:"\f0252"}.mdi-folder-move-outline:before{content:"\f1246"}.mdi-folder-multiple:before{content:"\f0253"}.mdi-folder-multiple-image:before{content:"\f0254"}.mdi-folder-multiple-outline:before{content:"\f0255"}.mdi-folder-multiple-plus:before{content:"\f147e"}.mdi-folder-multiple-plus-outline:before{content:"\f147f"}.mdi-folder-music:before{content:"\f1359"}.mdi-folder-music-outline:before{content:"\f135a"}.mdi-folder-network:before{content:"\f0870"}.mdi-folder-network-outline:before{content:"\f0c81"}.mdi-folder-off:before{content:"\f19f8"}.mdi-folder-off-outline:before{content:"\f19f9"}.mdi-folder-open:before{content:"\f0770"}.mdi-folder-open-outline:before{content:"\f0dcf"}.mdi-folder-outline:before{content:"\f0256"}.mdi-folder-play:before{content:"\f19fa"}.mdi-folder-play-outline:before{content:"\f19fb"}.mdi-folder-plus:before{content:"\f0257"}.mdi-folder-plus-outline:before{content:"\f0b9d"}.mdi-folder-pound:before{content:"\f0d09"}.mdi-folder-pound-outline:before{content:"\f0d0a"}.mdi-folder-question:before{content:"\f19ca"}.mdi-folder-question-outline:before{content:"\f19cb"}.mdi-folder-refresh:before{content:"\f0749"}.mdi-folder-refresh-outline:before{content:"\f0542"}.mdi-folder-remove:before{content:"\f0258"}.mdi-folder-remove-outline:before{content:"\f0b9e"}.mdi-folder-search:before{content:"\f0968"}.mdi-folder-search-outline:before{content:"\f0969"}.mdi-folder-settings:before{content:"\f107d"}.mdi-folder-settings-outline:before{content:"\f107e"}.mdi-folder-star:before{content:"\f069d"}.mdi-folder-star-multiple:before{content:"\f13d3"}.mdi-folder-star-multiple-outline:before{content:"\f13d4"}.mdi-folder-star-outline:before{content:"\f0b9f"}.mdi-folder-swap:before{content:"\f0fb6"}.mdi-folder-swap-outline:before{content:"\f0fb7"}.mdi-folder-sync:before{content:"\f0d0b"}.mdi-folder-sync-outline:before{content:"\f0d0c"}.mdi-folder-table:before{content:"\f12e3"}.mdi-folder-table-outline:before{content:"\f12e4"}.mdi-folder-text:before{content:"\f0c82"}.mdi-folder-text-outline:before{content:"\f0c83"}.mdi-folder-upload:before{content:"\f0259"}.mdi-folder-upload-outline:before{content:"\f10ed"}.mdi-folder-wrench:before{content:"\f19fc"}.mdi-folder-wrench-outline:before{content:"\f19fd"}.mdi-folder-zip:before{content:"\f06eb"}.mdi-folder-zip-outline:before{content:"\f07b9"}.mdi-font-awesome:before{content:"\f003a"}.mdi-food:before{content:"\f025a"}.mdi-food-apple:before{content:"\f025b"}.mdi-food-apple-outline:before{content:"\f0c84"}.mdi-food-croissant:before{content:"\f07c8"}.mdi-food-drumstick:before{content:"\f141f"}.mdi-food-drumstick-off:before{content:"\f1468"}.mdi-food-drumstick-off-outline:before{content:"\f1469"}.mdi-food-drumstick-outline:before{content:"\f1420"}.mdi-food-fork-drink:before{content:"\f05f2"}.mdi-food-halal:before{content:"\f1572"}.mdi-food-hot-dog:before{content:"\f184b"}.mdi-food-kosher:before{content:"\f1573"}.mdi-food-off:before{content:"\f05f3"}.mdi-food-off-outline:before{content:"\f1915"}.mdi-food-outline:before{content:"\f1916"}.mdi-food-steak:before{content:"\f146a"}.mdi-food-steak-off:before{content:"\f146b"}.mdi-food-takeout-box:before{content:"\f1836"}.mdi-food-takeout-box-outline:before{content:"\f1837"}.mdi-food-turkey:before{content:"\f171c"}.mdi-food-variant:before{content:"\f025c"}.mdi-food-variant-off:before{content:"\f13e5"}.mdi-foot-print:before{content:"\f0f52"}.mdi-football:before{content:"\f025d"}.mdi-football-australian:before{content:"\f025e"}.mdi-football-helmet:before{content:"\f025f"}.mdi-forest:before{content:"\f1897"}.mdi-forest-outline:before{content:"\f1c63"}.mdi-forklift:before{content:"\f07c9"}.mdi-form-dropdown:before{content:"\f1400"}.mdi-form-select:before{content:"\f1401"}.mdi-form-textarea:before{content:"\f1095"}.mdi-form-textbox:before{content:"\f060e"}.mdi-form-textbox-lock:before{content:"\f135d"}.mdi-form-textbox-password:before{content:"\f07f5"}.mdi-format-align-bottom:before{content:"\f0753"}.mdi-format-align-center:before{content:"\f0260"}.mdi-format-align-justify:before{content:"\f0261"}.mdi-format-align-left:before{content:"\f0262"}.mdi-format-align-middle:before{content:"\f0754"}.mdi-format-align-right:before{content:"\f0263"}.mdi-format-align-top:before{content:"\f0755"}.mdi-format-annotation-minus:before{content:"\f0abc"}.mdi-format-annotation-plus:before{content:"\f0646"}.mdi-format-bold:before{content:"\f0264"}.mdi-format-clear:before{content:"\f0265"}.mdi-format-color-fill:before{content:"\f0266"}.mdi-format-color-highlight:before{content:"\f0e31"}.mdi-format-color-marker-cancel:before{content:"\f1313"}.mdi-format-color-text:before{content:"\f069e"}.mdi-format-columns:before{content:"\f08df"}.mdi-format-float-center:before{content:"\f0267"}.mdi-format-float-left:before{content:"\f0268"}.mdi-format-float-none:before{content:"\f0269"}.mdi-format-float-right:before{content:"\f026a"}.mdi-format-font:before{content:"\f06d6"}.mdi-format-font-size-decrease:before{content:"\f09f3"}.mdi-format-font-size-increase:before{content:"\f09f4"}.mdi-format-header-1:before{content:"\f026b"}.mdi-format-header-2:before{content:"\f026c"}.mdi-format-header-3:before{content:"\f026d"}.mdi-format-header-4:before{content:"\f026e"}.mdi-format-header-5:before{content:"\f026f"}.mdi-format-header-6:before{content:"\f0270"}.mdi-format-header-decrease:before{content:"\f0271"}.mdi-format-header-equal:before{content:"\f0272"}.mdi-format-header-increase:before{content:"\f0273"}.mdi-format-header-pound:before{content:"\f0274"}.mdi-format-horizontal-align-center:before{content:"\f061e"}.mdi-format-horizontal-align-left:before{content:"\f061f"}.mdi-format-horizontal-align-right:before{content:"\f0620"}.mdi-format-indent-decrease:before{content:"\f0275"}.mdi-format-indent-increase:before{content:"\f0276"}.mdi-format-italic:before{content:"\f0277"}.mdi-format-letter-case:before{content:"\f0b34"}.mdi-format-letter-case-lower:before{content:"\f0b35"}.mdi-format-letter-case-upper:before{content:"\f0b36"}.mdi-format-letter-ends-with:before{content:"\f0fb8"}.mdi-format-letter-matches:before{content:"\f0fb9"}.mdi-format-letter-spacing:before{content:"\f1956"}.mdi-format-letter-spacing-variant:before{content:"\f1afb"}.mdi-format-letter-starts-with:before{content:"\f0fba"}.mdi-format-line-height:before{content:"\f1afc"}.mdi-format-line-spacing:before{content:"\f0278"}.mdi-format-line-style:before{content:"\f05c8"}.mdi-format-line-weight:before{content:"\f05c9"}.mdi-format-list-bulleted:before{content:"\f0279"}.mdi-format-list-bulleted-square:before{content:"\f0dd0"}.mdi-format-list-bulleted-triangle:before{content:"\f0eb2"}.mdi-format-list-bulleted-type:before{content:"\f027a"}.mdi-format-list-checkbox:before{content:"\f096a"}.mdi-format-list-checks:before{content:"\f0756"}.mdi-format-list-group:before{content:"\f1860"}.mdi-format-list-group-plus:before{content:"\f1b56"}.mdi-format-list-numbered:before{content:"\f027b"}.mdi-format-list-numbered-rtl:before{content:"\f0d0d"}.mdi-format-list-text:before{content:"\f126f"}.mdi-format-overline:before{content:"\f0eb3"}.mdi-format-page-break:before{content:"\f06d7"}.mdi-format-page-split:before{content:"\f1917"}.mdi-format-paint:before{content:"\f027c"}.mdi-format-paragraph:before{content:"\f027d"}.mdi-format-paragraph-spacing:before{content:"\f1afd"}.mdi-format-pilcrow:before{content:"\f06d8"}.mdi-format-pilcrow-arrow-left:before{content:"\f0286"}.mdi-format-pilcrow-arrow-right:before{content:"\f0285"}.mdi-format-quote-close:before{content:"\f027e"}.mdi-format-quote-close-outline:before{content:"\f11a8"}.mdi-format-quote-open:before{content:"\f0757"}.mdi-format-quote-open-outline:before{content:"\f11a7"}.mdi-format-rotate-90:before{content:"\f06aa"}.mdi-format-section:before{content:"\f069f"}.mdi-format-size:before{content:"\f027f"}.mdi-format-strikethrough:before{content:"\f0280"}.mdi-format-strikethrough-variant:before{content:"\f0281"}.mdi-format-subscript:before{content:"\f0282"}.mdi-format-superscript:before{content:"\f0283"}.mdi-format-text:before{content:"\f0284"}.mdi-format-text-rotation-angle-down:before{content:"\f0fbb"}.mdi-format-text-rotation-angle-up:before{content:"\f0fbc"}.mdi-format-text-rotation-down:before{content:"\f0d73"}.mdi-format-text-rotation-down-vertical:before{content:"\f0fbd"}.mdi-format-text-rotation-none:before{content:"\f0d74"}.mdi-format-text-rotation-up:before{content:"\f0fbe"}.mdi-format-text-rotation-vertical:before{content:"\f0fbf"}.mdi-format-text-variant:before{content:"\f0e32"}.mdi-format-text-variant-outline:before{content:"\f150f"}.mdi-format-text-wrapping-clip:before{content:"\f0d0e"}.mdi-format-text-wrapping-overflow:before{content:"\f0d0f"}.mdi-format-text-wrapping-wrap:before{content:"\f0d10"}.mdi-format-textbox:before{content:"\f0d11"}.mdi-format-title:before{content:"\f05f4"}.mdi-format-underline:before{content:"\f0287"}.mdi-format-underline-wavy:before{content:"\f18e9"}.mdi-format-vertical-align-bottom:before{content:"\f0621"}.mdi-format-vertical-align-center:before{content:"\f0622"}.mdi-format-vertical-align-top:before{content:"\f0623"}.mdi-format-wrap-inline:before{content:"\f0288"}.mdi-format-wrap-square:before{content:"\f0289"}.mdi-format-wrap-tight:before{content:"\f028a"}.mdi-format-wrap-top-bottom:before{content:"\f028b"}.mdi-forum:before{content:"\f028c"}.mdi-forum-minus:before{content:"\f1aa9"}.mdi-forum-minus-outline:before{content:"\f1aaa"}.mdi-forum-outline:before{content:"\f0822"}.mdi-forum-plus:before{content:"\f1aab"}.mdi-forum-plus-outline:before{content:"\f1aac"}.mdi-forum-remove:before{content:"\f1aad"}.mdi-forum-remove-outline:before{content:"\f1aae"}.mdi-forward:before{content:"\f028d"}.mdi-forwardburger:before{content:"\f0d75"}.mdi-fountain:before{content:"\f096b"}.mdi-fountain-pen:before{content:"\f0d12"}.mdi-fountain-pen-tip:before{content:"\f0d13"}.mdi-fraction-one-half:before{content:"\f1992"}.mdi-freebsd:before{content:"\f08e0"}.mdi-french-fries:before{content:"\f1957"}.mdi-frequently-asked-questions:before{content:"\f0eb4"}.mdi-fridge:before{content:"\f0290"}.mdi-fridge-alert:before{content:"\f11b1"}.mdi-fridge-alert-outline:before{content:"\f11b2"}.mdi-fridge-bottom:before{content:"\f0292"}.mdi-fridge-industrial:before{content:"\f15ee"}.mdi-fridge-industrial-alert:before{content:"\f15ef"}.mdi-fridge-industrial-alert-outline:before{content:"\f15f0"}.mdi-fridge-industrial-off:before{content:"\f15f1"}.mdi-fridge-industrial-off-outline:before{content:"\f15f2"}.mdi-fridge-industrial-outline:before{content:"\f15f3"}.mdi-fridge-off:before{content:"\f11af"}.mdi-fridge-off-outline:before{content:"\f11b0"}.mdi-fridge-outline:before{content:"\f028f"}.mdi-fridge-top:before{content:"\f0291"}.mdi-fridge-variant:before{content:"\f15f4"}.mdi-fridge-variant-alert:before{content:"\f15f5"}.mdi-fridge-variant-alert-outline:before{content:"\f15f6"}.mdi-fridge-variant-off:before{content:"\f15f7"}.mdi-fridge-variant-off-outline:before{content:"\f15f8"}.mdi-fridge-variant-outline:before{content:"\f15f9"}.mdi-fruit-cherries:before{content:"\f1042"}.mdi-fruit-cherries-off:before{content:"\f13f8"}.mdi-fruit-citrus:before{content:"\f1043"}.mdi-fruit-citrus-off:before{content:"\f13f9"}.mdi-fruit-grapes:before{content:"\f1044"}.mdi-fruit-grapes-outline:before{content:"\f1045"}.mdi-fruit-pear:before{content:"\f1a0e"}.mdi-fruit-pineapple:before{content:"\f1046"}.mdi-fruit-watermelon:before{content:"\f1047"}.mdi-fuel:before{content:"\f07ca"}.mdi-fuel-cell:before{content:"\f18b5"}.mdi-fullscreen:before{content:"\f0293"}.mdi-fullscreen-exit:before{content:"\f0294"}.mdi-function:before{content:"\f0295"}.mdi-function-variant:before{content:"\f0871"}.mdi-furigana-horizontal:before{content:"\f1081"}.mdi-furigana-vertical:before{content:"\f1082"}.mdi-fuse:before{content:"\f0c85"}.mdi-fuse-alert:before{content:"\f142d"}.mdi-fuse-blade:before{content:"\f0c86"}.mdi-fuse-off:before{content:"\f142c"}.mdi-gamepad:before{content:"\f0296"}.mdi-gamepad-circle:before{content:"\f0e33"}.mdi-gamepad-circle-down:before{content:"\f0e34"}.mdi-gamepad-circle-left:before{content:"\f0e35"}.mdi-gamepad-circle-outline:before{content:"\f0e36"}.mdi-gamepad-circle-right:before{content:"\f0e37"}.mdi-gamepad-circle-up:before{content:"\f0e38"}.mdi-gamepad-down:before{content:"\f0e39"}.mdi-gamepad-left:before{content:"\f0e3a"}.mdi-gamepad-outline:before{content:"\f1919"}.mdi-gamepad-right:before{content:"\f0e3b"}.mdi-gamepad-round:before{content:"\f0e3c"}.mdi-gamepad-round-down:before{content:"\f0e3d"}.mdi-gamepad-round-left:before{content:"\f0e3e"}.mdi-gamepad-round-outline:before{content:"\f0e3f"}.mdi-gamepad-round-right:before{content:"\f0e40"}.mdi-gamepad-round-up:before{content:"\f0e41"}.mdi-gamepad-square:before{content:"\f0eb5"}.mdi-gamepad-square-outline:before{content:"\f0eb6"}.mdi-gamepad-up:before{content:"\f0e42"}.mdi-gamepad-variant:before{content:"\f0297"}.mdi-gamepad-variant-outline:before{content:"\f0eb7"}.mdi-gamma:before{content:"\f10ee"}.mdi-gantry-crane:before{content:"\f0dd1"}.mdi-garage:before{content:"\f06d9"}.mdi-garage-alert:before{content:"\f0872"}.mdi-garage-alert-variant:before{content:"\f12d5"}.mdi-garage-lock:before{content:"\f17fb"}.mdi-garage-open:before{content:"\f06da"}.mdi-garage-open-variant:before{content:"\f12d4"}.mdi-garage-variant:before{content:"\f12d3"}.mdi-garage-variant-lock:before{content:"\f17fc"}.mdi-gas-burner:before{content:"\f1a1b"}.mdi-gas-cylinder:before{content:"\f0647"}.mdi-gas-station:before{content:"\f0298"}.mdi-gas-station-in-use:before{content:"\f1cc4"}.mdi-gas-station-in-use-outline:before{content:"\f1cc5"}.mdi-gas-station-off:before{content:"\f1409"}.mdi-gas-station-off-outline:before{content:"\f140a"}.mdi-gas-station-outline:before{content:"\f0eb8"}.mdi-gate:before{content:"\f0299"}.mdi-gate-alert:before{content:"\f17f8"}.mdi-gate-and:before{content:"\f08e1"}.mdi-gate-arrow-left:before{content:"\f17f7"}.mdi-gate-arrow-right:before{content:"\f1169"}.mdi-gate-buffer:before{content:"\f1afe"}.mdi-gate-nand:before{content:"\f08e2"}.mdi-gate-nor:before{content:"\f08e3"}.mdi-gate-not:before{content:"\f08e4"}.mdi-gate-open:before{content:"\f116a"}.mdi-gate-or:before{content:"\f08e5"}.mdi-gate-xnor:before{content:"\f08e6"}.mdi-gate-xor:before{content:"\f08e7"}.mdi-gatsby:before{content:"\f0e43"}.mdi-gauge:before{content:"\f029a"}.mdi-gauge-empty:before{content:"\f0873"}.mdi-gauge-full:before{content:"\f0874"}.mdi-gauge-low:before{content:"\f0875"}.mdi-gavel:before{content:"\f029b"}.mdi-gender-female:before{content:"\f029c"}.mdi-gender-male:before{content:"\f029d"}.mdi-gender-male-female:before{content:"\f029e"}.mdi-gender-male-female-variant:before{content:"\f113f"}.mdi-gender-non-binary:before{content:"\f1140"}.mdi-gender-transgender:before{content:"\f029f"}.mdi-generator-mobile:before{content:"\f1c8a"}.mdi-generator-portable:before{content:"\f1c8b"}.mdi-generator-stationary:before{content:"\f1c8c"}.mdi-gentoo:before{content:"\f08e8"}.mdi-gesture:before{content:"\f07cb"}.mdi-gesture-double-tap:before{content:"\f073c"}.mdi-gesture-pinch:before{content:"\f0abd"}.mdi-gesture-spread:before{content:"\f0abe"}.mdi-gesture-swipe:before{content:"\f0d76"}.mdi-gesture-swipe-down:before{content:"\f073d"}.mdi-gesture-swipe-horizontal:before{content:"\f0abf"}.mdi-gesture-swipe-left:before{content:"\f073e"}.mdi-gesture-swipe-right:before{content:"\f073f"}.mdi-gesture-swipe-up:before{content:"\f0740"}.mdi-gesture-swipe-vertical:before{content:"\f0ac0"}.mdi-gesture-tap:before{content:"\f0741"}.mdi-gesture-tap-box:before{content:"\f12a9"}.mdi-gesture-tap-button:before{content:"\f12a8"}.mdi-gesture-tap-hold:before{content:"\f0d77"}.mdi-gesture-two-double-tap:before{content:"\f0742"}.mdi-gesture-two-tap:before{content:"\f0743"}.mdi-ghost:before{content:"\f02a0"}.mdi-ghost-off:before{content:"\f09f5"}.mdi-ghost-off-outline:before{content:"\f165c"}.mdi-ghost-outline:before{content:"\f165d"}.mdi-gift:before{content:"\f0e44"}.mdi-gift-off:before{content:"\f16ef"}.mdi-gift-off-outline:before{content:"\f16f0"}.mdi-gift-open:before{content:"\f16f1"}.mdi-gift-open-outline:before{content:"\f16f2"}.mdi-gift-outline:before{content:"\f02a1"}.mdi-git:before{content:"\f02a2"}.mdi-github:before{content:"\f02a4"}.mdi-gitlab:before{content:"\f0ba0"}.mdi-glass-cocktail:before{content:"\f0356"}.mdi-glass-cocktail-off:before{content:"\f15e6"}.mdi-glass-flute:before{content:"\f02a5"}.mdi-glass-fragile:before{content:"\f1873"}.mdi-glass-mug:before{content:"\f02a6"}.mdi-glass-mug-off:before{content:"\f15e7"}.mdi-glass-mug-variant:before{content:"\f1116"}.mdi-glass-mug-variant-off:before{content:"\f15e8"}.mdi-glass-pint-outline:before{content:"\f130d"}.mdi-glass-stange:before{content:"\f02a7"}.mdi-glass-tulip:before{content:"\f02a8"}.mdi-glass-wine:before{content:"\f0876"}.mdi-glasses:before{content:"\f02aa"}.mdi-globe-light:before{content:"\f066f"}.mdi-globe-light-outline:before{content:"\f12d7"}.mdi-globe-model:before{content:"\f08e9"}.mdi-gmail:before{content:"\f02ab"}.mdi-gnome:before{content:"\f02ac"}.mdi-go-kart:before{content:"\f0d79"}.mdi-go-kart-track:before{content:"\f0d7a"}.mdi-gog:before{content:"\f0ba1"}.mdi-gold:before{content:"\f124f"}.mdi-golf:before{content:"\f0823"}.mdi-golf-cart:before{content:"\f11a4"}.mdi-golf-tee:before{content:"\f1083"}.mdi-gondola:before{content:"\f0686"}.mdi-goodreads:before{content:"\f0d7b"}.mdi-google:before{content:"\f02ad"}.mdi-google-ads:before{content:"\f0c87"}.mdi-google-analytics:before{content:"\f07cc"}.mdi-google-assistant:before{content:"\f07cd"}.mdi-google-cardboard:before{content:"\f02ae"}.mdi-google-chrome:before{content:"\f02af"}.mdi-google-circles:before{content:"\f02b0"}.mdi-google-circles-communities:before{content:"\f02b1"}.mdi-google-circles-extended:before{content:"\f02b2"}.mdi-google-circles-group:before{content:"\f02b3"}.mdi-google-classroom:before{content:"\f02c0"}.mdi-google-cloud:before{content:"\f11f6"}.mdi-google-downasaur:before{content:"\f1362"}.mdi-google-drive:before{content:"\f02b6"}.mdi-google-earth:before{content:"\f02b7"}.mdi-google-fit:before{content:"\f096c"}.mdi-google-glass:before{content:"\f02b8"}.mdi-google-hangouts:before{content:"\f02c9"}.mdi-google-keep:before{content:"\f06dc"}.mdi-google-lens:before{content:"\f09f6"}.mdi-google-maps:before{content:"\f05f5"}.mdi-google-my-business:before{content:"\f1048"}.mdi-google-nearby:before{content:"\f02b9"}.mdi-google-play:before{content:"\f02bc"}.mdi-google-plus:before{content:"\f02bd"}.mdi-google-podcast:before{content:"\f0eb9"}.mdi-google-spreadsheet:before{content:"\f09f7"}.mdi-google-street-view:before{content:"\f0c88"}.mdi-google-translate:before{content:"\f02bf"}.mdi-gradient-horizontal:before{content:"\f174a"}.mdi-gradient-vertical:before{content:"\f06a0"}.mdi-grain:before{content:"\f0d7c"}.mdi-graph:before{content:"\f1049"}.mdi-graph-outline:before{content:"\f104a"}.mdi-graphql:before{content:"\f0877"}.mdi-grass:before{content:"\f1510"}.mdi-grave-stone:before{content:"\f0ba2"}.mdi-grease-pencil:before{content:"\f0648"}.mdi-greater-than:before{content:"\f096d"}.mdi-greater-than-or-equal:before{content:"\f096e"}.mdi-greenhouse:before{content:"\f002d"}.mdi-grid:before{content:"\f02c1"}.mdi-grid-large:before{content:"\f0758"}.mdi-grid-off:before{content:"\f02c2"}.mdi-grill:before{content:"\f0e45"}.mdi-grill-outline:before{content:"\f118a"}.mdi-group:before{content:"\f02c3"}.mdi-guitar-acoustic:before{content:"\f0771"}.mdi-guitar-electric:before{content:"\f02c4"}.mdi-guitar-pick:before{content:"\f02c5"}.mdi-guitar-pick-outline:before{content:"\f02c6"}.mdi-guy-fawkes-mask:before{content:"\f0825"}.mdi-gymnastics:before{content:"\f1a41"}.mdi-hail:before{content:"\f0ac1"}.mdi-hair-dryer:before{content:"\f10ef"}.mdi-hair-dryer-outline:before{content:"\f10f0"}.mdi-halloween:before{content:"\f0ba3"}.mdi-hamburger:before{content:"\f0685"}.mdi-hamburger-check:before{content:"\f1776"}.mdi-hamburger-minus:before{content:"\f1777"}.mdi-hamburger-off:before{content:"\f1778"}.mdi-hamburger-plus:before{content:"\f1779"}.mdi-hamburger-remove:before{content:"\f177a"}.mdi-hammer:before{content:"\f08ea"}.mdi-hammer-screwdriver:before{content:"\f1322"}.mdi-hammer-sickle:before{content:"\f1887"}.mdi-hammer-wrench:before{content:"\f1323"}.mdi-hand-back-left:before{content:"\f0e46"}.mdi-hand-back-left-off:before{content:"\f1830"}.mdi-hand-back-left-off-outline:before{content:"\f1832"}.mdi-hand-back-left-outline:before{content:"\f182c"}.mdi-hand-back-right:before{content:"\f0e47"}.mdi-hand-back-right-off:before{content:"\f1831"}.mdi-hand-back-right-off-outline:before{content:"\f1833"}.mdi-hand-back-right-outline:before{content:"\f182d"}.mdi-hand-clap:before{content:"\f194b"}.mdi-hand-clap-off:before{content:"\f1a42"}.mdi-hand-coin:before{content:"\f188f"}.mdi-hand-coin-outline:before{content:"\f1890"}.mdi-hand-cycle:before{content:"\f1b9c"}.mdi-hand-extended:before{content:"\f18b6"}.mdi-hand-extended-outline:before{content:"\f18b7"}.mdi-hand-front-left:before{content:"\f182b"}.mdi-hand-front-left-outline:before{content:"\f182e"}.mdi-hand-front-right:before{content:"\f0a4f"}.mdi-hand-front-right-outline:before{content:"\f182f"}.mdi-hand-heart:before{content:"\f10f1"}.mdi-hand-heart-outline:before{content:"\f157e"}.mdi-hand-okay:before{content:"\f0a50"}.mdi-hand-peace:before{content:"\f0a51"}.mdi-hand-peace-variant:before{content:"\f0a52"}.mdi-hand-pointing-down:before{content:"\f0a53"}.mdi-hand-pointing-left:before{content:"\f0a54"}.mdi-hand-pointing-right:before{content:"\f02c7"}.mdi-hand-pointing-up:before{content:"\f0a55"}.mdi-hand-saw:before{content:"\f0e48"}.mdi-hand-wash:before{content:"\f157f"}.mdi-hand-wash-outline:before{content:"\f1580"}.mdi-hand-water:before{content:"\f139f"}.mdi-hand-wave:before{content:"\f1821"}.mdi-hand-wave-outline:before{content:"\f1822"}.mdi-handball:before{content:"\f0f53"}.mdi-handcuffs:before{content:"\f113e"}.mdi-hands-pray:before{content:"\f0579"}.mdi-handshake:before{content:"\f1218"}.mdi-handshake-outline:before{content:"\f15a1"}.mdi-hanger:before{content:"\f02c8"}.mdi-hard-hat:before{content:"\f096f"}.mdi-harddisk:before{content:"\f02ca"}.mdi-harddisk-plus:before{content:"\f104b"}.mdi-harddisk-remove:before{content:"\f104c"}.mdi-hat-fedora:before{content:"\f0ba4"}.mdi-hazard-lights:before{content:"\f0c89"}.mdi-hdmi-port:before{content:"\f1bb8"}.mdi-hdr:before{content:"\f0d7d"}.mdi-hdr-off:before{content:"\f0d7e"}.mdi-head:before{content:"\f135e"}.mdi-head-alert:before{content:"\f1338"}.mdi-head-alert-outline:before{content:"\f1339"}.mdi-head-check:before{content:"\f133a"}.mdi-head-check-outline:before{content:"\f133b"}.mdi-head-cog:before{content:"\f133c"}.mdi-head-cog-outline:before{content:"\f133d"}.mdi-head-dots-horizontal:before{content:"\f133e"}.mdi-head-dots-horizontal-outline:before{content:"\f133f"}.mdi-head-flash:before{content:"\f1340"}.mdi-head-flash-outline:before{content:"\f1341"}.mdi-head-heart:before{content:"\f1342"}.mdi-head-heart-outline:before{content:"\f1343"}.mdi-head-lightbulb:before{content:"\f1344"}.mdi-head-lightbulb-outline:before{content:"\f1345"}.mdi-head-minus:before{content:"\f1346"}.mdi-head-minus-outline:before{content:"\f1347"}.mdi-head-outline:before{content:"\f135f"}.mdi-head-plus:before{content:"\f1348"}.mdi-head-plus-outline:before{content:"\f1349"}.mdi-head-question:before{content:"\f134a"}.mdi-head-question-outline:before{content:"\f134b"}.mdi-head-remove:before{content:"\f134c"}.mdi-head-remove-outline:before{content:"\f134d"}.mdi-head-snowflake:before{content:"\f134e"}.mdi-head-snowflake-outline:before{content:"\f134f"}.mdi-head-sync:before{content:"\f1350"}.mdi-head-sync-outline:before{content:"\f1351"}.mdi-headphones:before{content:"\f02cb"}.mdi-headphones-bluetooth:before{content:"\f0970"}.mdi-headphones-box:before{content:"\f02cc"}.mdi-headphones-off:before{content:"\f07ce"}.mdi-headphones-settings:before{content:"\f02cd"}.mdi-headset:before{content:"\f02ce"}.mdi-headset-dock:before{content:"\f02cf"}.mdi-headset-off:before{content:"\f02d0"}.mdi-heart:before{content:"\f02d1"}.mdi-heart-box:before{content:"\f02d2"}.mdi-heart-box-outline:before{content:"\f02d3"}.mdi-heart-broken:before{content:"\f02d4"}.mdi-heart-broken-outline:before{content:"\f0d14"}.mdi-heart-circle:before{content:"\f0971"}.mdi-heart-circle-outline:before{content:"\f0972"}.mdi-heart-cog:before{content:"\f1663"}.mdi-heart-cog-outline:before{content:"\f1664"}.mdi-heart-flash:before{content:"\f0ef9"}.mdi-heart-half:before{content:"\f06df"}.mdi-heart-half-full:before{content:"\f06de"}.mdi-heart-half-outline:before{content:"\f06e0"}.mdi-heart-minus:before{content:"\f142f"}.mdi-heart-minus-outline:before{content:"\f1432"}.mdi-heart-multiple:before{content:"\f0a56"}.mdi-heart-multiple-outline:before{content:"\f0a57"}.mdi-heart-off:before{content:"\f0759"}.mdi-heart-off-outline:before{content:"\f1434"}.mdi-heart-outline:before{content:"\f02d5"}.mdi-heart-plus:before{content:"\f142e"}.mdi-heart-plus-outline:before{content:"\f1431"}.mdi-heart-pulse:before{content:"\f05f6"}.mdi-heart-remove:before{content:"\f1430"}.mdi-heart-remove-outline:before{content:"\f1433"}.mdi-heart-search:before{content:"\f1c8d"}.mdi-heart-settings:before{content:"\f1665"}.mdi-heart-settings-outline:before{content:"\f1666"}.mdi-heat-pump:before{content:"\f1a43"}.mdi-heat-pump-outline:before{content:"\f1a44"}.mdi-heat-wave:before{content:"\f1a45"}.mdi-heating-coil:before{content:"\f1aaf"}.mdi-helicopter:before{content:"\f0ac2"}.mdi-help:before{content:"\f02d6"}.mdi-help-box:before{content:"\f078b"}.mdi-help-box-multiple:before{content:"\f1c0a"}.mdi-help-box-multiple-outline:before{content:"\f1c0b"}.mdi-help-box-outline:before{content:"\f1c0c"}.mdi-help-circle:before{content:"\f02d7"}.mdi-help-circle-outline:before{content:"\f0625"}.mdi-help-network:before{content:"\f06f5"}.mdi-help-network-outline:before{content:"\f0c8a"}.mdi-help-rhombus:before{content:"\f0ba5"}.mdi-help-rhombus-outline:before{content:"\f0ba6"}.mdi-hexadecimal:before{content:"\f12a7"}.mdi-hexagon:before{content:"\f02d8"}.mdi-hexagon-multiple:before{content:"\f06e1"}.mdi-hexagon-multiple-outline:before{content:"\f10f2"}.mdi-hexagon-outline:before{content:"\f02d9"}.mdi-hexagon-slice-1:before{content:"\f0ac3"}.mdi-hexagon-slice-2:before{content:"\f0ac4"}.mdi-hexagon-slice-3:before{content:"\f0ac5"}.mdi-hexagon-slice-4:before{content:"\f0ac6"}.mdi-hexagon-slice-5:before{content:"\f0ac7"}.mdi-hexagon-slice-6:before{content:"\f0ac8"}.mdi-hexagram:before{content:"\f0ac9"}.mdi-hexagram-outline:before{content:"\f0aca"}.mdi-high-definition:before{content:"\f07cf"}.mdi-high-definition-box:before{content:"\f0878"}.mdi-highway:before{content:"\f05f7"}.mdi-hiking:before{content:"\f0d7f"}.mdi-history:before{content:"\f02da"}.mdi-hockey-puck:before{content:"\f0879"}.mdi-hockey-sticks:before{content:"\f087a"}.mdi-hololens:before{content:"\f02db"}.mdi-home:before{content:"\f02dc"}.mdi-home-account:before{content:"\f0826"}.mdi-home-alert:before{content:"\f087b"}.mdi-home-alert-outline:before{content:"\f15d0"}.mdi-home-analytics:before{content:"\f0eba"}.mdi-home-assistant:before{content:"\f07d0"}.mdi-home-automation:before{content:"\f07d1"}.mdi-home-battery:before{content:"\f1901"}.mdi-home-battery-outline:before{content:"\f1902"}.mdi-home-circle:before{content:"\f07d2"}.mdi-home-circle-outline:before{content:"\f104d"}.mdi-home-city:before{content:"\f0d15"}.mdi-home-city-outline:before{content:"\f0d16"}.mdi-home-clock:before{content:"\f1a12"}.mdi-home-clock-outline:before{content:"\f1a13"}.mdi-home-edit:before{content:"\f1159"}.mdi-home-edit-outline:before{content:"\f115a"}.mdi-home-export-outline:before{content:"\f0f9b"}.mdi-home-flood:before{content:"\f0efa"}.mdi-home-floor-0:before{content:"\f0dd2"}.mdi-home-floor-1:before{content:"\f0d80"}.mdi-home-floor-2:before{content:"\f0d81"}.mdi-home-floor-3:before{content:"\f0d82"}.mdi-home-floor-a:before{content:"\f0d83"}.mdi-home-floor-b:before{content:"\f0d84"}.mdi-home-floor-g:before{content:"\f0d85"}.mdi-home-floor-l:before{content:"\f0d86"}.mdi-home-floor-negative-1:before{content:"\f0dd3"}.mdi-home-group:before{content:"\f0dd4"}.mdi-home-group-minus:before{content:"\f19c1"}.mdi-home-group-plus:before{content:"\f19c0"}.mdi-home-group-remove:before{content:"\f19c2"}.mdi-home-heart:before{content:"\f0827"}.mdi-home-import-outline:before{content:"\f0f9c"}.mdi-home-lightbulb:before{content:"\f1251"}.mdi-home-lightbulb-outline:before{content:"\f1252"}.mdi-home-lightning-bolt:before{content:"\f1903"}.mdi-home-lightning-bolt-outline:before{content:"\f1904"}.mdi-home-lock:before{content:"\f08eb"}.mdi-home-lock-open:before{content:"\f08ec"}.mdi-home-map-marker:before{content:"\f05f8"}.mdi-home-minus:before{content:"\f0974"}.mdi-home-minus-outline:before{content:"\f13d5"}.mdi-home-modern:before{content:"\f02dd"}.mdi-home-off:before{content:"\f1a46"}.mdi-home-off-outline:before{content:"\f1a47"}.mdi-home-outline:before{content:"\f06a1"}.mdi-home-percent:before{content:"\f1c7c"}.mdi-home-percent-outline:before{content:"\f1c7d"}.mdi-home-plus:before{content:"\f0975"}.mdi-home-plus-outline:before{content:"\f13d6"}.mdi-home-remove:before{content:"\f1247"}.mdi-home-remove-outline:before{content:"\f13d7"}.mdi-home-roof:before{content:"\f112b"}.mdi-home-search:before{content:"\f13b0"}.mdi-home-search-outline:before{content:"\f13b1"}.mdi-home-silo:before{content:"\f1ba0"}.mdi-home-silo-outline:before{content:"\f1ba1"}.mdi-home-sound-in:before{content:"\f1c2f"}.mdi-home-sound-in-outline:before{content:"\f1c30"}.mdi-home-sound-out:before{content:"\f1c31"}.mdi-home-sound-out-outline:before{content:"\f1c32"}.mdi-home-switch:before{content:"\f1794"}.mdi-home-switch-outline:before{content:"\f1795"}.mdi-home-thermometer:before{content:"\f0f54"}.mdi-home-thermometer-outline:before{content:"\f0f55"}.mdi-home-variant:before{content:"\f02de"}.mdi-home-variant-outline:before{content:"\f0ba7"}.mdi-hook:before{content:"\f06e2"}.mdi-hook-off:before{content:"\f06e3"}.mdi-hoop-house:before{content:"\f0e56"}.mdi-hops:before{content:"\f02df"}.mdi-horizontal-rotate-clockwise:before{content:"\f10f3"}.mdi-horizontal-rotate-counterclockwise:before{content:"\f10f4"}.mdi-horse:before{content:"\f15bf"}.mdi-horse-human:before{content:"\f15c0"}.mdi-horse-variant:before{content:"\f15c1"}.mdi-horse-variant-fast:before{content:"\f186e"}.mdi-horseshoe:before{content:"\f0a58"}.mdi-hospital:before{content:"\f0ff6"}.mdi-hospital-box:before{content:"\f02e0"}.mdi-hospital-box-outline:before{content:"\f0ff7"}.mdi-hospital-building:before{content:"\f02e1"}.mdi-hospital-marker:before{content:"\f02e2"}.mdi-hot-tub:before{content:"\f0828"}.mdi-hours-12:before{content:"\f1c94"}.mdi-hours-24:before{content:"\f1478"}.mdi-hub:before{content:"\f1c95"}.mdi-hub-outline:before{content:"\f1c96"}.mdi-hubspot:before{content:"\f0d17"}.mdi-hulu:before{content:"\f0829"}.mdi-human:before{content:"\f02e6"}.mdi-human-baby-changing-table:before{content:"\f138b"}.mdi-human-cane:before{content:"\f1581"}.mdi-human-capacity-decrease:before{content:"\f159b"}.mdi-human-capacity-increase:before{content:"\f159c"}.mdi-human-child:before{content:"\f02e7"}.mdi-human-dolly:before{content:"\f1980"}.mdi-human-edit:before{content:"\f14e8"}.mdi-human-female:before{content:"\f0649"}.mdi-human-female-boy:before{content:"\f0a59"}.mdi-human-female-dance:before{content:"\f15c9"}.mdi-human-female-female:before{content:"\f0a5a"}.mdi-human-female-female-child:before{content:"\f1c8e"}.mdi-human-female-girl:before{content:"\f0a5b"}.mdi-human-greeting:before{content:"\f17c4"}.mdi-human-greeting-proximity:before{content:"\f159d"}.mdi-human-greeting-variant:before{content:"\f064a"}.mdi-human-handsdown:before{content:"\f064b"}.mdi-human-handsup:before{content:"\f064c"}.mdi-human-male:before{content:"\f064d"}.mdi-human-male-board:before{content:"\f0890"}.mdi-human-male-board-poll:before{content:"\f0846"}.mdi-human-male-boy:before{content:"\f0a5c"}.mdi-human-male-child:before{content:"\f138c"}.mdi-human-male-female:before{content:"\f02e8"}.mdi-human-male-female-child:before{content:"\f1823"}.mdi-human-male-girl:before{content:"\f0a5d"}.mdi-human-male-height:before{content:"\f0efb"}.mdi-human-male-height-variant:before{content:"\f0efc"}.mdi-human-male-male:before{content:"\f0a5e"}.mdi-human-male-male-child:before{content:"\f1c8f"}.mdi-human-non-binary:before{content:"\f1848"}.mdi-human-pregnant:before{content:"\f05cf"}.mdi-human-queue:before{content:"\f1571"}.mdi-human-scooter:before{content:"\f11e9"}.mdi-human-walker:before{content:"\f1b71"}.mdi-human-wheelchair:before{content:"\f138d"}.mdi-human-white-cane:before{content:"\f1981"}.mdi-humble-bundle:before{content:"\f0744"}.mdi-hvac:before{content:"\f1352"}.mdi-hvac-off:before{content:"\f159e"}.mdi-hydraulic-oil-level:before{content:"\f1324"}.mdi-hydraulic-oil-temperature:before{content:"\f1325"}.mdi-hydro-power:before{content:"\f12e5"}.mdi-hydrogen-station:before{content:"\f1894"}.mdi-ice-cream:before{content:"\f082a"}.mdi-ice-cream-off:before{content:"\f0e52"}.mdi-ice-pop:before{content:"\f0efd"}.mdi-id-card:before{content:"\f0fc0"}.mdi-identifier:before{content:"\f0efe"}.mdi-ideogram-cjk:before{content:"\f1331"}.mdi-ideogram-cjk-variant:before{content:"\f1332"}.mdi-image:before{content:"\f02e9"}.mdi-image-album:before{content:"\f02ea"}.mdi-image-area:before{content:"\f02eb"}.mdi-image-area-close:before{content:"\f02ec"}.mdi-image-auto-adjust:before{content:"\f0fc1"}.mdi-image-broken:before{content:"\f02ed"}.mdi-image-broken-variant:before{content:"\f02ee"}.mdi-image-check:before{content:"\f1b25"}.mdi-image-check-outline:before{content:"\f1b26"}.mdi-image-edit:before{content:"\f11e3"}.mdi-image-edit-outline:before{content:"\f11e4"}.mdi-image-filter-black-white:before{content:"\f02f0"}.mdi-image-filter-center-focus:before{content:"\f02f1"}.mdi-image-filter-center-focus-strong:before{content:"\f0eff"}.mdi-image-filter-center-focus-strong-outline:before{content:"\f0f00"}.mdi-image-filter-center-focus-weak:before{content:"\f02f2"}.mdi-image-filter-drama:before{content:"\f02f3"}.mdi-image-filter-drama-outline:before{content:"\f1bff"}.mdi-image-filter-frames:before{content:"\f02f4"}.mdi-image-filter-hdr:before{content:"\f02f5"}.mdi-image-filter-hdr-outline:before{content:"\f1c64"}.mdi-image-filter-none:before{content:"\f02f6"}.mdi-image-filter-tilt-shift:before{content:"\f02f7"}.mdi-image-filter-vintage:before{content:"\f02f8"}.mdi-image-frame:before{content:"\f0e49"}.mdi-image-lock:before{content:"\f1ab0"}.mdi-image-lock-outline:before{content:"\f1ab1"}.mdi-image-marker:before{content:"\f177b"}.mdi-image-marker-outline:before{content:"\f177c"}.mdi-image-minus:before{content:"\f1419"}.mdi-image-minus-outline:before{content:"\f1b47"}.mdi-image-move:before{content:"\f09f8"}.mdi-image-multiple:before{content:"\f02f9"}.mdi-image-multiple-outline:before{content:"\f02ef"}.mdi-image-off:before{content:"\f082b"}.mdi-image-off-outline:before{content:"\f11d1"}.mdi-image-outline:before{content:"\f0976"}.mdi-image-plus:before{content:"\f087c"}.mdi-image-plus-outline:before{content:"\f1b46"}.mdi-image-refresh:before{content:"\f19fe"}.mdi-image-refresh-outline:before{content:"\f19ff"}.mdi-image-remove:before{content:"\f1418"}.mdi-image-remove-outline:before{content:"\f1b48"}.mdi-image-search:before{content:"\f0977"}.mdi-image-search-outline:before{content:"\f0978"}.mdi-image-size-select-actual:before{content:"\f0c8d"}.mdi-image-size-select-large:before{content:"\f0c8e"}.mdi-image-size-select-small:before{content:"\f0c8f"}.mdi-image-sync:before{content:"\f1a00"}.mdi-image-sync-outline:before{content:"\f1a01"}.mdi-image-text:before{content:"\f160d"}.mdi-import:before{content:"\f02fa"}.mdi-inbox:before{content:"\f0687"}.mdi-inbox-arrow-down:before{content:"\f02fb"}.mdi-inbox-arrow-down-outline:before{content:"\f1270"}.mdi-inbox-arrow-up:before{content:"\f03d1"}.mdi-inbox-arrow-up-outline:before{content:"\f1271"}.mdi-inbox-full:before{content:"\f1272"}.mdi-inbox-full-outline:before{content:"\f1273"}.mdi-inbox-multiple:before{content:"\f08b0"}.mdi-inbox-multiple-outline:before{content:"\f0ba8"}.mdi-inbox-outline:before{content:"\f1274"}.mdi-inbox-remove:before{content:"\f159f"}.mdi-inbox-remove-outline:before{content:"\f15a0"}.mdi-incognito:before{content:"\f05f9"}.mdi-incognito-circle:before{content:"\f1421"}.mdi-incognito-circle-off:before{content:"\f1422"}.mdi-incognito-off:before{content:"\f0075"}.mdi-induction:before{content:"\f184c"}.mdi-infinity:before{content:"\f06e4"}.mdi-information:before{content:"\f02fc"}.mdi-information-box:before{content:"\f1c65"}.mdi-information-box-outline:before{content:"\f1c66"}.mdi-information-off:before{content:"\f178c"}.mdi-information-off-outline:before{content:"\f178d"}.mdi-information-outline:before{content:"\f02fd"}.mdi-information-slab-box:before{content:"\f1c67"}.mdi-information-slab-box-outline:before{content:"\f1c68"}.mdi-information-slab-circle:before{content:"\f1c69"}.mdi-information-slab-circle-outline:before{content:"\f1c6a"}.mdi-information-slab-symbol:before{content:"\f1c6b"}.mdi-information-symbol:before{content:"\f1c6c"}.mdi-information-variant:before{content:"\f064e"}.mdi-information-variant-box:before{content:"\f1c6d"}.mdi-information-variant-box-outline:before{content:"\f1c6e"}.mdi-information-variant-circle:before{content:"\f1c6f"}.mdi-information-variant-circle-outline:before{content:"\f1c70"}.mdi-instagram:before{content:"\f02fe"}.mdi-instrument-triangle:before{content:"\f104e"}.mdi-integrated-circuit-chip:before{content:"\f1913"}.mdi-invert-colors:before{content:"\f0301"}.mdi-invert-colors-off:before{content:"\f0e4a"}.mdi-invoice:before{content:"\f1cd2"}.mdi-invoice-arrow-left:before{content:"\f1cd3"}.mdi-invoice-arrow-left-outline:before{content:"\f1cd4"}.mdi-invoice-arrow-right:before{content:"\f1cd5"}.mdi-invoice-arrow-right-outline:before{content:"\f1cd6"}.mdi-invoice-check:before{content:"\f1cd7"}.mdi-invoice-check-outline:before{content:"\f1cd8"}.mdi-invoice-clock:before{content:"\f1cd9"}.mdi-invoice-clock-outline:before{content:"\f1cda"}.mdi-invoice-edit:before{content:"\f1cdb"}.mdi-invoice-edit-outline:before{content:"\f1cdc"}.mdi-invoice-export-outline:before{content:"\f1cdd"}.mdi-invoice-fast:before{content:"\f1cde"}.mdi-invoice-fast-outline:before{content:"\f1cdf"}.mdi-invoice-import:before{content:"\f1ce0"}.mdi-invoice-import-outline:before{content:"\f1ce1"}.mdi-invoice-list:before{content:"\f1ce2"}.mdi-invoice-list-outline:before{content:"\f1ce3"}.mdi-invoice-minus:before{content:"\f1ce4"}.mdi-invoice-minus-outline:before{content:"\f1ce5"}.mdi-invoice-multiple:before{content:"\f1ce6"}.mdi-invoice-multiple-outline:before{content:"\f1ce7"}.mdi-invoice-outline:before{content:"\f1ce8"}.mdi-invoice-plus:before{content:"\f1ce9"}.mdi-invoice-plus-outline:before{content:"\f1cea"}.mdi-invoice-remove:before{content:"\f1ceb"}.mdi-invoice-remove-outline:before{content:"\f1cec"}.mdi-invoice-send:before{content:"\f1ced"}.mdi-invoice-send-outline:before{content:"\f1cee"}.mdi-invoice-text:before{content:"\f1cef"}.mdi-invoice-text-arrow-left:before{content:"\f1cf0"}.mdi-invoice-text-arrow-left-outline:before{content:"\f1cf1"}.mdi-invoice-text-arrow-right:before{content:"\f1cf2"}.mdi-invoice-text-arrow-right-outline:before{content:"\f1cf3"}.mdi-invoice-text-check:before{content:"\f1cf4"}.mdi-invoice-text-check-outline:before{content:"\f1cf5"}.mdi-invoice-text-clock:before{content:"\f1cf6"}.mdi-invoice-text-clock-outline:before{content:"\f1cf7"}.mdi-invoice-text-edit:before{content:"\f1cf8"}.mdi-invoice-text-edit-outline:before{content:"\f1cf9"}.mdi-invoice-text-fast:before{content:"\f1cfa"}.mdi-invoice-text-fast-outline:before{content:"\f1cfb"}.mdi-invoice-text-minus:before{content:"\f1cfc"}.mdi-invoice-text-minus-outline:before{content:"\f1cfd"}.mdi-invoice-text-multiple:before{content:"\f1cfe"}.mdi-invoice-text-multiple-outline:before{content:"\f1cff"}.mdi-invoice-text-outline:before{content:"\f1d00"}.mdi-invoice-text-plus:before{content:"\f1d01"}.mdi-invoice-text-plus-outline:before{content:"\f1d02"}.mdi-invoice-text-remove:before{content:"\f1d03"}.mdi-invoice-text-remove-outline:before{content:"\f1d04"}.mdi-invoice-text-send:before{content:"\f1d05"}.mdi-invoice-text-send-outline:before{content:"\f1d06"}.mdi-iobroker:before{content:"\f12e8"}.mdi-ip:before{content:"\f0a5f"}.mdi-ip-network:before{content:"\f0a60"}.mdi-ip-network-outline:before{content:"\f0c90"}.mdi-ip-outline:before{content:"\f1982"}.mdi-ipod:before{content:"\f0c91"}.mdi-iron:before{content:"\f1824"}.mdi-iron-board:before{content:"\f1838"}.mdi-iron-outline:before{content:"\f1825"}.mdi-island:before{content:"\f104f"}.mdi-island-variant:before{content:"\f1cc6"}.mdi-iv-bag:before{content:"\f10b9"}.mdi-jabber:before{content:"\f0dd5"}.mdi-jeepney:before{content:"\f0302"}.mdi-jellyfish:before{content:"\f0f01"}.mdi-jellyfish-outline:before{content:"\f0f02"}.mdi-jira:before{content:"\f0303"}.mdi-jquery:before{content:"\f087d"}.mdi-jsfiddle:before{content:"\f0304"}.mdi-jump-rope:before{content:"\f12ff"}.mdi-kabaddi:before{content:"\f0d87"}.mdi-kangaroo:before{content:"\f1558"}.mdi-karate:before{content:"\f082c"}.mdi-kayaking:before{content:"\f08af"}.mdi-keg:before{content:"\f0305"}.mdi-kettle:before{content:"\f05fa"}.mdi-kettle-alert:before{content:"\f1317"}.mdi-kettle-alert-outline:before{content:"\f1318"}.mdi-kettle-off:before{content:"\f131b"}.mdi-kettle-off-outline:before{content:"\f131c"}.mdi-kettle-outline:before{content:"\f0f56"}.mdi-kettle-pour-over:before{content:"\f173c"}.mdi-kettle-steam:before{content:"\f1319"}.mdi-kettle-steam-outline:before{content:"\f131a"}.mdi-kettlebell:before{content:"\f1300"}.mdi-key:before{content:"\f0306"}.mdi-key-alert:before{content:"\f1983"}.mdi-key-alert-outline:before{content:"\f1984"}.mdi-key-arrow-right:before{content:"\f1312"}.mdi-key-chain:before{content:"\f1574"}.mdi-key-chain-variant:before{content:"\f1575"}.mdi-key-change:before{content:"\f0307"}.mdi-key-link:before{content:"\f119f"}.mdi-key-minus:before{content:"\f0308"}.mdi-key-outline:before{content:"\f0dd6"}.mdi-key-plus:before{content:"\f0309"}.mdi-key-remove:before{content:"\f030a"}.mdi-key-star:before{content:"\f119e"}.mdi-key-variant:before{content:"\f030b"}.mdi-key-wireless:before{content:"\f0fc2"}.mdi-keyboard:before{content:"\f030c"}.mdi-keyboard-backspace:before{content:"\f030d"}.mdi-keyboard-caps:before{content:"\f030e"}.mdi-keyboard-close:before{content:"\f030f"}.mdi-keyboard-close-outline:before{content:"\f1c00"}.mdi-keyboard-esc:before{content:"\f12b7"}.mdi-keyboard-f1:before{content:"\f12ab"}.mdi-keyboard-f10:before{content:"\f12b4"}.mdi-keyboard-f11:before{content:"\f12b5"}.mdi-keyboard-f12:before{content:"\f12b6"}.mdi-keyboard-f2:before{content:"\f12ac"}.mdi-keyboard-f3:before{content:"\f12ad"}.mdi-keyboard-f4:before{content:"\f12ae"}.mdi-keyboard-f5:before{content:"\f12af"}.mdi-keyboard-f6:before{content:"\f12b0"}.mdi-keyboard-f7:before{content:"\f12b1"}.mdi-keyboard-f8:before{content:"\f12b2"}.mdi-keyboard-f9:before{content:"\f12b3"}.mdi-keyboard-off:before{content:"\f0310"}.mdi-keyboard-off-outline:before{content:"\f0e4b"}.mdi-keyboard-outline:before{content:"\f097b"}.mdi-keyboard-return:before{content:"\f0311"}.mdi-keyboard-settings:before{content:"\f09f9"}.mdi-keyboard-settings-outline:before{content:"\f09fa"}.mdi-keyboard-space:before{content:"\f1050"}.mdi-keyboard-tab:before{content:"\f0312"}.mdi-keyboard-tab-reverse:before{content:"\f0325"}.mdi-keyboard-variant:before{content:"\f0313"}.mdi-khanda:before{content:"\f10fd"}.mdi-kickstarter:before{content:"\f0745"}.mdi-kite:before{content:"\f1985"}.mdi-kite-outline:before{content:"\f1986"}.mdi-kitesurfing:before{content:"\f1744"}.mdi-klingon:before{content:"\f135b"}.mdi-knife:before{content:"\f09fb"}.mdi-knife-military:before{content:"\f09fc"}.mdi-knob:before{content:"\f1b96"}.mdi-koala:before{content:"\f173f"}.mdi-kodi:before{content:"\f0314"}.mdi-kubernetes:before{content:"\f10fe"}.mdi-label:before{content:"\f0315"}.mdi-label-multiple:before{content:"\f1375"}.mdi-label-multiple-outline:before{content:"\f1376"}.mdi-label-off:before{content:"\f0acb"}.mdi-label-off-outline:before{content:"\f0acc"}.mdi-label-outline:before{content:"\f0316"}.mdi-label-percent:before{content:"\f12ea"}.mdi-label-percent-outline:before{content:"\f12eb"}.mdi-label-variant:before{content:"\f0acd"}.mdi-label-variant-outline:before{content:"\f0ace"}.mdi-ladder:before{content:"\f15a2"}.mdi-ladybug:before{content:"\f082d"}.mdi-lambda:before{content:"\f0627"}.mdi-lamp:before{content:"\f06b5"}.mdi-lamp-outline:before{content:"\f17d0"}.mdi-lamps:before{content:"\f1576"}.mdi-lamps-outline:before{content:"\f17d1"}.mdi-lan:before{content:"\f0317"}.mdi-lan-check:before{content:"\f12aa"}.mdi-lan-connect:before{content:"\f0318"}.mdi-lan-disconnect:before{content:"\f0319"}.mdi-lan-pending:before{content:"\f031a"}.mdi-land-fields:before{content:"\f1ab2"}.mdi-land-plots:before{content:"\f1ab3"}.mdi-land-plots-circle:before{content:"\f1ab4"}.mdi-land-plots-circle-variant:before{content:"\f1ab5"}.mdi-land-plots-marker:before{content:"\f1c5d"}.mdi-land-rows-horizontal:before{content:"\f1ab6"}.mdi-land-rows-vertical:before{content:"\f1ab7"}.mdi-landslide:before{content:"\f1a48"}.mdi-landslide-outline:before{content:"\f1a49"}.mdi-language-c:before{content:"\f0671"}.mdi-language-cpp:before{content:"\f0672"}.mdi-language-csharp:before{content:"\f031b"}.mdi-language-css3:before{content:"\f031c"}.mdi-language-fortran:before{content:"\f121a"}.mdi-language-go:before{content:"\f07d3"}.mdi-language-haskell:before{content:"\f0c92"}.mdi-language-html5:before{content:"\f031d"}.mdi-language-java:before{content:"\f0b37"}.mdi-language-javascript:before{content:"\f031e"}.mdi-language-kotlin:before{content:"\f1219"}.mdi-language-lua:before{content:"\f08b1"}.mdi-language-markdown:before{content:"\f0354"}.mdi-language-markdown-outline:before{content:"\f0f5b"}.mdi-language-php:before{content:"\f031f"}.mdi-language-python:before{content:"\f0320"}.mdi-language-r:before{content:"\f07d4"}.mdi-language-ruby:before{content:"\f0d2d"}.mdi-language-ruby-on-rails:before{content:"\f0acf"}.mdi-language-rust:before{content:"\f1617"}.mdi-language-swift:before{content:"\f06e5"}.mdi-language-typescript:before{content:"\f06e6"}.mdi-language-xaml:before{content:"\f0673"}.mdi-laptop:before{content:"\f0322"}.mdi-laptop-account:before{content:"\f1a4a"}.mdi-laptop-off:before{content:"\f06e7"}.mdi-laravel:before{content:"\f0ad0"}.mdi-laser-pointer:before{content:"\f1484"}.mdi-lasso:before{content:"\f0f03"}.mdi-lastpass:before{content:"\f0446"}.mdi-latitude:before{content:"\f0f57"}.mdi-launch:before{content:"\f0327"}.mdi-lava-lamp:before{content:"\f07d5"}.mdi-layers:before{content:"\f0328"}.mdi-layers-edit:before{content:"\f1892"}.mdi-layers-minus:before{content:"\f0e4c"}.mdi-layers-off:before{content:"\f0329"}.mdi-layers-off-outline:before{content:"\f09fd"}.mdi-layers-outline:before{content:"\f09fe"}.mdi-layers-plus:before{content:"\f0e4d"}.mdi-layers-remove:before{content:"\f0e4e"}.mdi-layers-search:before{content:"\f1206"}.mdi-layers-search-outline:before{content:"\f1207"}.mdi-layers-triple:before{content:"\f0f58"}.mdi-layers-triple-outline:before{content:"\f0f59"}.mdi-lead-pencil:before{content:"\f064f"}.mdi-leaf:before{content:"\f032a"}.mdi-leaf-circle:before{content:"\f1905"}.mdi-leaf-circle-outline:before{content:"\f1906"}.mdi-leaf-maple:before{content:"\f0c93"}.mdi-leaf-maple-off:before{content:"\f12da"}.mdi-leaf-off:before{content:"\f12d9"}.mdi-leak:before{content:"\f0dd7"}.mdi-leak-off:before{content:"\f0dd8"}.mdi-lectern:before{content:"\f1af0"}.mdi-led-off:before{content:"\f032b"}.mdi-led-on:before{content:"\f032c"}.mdi-led-outline:before{content:"\f032d"}.mdi-led-strip:before{content:"\f07d6"}.mdi-led-strip-variant:before{content:"\f1051"}.mdi-led-strip-variant-off:before{content:"\f1a4b"}.mdi-led-variant-off:before{content:"\f032e"}.mdi-led-variant-on:before{content:"\f032f"}.mdi-led-variant-outline:before{content:"\f0330"}.mdi-leek:before{content:"\f117d"}.mdi-less-than:before{content:"\f097c"}.mdi-less-than-or-equal:before{content:"\f097d"}.mdi-library:before{content:"\f0331"}.mdi-library-outline:before{content:"\f1a22"}.mdi-library-shelves:before{content:"\f0ba9"}.mdi-license:before{content:"\f0fc3"}.mdi-lifebuoy:before{content:"\f087e"}.mdi-light-flood-down:before{content:"\f1987"}.mdi-light-flood-up:before{content:"\f1988"}.mdi-light-recessed:before{content:"\f179b"}.mdi-light-switch:before{content:"\f097e"}.mdi-light-switch-off:before{content:"\f1a24"}.mdi-lightbulb:before{content:"\f0335"}.mdi-lightbulb-alert:before{content:"\f19e1"}.mdi-lightbulb-alert-outline:before{content:"\f19e2"}.mdi-lightbulb-auto:before{content:"\f1800"}.mdi-lightbulb-auto-outline:before{content:"\f1801"}.mdi-lightbulb-cfl:before{content:"\f1208"}.mdi-lightbulb-cfl-off:before{content:"\f1209"}.mdi-lightbulb-cfl-spiral:before{content:"\f1275"}.mdi-lightbulb-cfl-spiral-off:before{content:"\f12c3"}.mdi-lightbulb-fluorescent-tube:before{content:"\f1804"}.mdi-lightbulb-fluorescent-tube-outline:before{content:"\f1805"}.mdi-lightbulb-group:before{content:"\f1253"}.mdi-lightbulb-group-off:before{content:"\f12cd"}.mdi-lightbulb-group-off-outline:before{content:"\f12ce"}.mdi-lightbulb-group-outline:before{content:"\f1254"}.mdi-lightbulb-multiple:before{content:"\f1255"}.mdi-lightbulb-multiple-off:before{content:"\f12cf"}.mdi-lightbulb-multiple-off-outline:before{content:"\f12d0"}.mdi-lightbulb-multiple-outline:before{content:"\f1256"}.mdi-lightbulb-night:before{content:"\f1a4c"}.mdi-lightbulb-night-outline:before{content:"\f1a4d"}.mdi-lightbulb-off:before{content:"\f0e4f"}.mdi-lightbulb-off-outline:before{content:"\f0e50"}.mdi-lightbulb-on:before{content:"\f06e8"}.mdi-lightbulb-on-10:before{content:"\f1a4e"}.mdi-lightbulb-on-20:before{content:"\f1a4f"}.mdi-lightbulb-on-30:before{content:"\f1a50"}.mdi-lightbulb-on-40:before{content:"\f1a51"}.mdi-lightbulb-on-50:before{content:"\f1a52"}.mdi-lightbulb-on-60:before{content:"\f1a53"}.mdi-lightbulb-on-70:before{content:"\f1a54"}.mdi-lightbulb-on-80:before{content:"\f1a55"}.mdi-lightbulb-on-90:before{content:"\f1a56"}.mdi-lightbulb-on-outline:before{content:"\f06e9"}.mdi-lightbulb-outline:before{content:"\f0336"}.mdi-lightbulb-question:before{content:"\f19e3"}.mdi-lightbulb-question-outline:before{content:"\f19e4"}.mdi-lightbulb-spot:before{content:"\f17f4"}.mdi-lightbulb-spot-off:before{content:"\f17f5"}.mdi-lightbulb-variant:before{content:"\f1802"}.mdi-lightbulb-variant-outline:before{content:"\f1803"}.mdi-lighthouse:before{content:"\f09ff"}.mdi-lighthouse-on:before{content:"\f0a00"}.mdi-lightning-bolt:before{content:"\f140b"}.mdi-lightning-bolt-circle:before{content:"\f0820"}.mdi-lightning-bolt-outline:before{content:"\f140c"}.mdi-line-scan:before{content:"\f0624"}.mdi-lingerie:before{content:"\f1476"}.mdi-link:before{content:"\f0337"}.mdi-link-box:before{content:"\f0d1a"}.mdi-link-box-outline:before{content:"\f0d1b"}.mdi-link-box-variant:before{content:"\f0d1c"}.mdi-link-box-variant-outline:before{content:"\f0d1d"}.mdi-link-circle:before{content:"\f1cac"}.mdi-link-circle-outline:before{content:"\f1cad"}.mdi-link-edit:before{content:"\f1cae"}.mdi-link-lock:before{content:"\f10ba"}.mdi-link-off:before{content:"\f0338"}.mdi-link-plus:before{content:"\f0c94"}.mdi-link-variant:before{content:"\f0339"}.mdi-link-variant-minus:before{content:"\f10ff"}.mdi-link-variant-off:before{content:"\f033a"}.mdi-link-variant-plus:before{content:"\f1100"}.mdi-link-variant-remove:before{content:"\f1101"}.mdi-linkedin:before{content:"\f033b"}.mdi-linux:before{content:"\f033d"}.mdi-linux-mint:before{content:"\f08ed"}.mdi-lipstick:before{content:"\f13b5"}.mdi-liquid-spot:before{content:"\f1826"}.mdi-liquor:before{content:"\f191e"}.mdi-list-box:before{content:"\f1b7b"}.mdi-list-box-outline:before{content:"\f1b7c"}.mdi-list-status:before{content:"\f15ab"}.mdi-litecoin:before{content:"\f0a61"}.mdi-loading:before{content:"\f0772"}.mdi-location-enter:before{content:"\f0fc4"}.mdi-location-exit:before{content:"\f0fc5"}.mdi-lock:before{content:"\f033e"}.mdi-lock-alert:before{content:"\f08ee"}.mdi-lock-alert-outline:before{content:"\f15d1"}.mdi-lock-check:before{content:"\f139a"}.mdi-lock-check-outline:before{content:"\f16a8"}.mdi-lock-clock:before{content:"\f097f"}.mdi-lock-minus:before{content:"\f16a9"}.mdi-lock-minus-outline:before{content:"\f16aa"}.mdi-lock-off:before{content:"\f1671"}.mdi-lock-off-outline:before{content:"\f1672"}.mdi-lock-open:before{content:"\f033f"}.mdi-lock-open-alert:before{content:"\f139b"}.mdi-lock-open-alert-outline:before{content:"\f15d2"}.mdi-lock-open-check:before{content:"\f139c"}.mdi-lock-open-check-outline:before{content:"\f16ab"}.mdi-lock-open-minus:before{content:"\f16ac"}.mdi-lock-open-minus-outline:before{content:"\f16ad"}.mdi-lock-open-outline:before{content:"\f0340"}.mdi-lock-open-plus:before{content:"\f16ae"}.mdi-lock-open-plus-outline:before{content:"\f16af"}.mdi-lock-open-remove:before{content:"\f16b0"}.mdi-lock-open-remove-outline:before{content:"\f16b1"}.mdi-lock-open-variant:before{content:"\f0fc6"}.mdi-lock-open-variant-outline:before{content:"\f0fc7"}.mdi-lock-outline:before{content:"\f0341"}.mdi-lock-pattern:before{content:"\f06ea"}.mdi-lock-percent:before{content:"\f1c12"}.mdi-lock-percent-open:before{content:"\f1c13"}.mdi-lock-percent-open-outline:before{content:"\f1c14"}.mdi-lock-percent-open-variant:before{content:"\f1c15"}.mdi-lock-percent-open-variant-outline:before{content:"\f1c16"}.mdi-lock-percent-outline:before{content:"\f1c17"}.mdi-lock-plus:before{content:"\f05fb"}.mdi-lock-plus-outline:before{content:"\f16b2"}.mdi-lock-question:before{content:"\f08ef"}.mdi-lock-remove:before{content:"\f16b3"}.mdi-lock-remove-outline:before{content:"\f16b4"}.mdi-lock-reset:before{content:"\f0773"}.mdi-lock-smart:before{content:"\f08b2"}.mdi-locker:before{content:"\f07d7"}.mdi-locker-multiple:before{content:"\f07d8"}.mdi-login:before{content:"\f0342"}.mdi-login-variant:before{content:"\f05fc"}.mdi-logout:before{content:"\f0343"}.mdi-logout-variant:before{content:"\f05fd"}.mdi-longitude:before{content:"\f0f5a"}.mdi-looks:before{content:"\f0344"}.mdi-lotion:before{content:"\f1582"}.mdi-lotion-outline:before{content:"\f1583"}.mdi-lotion-plus:before{content:"\f1584"}.mdi-lotion-plus-outline:before{content:"\f1585"}.mdi-loupe:before{content:"\f0345"}.mdi-lumx:before{content:"\f0346"}.mdi-lungs:before{content:"\f1084"}.mdi-mace:before{content:"\f1843"}.mdi-magazine-pistol:before{content:"\f0324"}.mdi-magazine-rifle:before{content:"\f0323"}.mdi-magic-staff:before{content:"\f1844"}.mdi-magnet:before{content:"\f0347"}.mdi-magnet-on:before{content:"\f0348"}.mdi-magnify:before{content:"\f0349"}.mdi-magnify-close:before{content:"\f0980"}.mdi-magnify-expand:before{content:"\f1874"}.mdi-magnify-minus:before{content:"\f034a"}.mdi-magnify-minus-cursor:before{content:"\f0a62"}.mdi-magnify-minus-outline:before{content:"\f06ec"}.mdi-magnify-plus:before{content:"\f034b"}.mdi-magnify-plus-cursor:before{content:"\f0a63"}.mdi-magnify-plus-outline:before{content:"\f06ed"}.mdi-magnify-remove-cursor:before{content:"\f120c"}.mdi-magnify-remove-outline:before{content:"\f120d"}.mdi-magnify-scan:before{content:"\f1276"}.mdi-mail:before{content:"\f0ebb"}.mdi-mailbox:before{content:"\f06ee"}.mdi-mailbox-open:before{content:"\f0d88"}.mdi-mailbox-open-outline:before{content:"\f0d89"}.mdi-mailbox-open-up:before{content:"\f0d8a"}.mdi-mailbox-open-up-outline:before{content:"\f0d8b"}.mdi-mailbox-outline:before{content:"\f0d8c"}.mdi-mailbox-up:before{content:"\f0d8d"}.mdi-mailbox-up-outline:before{content:"\f0d8e"}.mdi-manjaro:before{content:"\f160a"}.mdi-map:before{content:"\f034d"}.mdi-map-check:before{content:"\f0ebc"}.mdi-map-check-outline:before{content:"\f0ebd"}.mdi-map-clock:before{content:"\f0d1e"}.mdi-map-clock-outline:before{content:"\f0d1f"}.mdi-map-legend:before{content:"\f0a01"}.mdi-map-marker:before{content:"\f034e"}.mdi-map-marker-account:before{content:"\f18e3"}.mdi-map-marker-account-outline:before{content:"\f18e4"}.mdi-map-marker-alert:before{content:"\f0f05"}.mdi-map-marker-alert-outline:before{content:"\f0f06"}.mdi-map-marker-check:before{content:"\f0c95"}.mdi-map-marker-check-outline:before{content:"\f12fb"}.mdi-map-marker-circle:before{content:"\f034f"}.mdi-map-marker-distance:before{content:"\f08f0"}.mdi-map-marker-down:before{content:"\f1102"}.mdi-map-marker-left:before{content:"\f12db"}.mdi-map-marker-left-outline:before{content:"\f12dd"}.mdi-map-marker-minus:before{content:"\f0650"}.mdi-map-marker-minus-outline:before{content:"\f12f9"}.mdi-map-marker-multiple:before{content:"\f0350"}.mdi-map-marker-multiple-outline:before{content:"\f1277"}.mdi-map-marker-off:before{content:"\f0351"}.mdi-map-marker-off-outline:before{content:"\f12fd"}.mdi-map-marker-outline:before{content:"\f07d9"}.mdi-map-marker-path:before{content:"\f0d20"}.mdi-map-marker-plus:before{content:"\f0651"}.mdi-map-marker-plus-outline:before{content:"\f12f8"}.mdi-map-marker-question:before{content:"\f0f07"}.mdi-map-marker-question-outline:before{content:"\f0f08"}.mdi-map-marker-radius:before{content:"\f0352"}.mdi-map-marker-radius-outline:before{content:"\f12fc"}.mdi-map-marker-remove:before{content:"\f0f09"}.mdi-map-marker-remove-outline:before{content:"\f12fa"}.mdi-map-marker-remove-variant:before{content:"\f0f0a"}.mdi-map-marker-right:before{content:"\f12dc"}.mdi-map-marker-right-outline:before{content:"\f12de"}.mdi-map-marker-star:before{content:"\f1608"}.mdi-map-marker-star-outline:before{content:"\f1609"}.mdi-map-marker-up:before{content:"\f1103"}.mdi-map-minus:before{content:"\f0981"}.mdi-map-outline:before{content:"\f0982"}.mdi-map-plus:before{content:"\f0983"}.mdi-map-search:before{content:"\f0984"}.mdi-map-search-outline:before{content:"\f0985"}.mdi-mapbox:before{content:"\f0baa"}.mdi-margin:before{content:"\f0353"}.mdi-marker:before{content:"\f0652"}.mdi-marker-cancel:before{content:"\f0dd9"}.mdi-marker-check:before{content:"\f0355"}.mdi-mastodon:before{content:"\f0ad1"}.mdi-material-design:before{content:"\f0986"}.mdi-material-ui:before{content:"\f0357"}.mdi-math-compass:before{content:"\f0358"}.mdi-math-cos:before{content:"\f0c96"}.mdi-math-integral:before{content:"\f0fc8"}.mdi-math-integral-box:before{content:"\f0fc9"}.mdi-math-log:before{content:"\f1085"}.mdi-math-norm:before{content:"\f0fca"}.mdi-math-norm-box:before{content:"\f0fcb"}.mdi-math-sin:before{content:"\f0c97"}.mdi-math-tan:before{content:"\f0c98"}.mdi-matrix:before{content:"\f0628"}.mdi-medal:before{content:"\f0987"}.mdi-medal-outline:before{content:"\f1326"}.mdi-medical-bag:before{content:"\f06ef"}.mdi-medical-cotton-swab:before{content:"\f1ab8"}.mdi-medication:before{content:"\f1b14"}.mdi-medication-outline:before{content:"\f1b15"}.mdi-meditation:before{content:"\f117b"}.mdi-memory:before{content:"\f035b"}.mdi-memory-arrow-down:before{content:"\f1ca6"}.mdi-menorah:before{content:"\f17d4"}.mdi-menorah-fire:before{content:"\f17d5"}.mdi-menu:before{content:"\f035c"}.mdi-menu-close:before{content:"\f1c90"}.mdi-menu-down:before{content:"\f035d"}.mdi-menu-down-outline:before{content:"\f06b6"}.mdi-menu-left:before{content:"\f035e"}.mdi-menu-left-outline:before{content:"\f0a02"}.mdi-menu-open:before{content:"\f0bab"}.mdi-menu-right:before{content:"\f035f"}.mdi-menu-right-outline:before{content:"\f0a03"}.mdi-menu-swap:before{content:"\f0a64"}.mdi-menu-swap-outline:before{content:"\f0a65"}.mdi-menu-up:before{content:"\f0360"}.mdi-menu-up-outline:before{content:"\f06b7"}.mdi-merge:before{content:"\f0f5c"}.mdi-message:before{content:"\f0361"}.mdi-message-alert:before{content:"\f0362"}.mdi-message-alert-outline:before{content:"\f0a04"}.mdi-message-arrow-left:before{content:"\f12f2"}.mdi-message-arrow-left-outline:before{content:"\f12f3"}.mdi-message-arrow-right:before{content:"\f12f4"}.mdi-message-arrow-right-outline:before{content:"\f12f5"}.mdi-message-badge:before{content:"\f1941"}.mdi-message-badge-outline:before{content:"\f1942"}.mdi-message-bookmark:before{content:"\f15ac"}.mdi-message-bookmark-outline:before{content:"\f15ad"}.mdi-message-bulleted:before{content:"\f06a2"}.mdi-message-bulleted-off:before{content:"\f06a3"}.mdi-message-check:before{content:"\f1b8a"}.mdi-message-check-outline:before{content:"\f1b8b"}.mdi-message-cog:before{content:"\f06f1"}.mdi-message-cog-outline:before{content:"\f1172"}.mdi-message-draw:before{content:"\f0363"}.mdi-message-fast:before{content:"\f19cc"}.mdi-message-fast-outline:before{content:"\f19cd"}.mdi-message-flash:before{content:"\f15a9"}.mdi-message-flash-outline:before{content:"\f15aa"}.mdi-message-image:before{content:"\f0364"}.mdi-message-image-outline:before{content:"\f116c"}.mdi-message-lock:before{content:"\f0fcc"}.mdi-message-lock-outline:before{content:"\f116d"}.mdi-message-minus:before{content:"\f116e"}.mdi-message-minus-outline:before{content:"\f116f"}.mdi-message-off:before{content:"\f164d"}.mdi-message-off-outline:before{content:"\f164e"}.mdi-message-outline:before{content:"\f0365"}.mdi-message-plus:before{content:"\f0653"}.mdi-message-plus-outline:before{content:"\f10bb"}.mdi-message-processing:before{content:"\f0366"}.mdi-message-processing-outline:before{content:"\f1170"}.mdi-message-question:before{content:"\f173a"}.mdi-message-question-outline:before{content:"\f173b"}.mdi-message-reply:before{content:"\f0367"}.mdi-message-reply-outline:before{content:"\f173d"}.mdi-message-reply-text:before{content:"\f0368"}.mdi-message-reply-text-outline:before{content:"\f173e"}.mdi-message-settings:before{content:"\f06f0"}.mdi-message-settings-outline:before{content:"\f1171"}.mdi-message-star:before{content:"\f069a"}.mdi-message-star-outline:before{content:"\f1250"}.mdi-message-text:before{content:"\f0369"}.mdi-message-text-clock:before{content:"\f1173"}.mdi-message-text-clock-outline:before{content:"\f1174"}.mdi-message-text-fast:before{content:"\f19ce"}.mdi-message-text-fast-outline:before{content:"\f19cf"}.mdi-message-text-lock:before{content:"\f0fcd"}.mdi-message-text-lock-outline:before{content:"\f1175"}.mdi-message-text-outline:before{content:"\f036a"}.mdi-message-video:before{content:"\f036b"}.mdi-meteor:before{content:"\f0629"}.mdi-meter-electric:before{content:"\f1a57"}.mdi-meter-electric-outline:before{content:"\f1a58"}.mdi-meter-gas:before{content:"\f1a59"}.mdi-meter-gas-outline:before{content:"\f1a5a"}.mdi-metronome:before{content:"\f07da"}.mdi-metronome-tick:before{content:"\f07db"}.mdi-micro-sd:before{content:"\f07dc"}.mdi-microphone:before{content:"\f036c"}.mdi-microphone-message:before{content:"\f050a"}.mdi-microphone-message-off:before{content:"\f050b"}.mdi-microphone-minus:before{content:"\f08b3"}.mdi-microphone-off:before{content:"\f036d"}.mdi-microphone-outline:before{content:"\f036e"}.mdi-microphone-plus:before{content:"\f08b4"}.mdi-microphone-question:before{content:"\f1989"}.mdi-microphone-question-outline:before{content:"\f198a"}.mdi-microphone-settings:before{content:"\f036f"}.mdi-microphone-variant:before{content:"\f0370"}.mdi-microphone-variant-off:before{content:"\f0371"}.mdi-microscope:before{content:"\f0654"}.mdi-microsoft:before{content:"\f0372"}.mdi-microsoft-access:before{content:"\f138e"}.mdi-microsoft-azure:before{content:"\f0805"}.mdi-microsoft-azure-devops:before{content:"\f0fd5"}.mdi-microsoft-bing:before{content:"\f00a4"}.mdi-microsoft-dynamics-365:before{content:"\f0988"}.mdi-microsoft-edge:before{content:"\f01e9"}.mdi-microsoft-excel:before{content:"\f138f"}.mdi-microsoft-internet-explorer:before{content:"\f0300"}.mdi-microsoft-office:before{content:"\f03c6"}.mdi-microsoft-onedrive:before{content:"\f03ca"}.mdi-microsoft-onenote:before{content:"\f0747"}.mdi-microsoft-outlook:before{content:"\f0d22"}.mdi-microsoft-powerpoint:before{content:"\f1390"}.mdi-microsoft-sharepoint:before{content:"\f1391"}.mdi-microsoft-teams:before{content:"\f02bb"}.mdi-microsoft-visual-studio:before{content:"\f0610"}.mdi-microsoft-visual-studio-code:before{content:"\f0a1e"}.mdi-microsoft-windows:before{content:"\f05b3"}.mdi-microsoft-windows-classic:before{content:"\f0a21"}.mdi-microsoft-word:before{content:"\f1392"}.mdi-microsoft-xbox:before{content:"\f05b9"}.mdi-microsoft-xbox-controller:before{content:"\f05ba"}.mdi-microsoft-xbox-controller-battery-alert:before{content:"\f074b"}.mdi-microsoft-xbox-controller-battery-charging:before{content:"\f0a22"}.mdi-microsoft-xbox-controller-battery-empty:before{content:"\f074c"}.mdi-microsoft-xbox-controller-battery-full:before{content:"\f074d"}.mdi-microsoft-xbox-controller-battery-low:before{content:"\f074e"}.mdi-microsoft-xbox-controller-battery-medium:before{content:"\f074f"}.mdi-microsoft-xbox-controller-battery-unknown:before{content:"\f0750"}.mdi-microsoft-xbox-controller-menu:before{content:"\f0e6f"}.mdi-microsoft-xbox-controller-off:before{content:"\f05bb"}.mdi-microsoft-xbox-controller-view:before{content:"\f0e70"}.mdi-microwave:before{content:"\f0c99"}.mdi-microwave-off:before{content:"\f1423"}.mdi-middleware:before{content:"\f0f5d"}.mdi-middleware-outline:before{content:"\f0f5e"}.mdi-midi:before{content:"\f08f1"}.mdi-midi-port:before{content:"\f08f2"}.mdi-mine:before{content:"\f0dda"}.mdi-minecraft:before{content:"\f0373"}.mdi-mini-sd:before{content:"\f0a05"}.mdi-minidisc:before{content:"\f0a06"}.mdi-minus:before{content:"\f0374"}.mdi-minus-box:before{content:"\f0375"}.mdi-minus-box-multiple:before{content:"\f1141"}.mdi-minus-box-multiple-outline:before{content:"\f1142"}.mdi-minus-box-outline:before{content:"\f06f2"}.mdi-minus-circle:before{content:"\f0376"}.mdi-minus-circle-multiple:before{content:"\f035a"}.mdi-minus-circle-multiple-outline:before{content:"\f0ad3"}.mdi-minus-circle-off:before{content:"\f1459"}.mdi-minus-circle-off-outline:before{content:"\f145a"}.mdi-minus-circle-outline:before{content:"\f0377"}.mdi-minus-network:before{content:"\f0378"}.mdi-minus-network-outline:before{content:"\f0c9a"}.mdi-minus-thick:before{content:"\f1639"}.mdi-mirror:before{content:"\f11fd"}.mdi-mirror-rectangle:before{content:"\f179f"}.mdi-mirror-variant:before{content:"\f17a0"}.mdi-mixed-martial-arts:before{content:"\f0d8f"}.mdi-mixed-reality:before{content:"\f087f"}.mdi-molecule:before{content:"\f0bac"}.mdi-molecule-co:before{content:"\f12fe"}.mdi-molecule-co2:before{content:"\f07e4"}.mdi-monitor:before{content:"\f0379"}.mdi-monitor-account:before{content:"\f1a5b"}.mdi-monitor-arrow-down:before{content:"\f19d0"}.mdi-monitor-arrow-down-variant:before{content:"\f19d1"}.mdi-monitor-cellphone:before{content:"\f0989"}.mdi-monitor-cellphone-star:before{content:"\f098a"}.mdi-monitor-dashboard:before{content:"\f0a07"}.mdi-monitor-edit:before{content:"\f12c6"}.mdi-monitor-eye:before{content:"\f13b4"}.mdi-monitor-lock:before{content:"\f0ddb"}.mdi-monitor-multiple:before{content:"\f037a"}.mdi-monitor-off:before{content:"\f0d90"}.mdi-monitor-screenshot:before{content:"\f0e51"}.mdi-monitor-share:before{content:"\f1483"}.mdi-monitor-shimmer:before{content:"\f1104"}.mdi-monitor-small:before{content:"\f1876"}.mdi-monitor-speaker:before{content:"\f0f5f"}.mdi-monitor-speaker-off:before{content:"\f0f60"}.mdi-monitor-star:before{content:"\f0ddc"}.mdi-monitor-vertical:before{content:"\f1c33"}.mdi-moon-first-quarter:before{content:"\f0f61"}.mdi-moon-full:before{content:"\f0f62"}.mdi-moon-last-quarter:before{content:"\f0f63"}.mdi-moon-new:before{content:"\f0f64"}.mdi-moon-waning-crescent:before{content:"\f0f65"}.mdi-moon-waning-gibbous:before{content:"\f0f66"}.mdi-moon-waxing-crescent:before{content:"\f0f67"}.mdi-moon-waxing-gibbous:before{content:"\f0f68"}.mdi-moped:before{content:"\f1086"}.mdi-moped-electric:before{content:"\f15b7"}.mdi-moped-electric-outline:before{content:"\f15b8"}.mdi-moped-outline:before{content:"\f15b9"}.mdi-more:before{content:"\f037b"}.mdi-mortar-pestle:before{content:"\f1748"}.mdi-mortar-pestle-plus:before{content:"\f03f1"}.mdi-mosque:before{content:"\f0d45"}.mdi-mosque-outline:before{content:"\f1827"}.mdi-mother-heart:before{content:"\f1314"}.mdi-mother-nurse:before{content:"\f0d21"}.mdi-motion:before{content:"\f15b2"}.mdi-motion-outline:before{content:"\f15b3"}.mdi-motion-pause:before{content:"\f1590"}.mdi-motion-pause-outline:before{content:"\f1592"}.mdi-motion-play:before{content:"\f158f"}.mdi-motion-play-outline:before{content:"\f1591"}.mdi-motion-sensor:before{content:"\f0d91"}.mdi-motion-sensor-off:before{content:"\f1435"}.mdi-motorbike:before{content:"\f037c"}.mdi-motorbike-electric:before{content:"\f15ba"}.mdi-motorbike-off:before{content:"\f1b16"}.mdi-mouse:before{content:"\f037d"}.mdi-mouse-bluetooth:before{content:"\f098b"}.mdi-mouse-left-click:before{content:"\f1d07"}.mdi-mouse-left-click-outline:before{content:"\f1d08"}.mdi-mouse-move-down:before{content:"\f1550"}.mdi-mouse-move-up:before{content:"\f1551"}.mdi-mouse-move-vertical:before{content:"\f1552"}.mdi-mouse-off:before{content:"\f037e"}.mdi-mouse-outline:before{content:"\f1d09"}.mdi-mouse-right-click:before{content:"\f1d0a"}.mdi-mouse-right-click-outline:before{content:"\f1d0b"}.mdi-mouse-scroll-wheel:before{content:"\f1d0c"}.mdi-mouse-variant:before{content:"\f037f"}.mdi-mouse-variant-off:before{content:"\f0380"}.mdi-move-resize:before{content:"\f0655"}.mdi-move-resize-variant:before{content:"\f0656"}.mdi-movie:before{content:"\f0381"}.mdi-movie-check:before{content:"\f16f3"}.mdi-movie-check-outline:before{content:"\f16f4"}.mdi-movie-cog:before{content:"\f16f5"}.mdi-movie-cog-outline:before{content:"\f16f6"}.mdi-movie-edit:before{content:"\f1122"}.mdi-movie-edit-outline:before{content:"\f1123"}.mdi-movie-filter:before{content:"\f1124"}.mdi-movie-filter-outline:before{content:"\f1125"}.mdi-movie-minus:before{content:"\f16f7"}.mdi-movie-minus-outline:before{content:"\f16f8"}.mdi-movie-off:before{content:"\f16f9"}.mdi-movie-off-outline:before{content:"\f16fa"}.mdi-movie-open:before{content:"\f0fce"}.mdi-movie-open-check:before{content:"\f16fb"}.mdi-movie-open-check-outline:before{content:"\f16fc"}.mdi-movie-open-cog:before{content:"\f16fd"}.mdi-movie-open-cog-outline:before{content:"\f16fe"}.mdi-movie-open-edit:before{content:"\f16ff"}.mdi-movie-open-edit-outline:before{content:"\f1700"}.mdi-movie-open-minus:before{content:"\f1701"}.mdi-movie-open-minus-outline:before{content:"\f1702"}.mdi-movie-open-off:before{content:"\f1703"}.mdi-movie-open-off-outline:before{content:"\f1704"}.mdi-movie-open-outline:before{content:"\f0fcf"}.mdi-movie-open-play:before{content:"\f1705"}.mdi-movie-open-play-outline:before{content:"\f1706"}.mdi-movie-open-plus:before{content:"\f1707"}.mdi-movie-open-plus-outline:before{content:"\f1708"}.mdi-movie-open-remove:before{content:"\f1709"}.mdi-movie-open-remove-outline:before{content:"\f170a"}.mdi-movie-open-settings:before{content:"\f170b"}.mdi-movie-open-settings-outline:before{content:"\f170c"}.mdi-movie-open-star:before{content:"\f170d"}.mdi-movie-open-star-outline:before{content:"\f170e"}.mdi-movie-outline:before{content:"\f0ddd"}.mdi-movie-play:before{content:"\f170f"}.mdi-movie-play-outline:before{content:"\f1710"}.mdi-movie-plus:before{content:"\f1711"}.mdi-movie-plus-outline:before{content:"\f1712"}.mdi-movie-remove:before{content:"\f1713"}.mdi-movie-remove-outline:before{content:"\f1714"}.mdi-movie-roll:before{content:"\f07de"}.mdi-movie-search:before{content:"\f11d2"}.mdi-movie-search-outline:before{content:"\f11d3"}.mdi-movie-settings:before{content:"\f1715"}.mdi-movie-settings-outline:before{content:"\f1716"}.mdi-movie-star:before{content:"\f1717"}.mdi-movie-star-outline:before{content:"\f1718"}.mdi-mower:before{content:"\f166f"}.mdi-mower-bag:before{content:"\f1670"}.mdi-mower-bag-on:before{content:"\f1b60"}.mdi-mower-on:before{content:"\f1b5f"}.mdi-muffin:before{content:"\f098c"}.mdi-multicast:before{content:"\f1893"}.mdi-multimedia:before{content:"\f1b97"}.mdi-multiplication:before{content:"\f0382"}.mdi-multiplication-box:before{content:"\f0383"}.mdi-mushroom:before{content:"\f07df"}.mdi-mushroom-off:before{content:"\f13fa"}.mdi-mushroom-off-outline:before{content:"\f13fb"}.mdi-mushroom-outline:before{content:"\f07e0"}.mdi-music:before{content:"\f075a"}.mdi-music-accidental-double-flat:before{content:"\f0f69"}.mdi-music-accidental-double-sharp:before{content:"\f0f6a"}.mdi-music-accidental-flat:before{content:"\f0f6b"}.mdi-music-accidental-natural:before{content:"\f0f6c"}.mdi-music-accidental-sharp:before{content:"\f0f6d"}.mdi-music-box:before{content:"\f0384"}.mdi-music-box-multiple:before{content:"\f0333"}.mdi-music-box-multiple-outline:before{content:"\f0f04"}.mdi-music-box-outline:before{content:"\f0385"}.mdi-music-circle:before{content:"\f0386"}.mdi-music-circle-outline:before{content:"\f0ad4"}.mdi-music-clef-alto:before{content:"\f0f6e"}.mdi-music-clef-bass:before{content:"\f0f6f"}.mdi-music-clef-treble:before{content:"\f0f70"}.mdi-music-note:before{content:"\f0387"}.mdi-music-note-bluetooth:before{content:"\f05fe"}.mdi-music-note-bluetooth-off:before{content:"\f05ff"}.mdi-music-note-eighth:before{content:"\f0388"}.mdi-music-note-eighth-dotted:before{content:"\f0f71"}.mdi-music-note-half:before{content:"\f0389"}.mdi-music-note-half-dotted:before{content:"\f0f72"}.mdi-music-note-minus:before{content:"\f1b89"}.mdi-music-note-off:before{content:"\f038a"}.mdi-music-note-off-outline:before{content:"\f0f73"}.mdi-music-note-outline:before{content:"\f0f74"}.mdi-music-note-plus:before{content:"\f0dde"}.mdi-music-note-quarter:before{content:"\f038b"}.mdi-music-note-quarter-dotted:before{content:"\f0f75"}.mdi-music-note-sixteenth:before{content:"\f038c"}.mdi-music-note-sixteenth-dotted:before{content:"\f0f76"}.mdi-music-note-whole:before{content:"\f038d"}.mdi-music-note-whole-dotted:before{content:"\f0f77"}.mdi-music-off:before{content:"\f075b"}.mdi-music-rest-eighth:before{content:"\f0f78"}.mdi-music-rest-half:before{content:"\f0f79"}.mdi-music-rest-quarter:before{content:"\f0f7a"}.mdi-music-rest-sixteenth:before{content:"\f0f7b"}.mdi-music-rest-whole:before{content:"\f0f7c"}.mdi-mustache:before{content:"\f15de"}.mdi-nail:before{content:"\f0ddf"}.mdi-nas:before{content:"\f08f3"}.mdi-nativescript:before{content:"\f0880"}.mdi-nature:before{content:"\f038e"}.mdi-nature-outline:before{content:"\f1c71"}.mdi-nature-people:before{content:"\f038f"}.mdi-nature-people-outline:before{content:"\f1c72"}.mdi-navigation:before{content:"\f0390"}.mdi-navigation-outline:before{content:"\f1607"}.mdi-navigation-variant:before{content:"\f18f0"}.mdi-navigation-variant-outline:before{content:"\f18f1"}.mdi-near-me:before{content:"\f05cd"}.mdi-necklace:before{content:"\f0f0b"}.mdi-needle:before{content:"\f0391"}.mdi-needle-off:before{content:"\f19d2"}.mdi-netflix:before{content:"\f0746"}.mdi-network:before{content:"\f06f3"}.mdi-network-off:before{content:"\f0c9b"}.mdi-network-off-outline:before{content:"\f0c9c"}.mdi-network-outline:before{content:"\f0c9d"}.mdi-network-pos:before{content:"\f1acb"}.mdi-network-strength-1:before{content:"\f08f4"}.mdi-network-strength-1-alert:before{content:"\f08f5"}.mdi-network-strength-2:before{content:"\f08f6"}.mdi-network-strength-2-alert:before{content:"\f08f7"}.mdi-network-strength-3:before{content:"\f08f8"}.mdi-network-strength-3-alert:before{content:"\f08f9"}.mdi-network-strength-4:before{content:"\f08fa"}.mdi-network-strength-4-alert:before{content:"\f08fb"}.mdi-network-strength-4-cog:before{content:"\f191a"}.mdi-network-strength-off:before{content:"\f08fc"}.mdi-network-strength-off-outline:before{content:"\f08fd"}.mdi-network-strength-outline:before{content:"\f08fe"}.mdi-new-box:before{content:"\f0394"}.mdi-newspaper:before{content:"\f0395"}.mdi-newspaper-check:before{content:"\f1943"}.mdi-newspaper-minus:before{content:"\f0f0c"}.mdi-newspaper-plus:before{content:"\f0f0d"}.mdi-newspaper-remove:before{content:"\f1944"}.mdi-newspaper-variant:before{content:"\f1001"}.mdi-newspaper-variant-multiple:before{content:"\f1002"}.mdi-newspaper-variant-multiple-outline:before{content:"\f1003"}.mdi-newspaper-variant-outline:before{content:"\f1004"}.mdi-nfc:before{content:"\f0396"}.mdi-nfc-search-variant:before{content:"\f0e53"}.mdi-nfc-tap:before{content:"\f0397"}.mdi-nfc-variant:before{content:"\f0398"}.mdi-nfc-variant-off:before{content:"\f0e54"}.mdi-ninja:before{content:"\f0774"}.mdi-nintendo-game-boy:before{content:"\f1393"}.mdi-nintendo-switch:before{content:"\f07e1"}.mdi-nintendo-wii:before{content:"\f05ab"}.mdi-nintendo-wiiu:before{content:"\f072d"}.mdi-nix:before{content:"\f1105"}.mdi-nodejs:before{content:"\f0399"}.mdi-noodles:before{content:"\f117e"}.mdi-not-equal:before{content:"\f098d"}.mdi-not-equal-variant:before{content:"\f098e"}.mdi-note:before{content:"\f039a"}.mdi-note-alert:before{content:"\f177d"}.mdi-note-alert-outline:before{content:"\f177e"}.mdi-note-check:before{content:"\f177f"}.mdi-note-check-outline:before{content:"\f1780"}.mdi-note-edit:before{content:"\f1781"}.mdi-note-edit-outline:before{content:"\f1782"}.mdi-note-minus:before{content:"\f164f"}.mdi-note-minus-outline:before{content:"\f1650"}.mdi-note-multiple:before{content:"\f06b8"}.mdi-note-multiple-outline:before{content:"\f06b9"}.mdi-note-off:before{content:"\f1783"}.mdi-note-off-outline:before{content:"\f1784"}.mdi-note-outline:before{content:"\f039b"}.mdi-note-plus:before{content:"\f039c"}.mdi-note-plus-outline:before{content:"\f039d"}.mdi-note-remove:before{content:"\f1651"}.mdi-note-remove-outline:before{content:"\f1652"}.mdi-note-search:before{content:"\f1653"}.mdi-note-search-outline:before{content:"\f1654"}.mdi-note-text:before{content:"\f039e"}.mdi-note-text-outline:before{content:"\f11d7"}.mdi-notebook:before{content:"\f082e"}.mdi-notebook-check:before{content:"\f14f5"}.mdi-notebook-check-outline:before{content:"\f14f6"}.mdi-notebook-edit:before{content:"\f14e7"}.mdi-notebook-edit-outline:before{content:"\f14e9"}.mdi-notebook-heart:before{content:"\f1a0b"}.mdi-notebook-heart-outline:before{content:"\f1a0c"}.mdi-notebook-minus:before{content:"\f1610"}.mdi-notebook-minus-outline:before{content:"\f1611"}.mdi-notebook-multiple:before{content:"\f0e55"}.mdi-notebook-outline:before{content:"\f0ebf"}.mdi-notebook-plus:before{content:"\f1612"}.mdi-notebook-plus-outline:before{content:"\f1613"}.mdi-notebook-remove:before{content:"\f1614"}.mdi-notebook-remove-outline:before{content:"\f1615"}.mdi-notification-clear-all:before{content:"\f039f"}.mdi-npm:before{content:"\f06f7"}.mdi-nuke:before{content:"\f06a4"}.mdi-null:before{content:"\f07e2"}.mdi-numeric:before{content:"\f03a0"}.mdi-numeric-0:before{content:"\f0b39"}.mdi-numeric-0-box:before{content:"\f03a1"}.mdi-numeric-0-box-multiple:before{content:"\f0f0e"}.mdi-numeric-0-box-multiple-outline:before{content:"\f03a2"}.mdi-numeric-0-box-outline:before{content:"\f03a3"}.mdi-numeric-0-circle:before{content:"\f0c9e"}.mdi-numeric-0-circle-outline:before{content:"\f0c9f"}.mdi-numeric-1:before{content:"\f0b3a"}.mdi-numeric-1-box:before{content:"\f03a4"}.mdi-numeric-1-box-multiple:before{content:"\f0f0f"}.mdi-numeric-1-box-multiple-outline:before{content:"\f03a5"}.mdi-numeric-1-box-outline:before{content:"\f03a6"}.mdi-numeric-1-circle:before{content:"\f0ca0"}.mdi-numeric-1-circle-outline:before{content:"\f0ca1"}.mdi-numeric-10:before{content:"\f0fe9"}.mdi-numeric-10-box:before{content:"\f0f7d"}.mdi-numeric-10-box-multiple:before{content:"\f0fea"}.mdi-numeric-10-box-multiple-outline:before{content:"\f0feb"}.mdi-numeric-10-box-outline:before{content:"\f0f7e"}.mdi-numeric-10-circle:before{content:"\f0fec"}.mdi-numeric-10-circle-outline:before{content:"\f0fed"}.mdi-numeric-2:before{content:"\f0b3b"}.mdi-numeric-2-box:before{content:"\f03a7"}.mdi-numeric-2-box-multiple:before{content:"\f0f10"}.mdi-numeric-2-box-multiple-outline:before{content:"\f03a8"}.mdi-numeric-2-box-outline:before{content:"\f03a9"}.mdi-numeric-2-circle:before{content:"\f0ca2"}.mdi-numeric-2-circle-outline:before{content:"\f0ca3"}.mdi-numeric-3:before{content:"\f0b3c"}.mdi-numeric-3-box:before{content:"\f03aa"}.mdi-numeric-3-box-multiple:before{content:"\f0f11"}.mdi-numeric-3-box-multiple-outline:before{content:"\f03ab"}.mdi-numeric-3-box-outline:before{content:"\f03ac"}.mdi-numeric-3-circle:before{content:"\f0ca4"}.mdi-numeric-3-circle-outline:before{content:"\f0ca5"}.mdi-numeric-4:before{content:"\f0b3d"}.mdi-numeric-4-box:before{content:"\f03ad"}.mdi-numeric-4-box-multiple:before{content:"\f0f12"}.mdi-numeric-4-box-multiple-outline:before{content:"\f03b2"}.mdi-numeric-4-box-outline:before{content:"\f03ae"}.mdi-numeric-4-circle:before{content:"\f0ca6"}.mdi-numeric-4-circle-outline:before{content:"\f0ca7"}.mdi-numeric-5:before{content:"\f0b3e"}.mdi-numeric-5-box:before{content:"\f03b1"}.mdi-numeric-5-box-multiple:before{content:"\f0f13"}.mdi-numeric-5-box-multiple-outline:before{content:"\f03af"}.mdi-numeric-5-box-outline:before{content:"\f03b0"}.mdi-numeric-5-circle:before{content:"\f0ca8"}.mdi-numeric-5-circle-outline:before{content:"\f0ca9"}.mdi-numeric-6:before{content:"\f0b3f"}.mdi-numeric-6-box:before{content:"\f03b3"}.mdi-numeric-6-box-multiple:before{content:"\f0f14"}.mdi-numeric-6-box-multiple-outline:before{content:"\f03b4"}.mdi-numeric-6-box-outline:before{content:"\f03b5"}.mdi-numeric-6-circle:before{content:"\f0caa"}.mdi-numeric-6-circle-outline:before{content:"\f0cab"}.mdi-numeric-7:before{content:"\f0b40"}.mdi-numeric-7-box:before{content:"\f03b6"}.mdi-numeric-7-box-multiple:before{content:"\f0f15"}.mdi-numeric-7-box-multiple-outline:before{content:"\f03b7"}.mdi-numeric-7-box-outline:before{content:"\f03b8"}.mdi-numeric-7-circle:before{content:"\f0cac"}.mdi-numeric-7-circle-outline:before{content:"\f0cad"}.mdi-numeric-8:before{content:"\f0b41"}.mdi-numeric-8-box:before{content:"\f03b9"}.mdi-numeric-8-box-multiple:before{content:"\f0f16"}.mdi-numeric-8-box-multiple-outline:before{content:"\f03ba"}.mdi-numeric-8-box-outline:before{content:"\f03bb"}.mdi-numeric-8-circle:before{content:"\f0cae"}.mdi-numeric-8-circle-outline:before{content:"\f0caf"}.mdi-numeric-9:before{content:"\f0b42"}.mdi-numeric-9-box:before{content:"\f03bc"}.mdi-numeric-9-box-multiple:before{content:"\f0f17"}.mdi-numeric-9-box-multiple-outline:before{content:"\f03bd"}.mdi-numeric-9-box-outline:before{content:"\f03be"}.mdi-numeric-9-circle:before{content:"\f0cb0"}.mdi-numeric-9-circle-outline:before{content:"\f0cb1"}.mdi-numeric-9-plus:before{content:"\f0fee"}.mdi-numeric-9-plus-box:before{content:"\f03bf"}.mdi-numeric-9-plus-box-multiple:before{content:"\f0f18"}.mdi-numeric-9-plus-box-multiple-outline:before{content:"\f03c0"}.mdi-numeric-9-plus-box-outline:before{content:"\f03c1"}.mdi-numeric-9-plus-circle:before{content:"\f0cb2"}.mdi-numeric-9-plus-circle-outline:before{content:"\f0cb3"}.mdi-numeric-negative-1:before{content:"\f1052"}.mdi-numeric-off:before{content:"\f19d3"}.mdi-numeric-positive-1:before{content:"\f15cb"}.mdi-nut:before{content:"\f06f8"}.mdi-nutrition:before{content:"\f03c2"}.mdi-nuxt:before{content:"\f1106"}.mdi-oar:before{content:"\f067c"}.mdi-ocarina:before{content:"\f0de0"}.mdi-oci:before{content:"\f12e9"}.mdi-ocr:before{content:"\f113a"}.mdi-octagon:before{content:"\f03c3"}.mdi-octagon-outline:before{content:"\f03c4"}.mdi-octagram:before{content:"\f06f9"}.mdi-octagram-edit:before{content:"\f1c34"}.mdi-octagram-edit-outline:before{content:"\f1c35"}.mdi-octagram-minus:before{content:"\f1c36"}.mdi-octagram-minus-outline:before{content:"\f1c37"}.mdi-octagram-outline:before{content:"\f0775"}.mdi-octagram-plus:before{content:"\f1c38"}.mdi-octagram-plus-outline:before{content:"\f1c39"}.mdi-octahedron:before{content:"\f1950"}.mdi-octahedron-off:before{content:"\f1951"}.mdi-odnoklassniki:before{content:"\f03c5"}.mdi-offer:before{content:"\f121b"}.mdi-office-building:before{content:"\f0991"}.mdi-office-building-cog:before{content:"\f1949"}.mdi-office-building-cog-outline:before{content:"\f194a"}.mdi-office-building-marker:before{content:"\f1520"}.mdi-office-building-marker-outline:before{content:"\f1521"}.mdi-office-building-minus:before{content:"\f1baa"}.mdi-office-building-minus-outline:before{content:"\f1bab"}.mdi-office-building-outline:before{content:"\f151f"}.mdi-office-building-plus:before{content:"\f1ba8"}.mdi-office-building-plus-outline:before{content:"\f1ba9"}.mdi-office-building-remove:before{content:"\f1bac"}.mdi-office-building-remove-outline:before{content:"\f1bad"}.mdi-oil:before{content:"\f03c7"}.mdi-oil-lamp:before{content:"\f0f19"}.mdi-oil-level:before{content:"\f1053"}.mdi-oil-temperature:before{content:"\f0ff8"}.mdi-om:before{content:"\f0973"}.mdi-omega:before{content:"\f03c9"}.mdi-one-up:before{content:"\f0bad"}.mdi-onepassword:before{content:"\f0881"}.mdi-opacity:before{content:"\f05cc"}.mdi-open-in-app:before{content:"\f03cb"}.mdi-open-in-new:before{content:"\f03cc"}.mdi-open-source-initiative:before{content:"\f0bae"}.mdi-openid:before{content:"\f03cd"}.mdi-opera:before{content:"\f03ce"}.mdi-orbit:before{content:"\f0018"}.mdi-orbit-variant:before{content:"\f15db"}.mdi-order-alphabetical-ascending:before{content:"\f020d"}.mdi-order-alphabetical-descending:before{content:"\f0d07"}.mdi-order-bool-ascending:before{content:"\f02be"}.mdi-order-bool-ascending-variant:before{content:"\f098f"}.mdi-order-bool-descending:before{content:"\f1384"}.mdi-order-bool-descending-variant:before{content:"\f0990"}.mdi-order-numeric-ascending:before{content:"\f0545"}.mdi-order-numeric-descending:before{content:"\f0546"}.mdi-origin:before{content:"\f0b43"}.mdi-ornament:before{content:"\f03cf"}.mdi-ornament-variant:before{content:"\f03d0"}.mdi-outdoor-lamp:before{content:"\f1054"}.mdi-overscan:before{content:"\f1005"}.mdi-owl:before{content:"\f03d2"}.mdi-pac-man:before{content:"\f0baf"}.mdi-package:before{content:"\f03d3"}.mdi-package-check:before{content:"\f1b51"}.mdi-package-down:before{content:"\f03d4"}.mdi-package-up:before{content:"\f03d5"}.mdi-package-variant:before{content:"\f03d6"}.mdi-package-variant-closed:before{content:"\f03d7"}.mdi-package-variant-closed-check:before{content:"\f1b52"}.mdi-package-variant-closed-minus:before{content:"\f19d4"}.mdi-package-variant-closed-plus:before{content:"\f19d5"}.mdi-package-variant-closed-remove:before{content:"\f19d6"}.mdi-package-variant-minus:before{content:"\f19d7"}.mdi-package-variant-plus:before{content:"\f19d8"}.mdi-package-variant-remove:before{content:"\f19d9"}.mdi-page-first:before{content:"\f0600"}.mdi-page-last:before{content:"\f0601"}.mdi-page-layout-body:before{content:"\f06fa"}.mdi-page-layout-footer:before{content:"\f06fb"}.mdi-page-layout-header:before{content:"\f06fc"}.mdi-page-layout-header-footer:before{content:"\f0f7f"}.mdi-page-layout-sidebar-left:before{content:"\f06fd"}.mdi-page-layout-sidebar-right:before{content:"\f06fe"}.mdi-page-next:before{content:"\f0bb0"}.mdi-page-next-outline:before{content:"\f0bb1"}.mdi-page-previous:before{content:"\f0bb2"}.mdi-page-previous-outline:before{content:"\f0bb3"}.mdi-pail:before{content:"\f1417"}.mdi-pail-minus:before{content:"\f1437"}.mdi-pail-minus-outline:before{content:"\f143c"}.mdi-pail-off:before{content:"\f1439"}.mdi-pail-off-outline:before{content:"\f143e"}.mdi-pail-outline:before{content:"\f143a"}.mdi-pail-plus:before{content:"\f1436"}.mdi-pail-plus-outline:before{content:"\f143b"}.mdi-pail-remove:before{content:"\f1438"}.mdi-pail-remove-outline:before{content:"\f143d"}.mdi-palette:before{content:"\f03d8"}.mdi-palette-advanced:before{content:"\f03d9"}.mdi-palette-outline:before{content:"\f0e0c"}.mdi-palette-swatch:before{content:"\f08b5"}.mdi-palette-swatch-outline:before{content:"\f135c"}.mdi-palette-swatch-variant:before{content:"\f195a"}.mdi-palm-tree:before{content:"\f1055"}.mdi-pan:before{content:"\f0bb4"}.mdi-pan-bottom-left:before{content:"\f0bb5"}.mdi-pan-bottom-right:before{content:"\f0bb6"}.mdi-pan-down:before{content:"\f0bb7"}.mdi-pan-horizontal:before{content:"\f0bb8"}.mdi-pan-left:before{content:"\f0bb9"}.mdi-pan-right:before{content:"\f0bba"}.mdi-pan-top-left:before{content:"\f0bbb"}.mdi-pan-top-right:before{content:"\f0bbc"}.mdi-pan-up:before{content:"\f0bbd"}.mdi-pan-vertical:before{content:"\f0bbe"}.mdi-panda:before{content:"\f03da"}.mdi-pandora:before{content:"\f03db"}.mdi-panorama:before{content:"\f03dc"}.mdi-panorama-fisheye:before{content:"\f03dd"}.mdi-panorama-horizontal:before{content:"\f1928"}.mdi-panorama-horizontal-outline:before{content:"\f03de"}.mdi-panorama-outline:before{content:"\f198c"}.mdi-panorama-sphere:before{content:"\f198d"}.mdi-panorama-sphere-outline:before{content:"\f198e"}.mdi-panorama-variant:before{content:"\f198f"}.mdi-panorama-variant-outline:before{content:"\f1990"}.mdi-panorama-vertical:before{content:"\f1929"}.mdi-panorama-vertical-outline:before{content:"\f03df"}.mdi-panorama-wide-angle:before{content:"\f195f"}.mdi-panorama-wide-angle-outline:before{content:"\f03e0"}.mdi-paper-cut-vertical:before{content:"\f03e1"}.mdi-paper-roll:before{content:"\f1157"}.mdi-paper-roll-outline:before{content:"\f1158"}.mdi-paperclip:before{content:"\f03e2"}.mdi-paperclip-check:before{content:"\f1ac6"}.mdi-paperclip-lock:before{content:"\f19da"}.mdi-paperclip-minus:before{content:"\f1ac7"}.mdi-paperclip-off:before{content:"\f1ac8"}.mdi-paperclip-plus:before{content:"\f1ac9"}.mdi-paperclip-remove:before{content:"\f1aca"}.mdi-parachute:before{content:"\f0cb4"}.mdi-parachute-outline:before{content:"\f0cb5"}.mdi-paragliding:before{content:"\f1745"}.mdi-parking:before{content:"\f03e3"}.mdi-party-popper:before{content:"\f1056"}.mdi-passport:before{content:"\f07e3"}.mdi-passport-alert:before{content:"\f1cb8"}.mdi-passport-biometric:before{content:"\f0de1"}.mdi-passport-cancel:before{content:"\f1cb9"}.mdi-passport-check:before{content:"\f1cba"}.mdi-passport-minus:before{content:"\f1cbb"}.mdi-passport-plus:before{content:"\f1cbc"}.mdi-passport-remove:before{content:"\f1cbd"}.mdi-pasta:before{content:"\f1160"}.mdi-patio-heater:before{content:"\f0f80"}.mdi-patreon:before{content:"\f0882"}.mdi-pause:before{content:"\f03e4"}.mdi-pause-box:before{content:"\f00bc"}.mdi-pause-box-outline:before{content:"\f1b7a"}.mdi-pause-circle:before{content:"\f03e5"}.mdi-pause-circle-outline:before{content:"\f03e6"}.mdi-pause-octagon:before{content:"\f03e7"}.mdi-pause-octagon-outline:before{content:"\f03e8"}.mdi-paw:before{content:"\f03e9"}.mdi-paw-off:before{content:"\f0657"}.mdi-paw-off-outline:before{content:"\f1676"}.mdi-paw-outline:before{content:"\f1675"}.mdi-peace:before{content:"\f0884"}.mdi-peanut:before{content:"\f0ffc"}.mdi-peanut-off:before{content:"\f0ffd"}.mdi-peanut-off-outline:before{content:"\f0fff"}.mdi-peanut-outline:before{content:"\f0ffe"}.mdi-pen:before{content:"\f03ea"}.mdi-pen-lock:before{content:"\f0de2"}.mdi-pen-minus:before{content:"\f0de3"}.mdi-pen-off:before{content:"\f0de4"}.mdi-pen-plus:before{content:"\f0de5"}.mdi-pen-remove:before{content:"\f0de6"}.mdi-pencil:before{content:"\f03eb"}.mdi-pencil-box:before{content:"\f03ec"}.mdi-pencil-box-multiple:before{content:"\f1144"}.mdi-pencil-box-multiple-outline:before{content:"\f1145"}.mdi-pencil-box-outline:before{content:"\f03ed"}.mdi-pencil-circle:before{content:"\f06ff"}.mdi-pencil-circle-outline:before{content:"\f0776"}.mdi-pencil-lock:before{content:"\f03ee"}.mdi-pencil-lock-outline:before{content:"\f0de7"}.mdi-pencil-minus:before{content:"\f0de8"}.mdi-pencil-minus-outline:before{content:"\f0de9"}.mdi-pencil-off:before{content:"\f03ef"}.mdi-pencil-off-outline:before{content:"\f0dea"}.mdi-pencil-outline:before{content:"\f0cb6"}.mdi-pencil-plus:before{content:"\f0deb"}.mdi-pencil-plus-outline:before{content:"\f0dec"}.mdi-pencil-remove:before{content:"\f0ded"}.mdi-pencil-remove-outline:before{content:"\f0dee"}.mdi-pencil-ruler:before{content:"\f1353"}.mdi-pencil-ruler-outline:before{content:"\f1c11"}.mdi-penguin:before{content:"\f0ec0"}.mdi-pentagon:before{content:"\f0701"}.mdi-pentagon-outline:before{content:"\f0700"}.mdi-pentagram:before{content:"\f1667"}.mdi-percent:before{content:"\f03f0"}.mdi-percent-box:before{content:"\f1a02"}.mdi-percent-box-outline:before{content:"\f1a03"}.mdi-percent-circle:before{content:"\f1a04"}.mdi-percent-circle-outline:before{content:"\f1a05"}.mdi-percent-outline:before{content:"\f1278"}.mdi-periodic-table:before{content:"\f08b6"}.mdi-perspective-less:before{content:"\f0d23"}.mdi-perspective-more:before{content:"\f0d24"}.mdi-ph:before{content:"\f17c5"}.mdi-phone:before{content:"\f03f2"}.mdi-phone-alert:before{content:"\f0f1a"}.mdi-phone-alert-outline:before{content:"\f118e"}.mdi-phone-bluetooth:before{content:"\f03f3"}.mdi-phone-bluetooth-outline:before{content:"\f118f"}.mdi-phone-cancel:before{content:"\f10bc"}.mdi-phone-cancel-outline:before{content:"\f1190"}.mdi-phone-check:before{content:"\f11a9"}.mdi-phone-check-outline:before{content:"\f11aa"}.mdi-phone-classic:before{content:"\f0602"}.mdi-phone-classic-off:before{content:"\f1279"}.mdi-phone-clock:before{content:"\f19db"}.mdi-phone-dial:before{content:"\f1559"}.mdi-phone-dial-outline:before{content:"\f155a"}.mdi-phone-forward:before{content:"\f03f4"}.mdi-phone-forward-outline:before{content:"\f1191"}.mdi-phone-hangup:before{content:"\f03f5"}.mdi-phone-hangup-outline:before{content:"\f1192"}.mdi-phone-in-talk:before{content:"\f03f6"}.mdi-phone-in-talk-outline:before{content:"\f1182"}.mdi-phone-incoming:before{content:"\f03f7"}.mdi-phone-incoming-outgoing:before{content:"\f1b3f"}.mdi-phone-incoming-outgoing-outline:before{content:"\f1b40"}.mdi-phone-incoming-outline:before{content:"\f1193"}.mdi-phone-lock:before{content:"\f03f8"}.mdi-phone-lock-outline:before{content:"\f1194"}.mdi-phone-log:before{content:"\f03f9"}.mdi-phone-log-outline:before{content:"\f1195"}.mdi-phone-message:before{content:"\f1196"}.mdi-phone-message-outline:before{content:"\f1197"}.mdi-phone-minus:before{content:"\f0658"}.mdi-phone-minus-outline:before{content:"\f1198"}.mdi-phone-missed:before{content:"\f03fa"}.mdi-phone-missed-outline:before{content:"\f11a5"}.mdi-phone-off:before{content:"\f0def"}.mdi-phone-off-outline:before{content:"\f11a6"}.mdi-phone-outgoing:before{content:"\f03fb"}.mdi-phone-outgoing-outline:before{content:"\f1199"}.mdi-phone-outline:before{content:"\f0df0"}.mdi-phone-paused:before{content:"\f03fc"}.mdi-phone-paused-outline:before{content:"\f119a"}.mdi-phone-plus:before{content:"\f0659"}.mdi-phone-plus-outline:before{content:"\f119b"}.mdi-phone-refresh:before{content:"\f1993"}.mdi-phone-refresh-outline:before{content:"\f1994"}.mdi-phone-remove:before{content:"\f152f"}.mdi-phone-remove-outline:before{content:"\f1530"}.mdi-phone-return:before{content:"\f082f"}.mdi-phone-return-outline:before{content:"\f119c"}.mdi-phone-ring:before{content:"\f11ab"}.mdi-phone-ring-outline:before{content:"\f11ac"}.mdi-phone-rotate-landscape:before{content:"\f0885"}.mdi-phone-rotate-portrait:before{content:"\f0886"}.mdi-phone-settings:before{content:"\f03fd"}.mdi-phone-settings-outline:before{content:"\f119d"}.mdi-phone-sync:before{content:"\f1995"}.mdi-phone-sync-outline:before{content:"\f1996"}.mdi-phone-voip:before{content:"\f03fe"}.mdi-pi:before{content:"\f03ff"}.mdi-pi-box:before{content:"\f0400"}.mdi-pi-hole:before{content:"\f0df1"}.mdi-piano:before{content:"\f067d"}.mdi-piano-off:before{content:"\f0698"}.mdi-pickaxe:before{content:"\f08b7"}.mdi-picture-in-picture-bottom-right:before{content:"\f0e57"}.mdi-picture-in-picture-bottom-right-outline:before{content:"\f0e58"}.mdi-picture-in-picture-top-right:before{content:"\f0e59"}.mdi-picture-in-picture-top-right-outline:before{content:"\f0e5a"}.mdi-pier:before{content:"\f0887"}.mdi-pier-crane:before{content:"\f0888"}.mdi-pig:before{content:"\f0401"}.mdi-pig-variant:before{content:"\f1006"}.mdi-pig-variant-outline:before{content:"\f1678"}.mdi-piggy-bank:before{content:"\f1007"}.mdi-piggy-bank-outline:before{content:"\f1679"}.mdi-pill:before{content:"\f0402"}.mdi-pill-multiple:before{content:"\f1b4c"}.mdi-pill-off:before{content:"\f1a5c"}.mdi-pillar:before{content:"\f0702"}.mdi-pin:before{content:"\f0403"}.mdi-pin-off:before{content:"\f0404"}.mdi-pin-off-outline:before{content:"\f0930"}.mdi-pin-outline:before{content:"\f0931"}.mdi-pine-tree:before{content:"\f0405"}.mdi-pine-tree-box:before{content:"\f0406"}.mdi-pine-tree-fire:before{content:"\f141a"}.mdi-pine-tree-variant:before{content:"\f1c73"}.mdi-pine-tree-variant-outline:before{content:"\f1c74"}.mdi-pinterest:before{content:"\f0407"}.mdi-pinwheel:before{content:"\f0ad5"}.mdi-pinwheel-outline:before{content:"\f0ad6"}.mdi-pipe:before{content:"\f07e5"}.mdi-pipe-disconnected:before{content:"\f07e6"}.mdi-pipe-leak:before{content:"\f0889"}.mdi-pipe-valve:before{content:"\f184d"}.mdi-pipe-wrench:before{content:"\f1354"}.mdi-pirate:before{content:"\f0a08"}.mdi-pistol:before{content:"\f0703"}.mdi-piston:before{content:"\f088a"}.mdi-pitchfork:before{content:"\f1553"}.mdi-pizza:before{content:"\f0409"}.mdi-plane-car:before{content:"\f1aff"}.mdi-plane-train:before{content:"\f1b00"}.mdi-play:before{content:"\f040a"}.mdi-play-box:before{content:"\f127a"}.mdi-play-box-edit-outline:before{content:"\f1c3a"}.mdi-play-box-lock:before{content:"\f1a16"}.mdi-play-box-lock-open:before{content:"\f1a17"}.mdi-play-box-lock-open-outline:before{content:"\f1a18"}.mdi-play-box-lock-outline:before{content:"\f1a19"}.mdi-play-box-multiple:before{content:"\f0d19"}.mdi-play-box-multiple-outline:before{content:"\f13e6"}.mdi-play-box-outline:before{content:"\f040b"}.mdi-play-circle:before{content:"\f040c"}.mdi-play-circle-outline:before{content:"\f040d"}.mdi-play-network:before{content:"\f088b"}.mdi-play-network-outline:before{content:"\f0cb7"}.mdi-play-outline:before{content:"\f0f1b"}.mdi-play-pause:before{content:"\f040e"}.mdi-play-protected-content:before{content:"\f040f"}.mdi-play-speed:before{content:"\f08ff"}.mdi-playlist-check:before{content:"\f05c7"}.mdi-playlist-edit:before{content:"\f0900"}.mdi-playlist-minus:before{content:"\f0410"}.mdi-playlist-music:before{content:"\f0cb8"}.mdi-playlist-music-outline:before{content:"\f0cb9"}.mdi-playlist-play:before{content:"\f0411"}.mdi-playlist-plus:before{content:"\f0412"}.mdi-playlist-remove:before{content:"\f0413"}.mdi-playlist-star:before{content:"\f0df2"}.mdi-plex:before{content:"\f06ba"}.mdi-pliers:before{content:"\f19a4"}.mdi-plus:before{content:"\f0415"}.mdi-plus-box:before{content:"\f0416"}.mdi-plus-box-multiple:before{content:"\f0334"}.mdi-plus-box-multiple-outline:before{content:"\f1143"}.mdi-plus-box-outline:before{content:"\f0704"}.mdi-plus-circle:before{content:"\f0417"}.mdi-plus-circle-multiple:before{content:"\f034c"}.mdi-plus-circle-multiple-outline:before{content:"\f0418"}.mdi-plus-circle-outline:before{content:"\f0419"}.mdi-plus-lock:before{content:"\f1a5d"}.mdi-plus-lock-open:before{content:"\f1a5e"}.mdi-plus-minus:before{content:"\f0992"}.mdi-plus-minus-box:before{content:"\f0993"}.mdi-plus-minus-variant:before{content:"\f14c9"}.mdi-plus-network:before{content:"\f041a"}.mdi-plus-network-outline:before{content:"\f0cba"}.mdi-plus-outline:before{content:"\f0705"}.mdi-plus-thick:before{content:"\f11ec"}.mdi-pocket:before{content:"\f1cbe"}.mdi-podcast:before{content:"\f0994"}.mdi-podium:before{content:"\f0d25"}.mdi-podium-bronze:before{content:"\f0d26"}.mdi-podium-gold:before{content:"\f0d27"}.mdi-podium-silver:before{content:"\f0d28"}.mdi-point-of-sale:before{content:"\f0d92"}.mdi-pokeball:before{content:"\f041d"}.mdi-pokemon-go:before{content:"\f0a09"}.mdi-poker-chip:before{content:"\f0830"}.mdi-polaroid:before{content:"\f041e"}.mdi-police-badge:before{content:"\f1167"}.mdi-police-badge-outline:before{content:"\f1168"}.mdi-police-station:before{content:"\f1839"}.mdi-poll:before{content:"\f041f"}.mdi-polo:before{content:"\f14c3"}.mdi-polymer:before{content:"\f0421"}.mdi-pool:before{content:"\f0606"}.mdi-pool-thermometer:before{content:"\f1a5f"}.mdi-popcorn:before{content:"\f0422"}.mdi-post:before{content:"\f1008"}.mdi-post-lamp:before{content:"\f1a60"}.mdi-post-outline:before{content:"\f1009"}.mdi-postage-stamp:before{content:"\f0cbb"}.mdi-pot:before{content:"\f02e5"}.mdi-pot-mix:before{content:"\f065b"}.mdi-pot-mix-outline:before{content:"\f0677"}.mdi-pot-outline:before{content:"\f02ff"}.mdi-pot-steam:before{content:"\f065a"}.mdi-pot-steam-outline:before{content:"\f0326"}.mdi-pound:before{content:"\f0423"}.mdi-pound-box:before{content:"\f0424"}.mdi-pound-box-outline:before{content:"\f117f"}.mdi-power:before{content:"\f0425"}.mdi-power-cycle:before{content:"\f0901"}.mdi-power-off:before{content:"\f0902"}.mdi-power-on:before{content:"\f0903"}.mdi-power-plug:before{content:"\f06a5"}.mdi-power-plug-battery:before{content:"\f1c3b"}.mdi-power-plug-battery-outline:before{content:"\f1c3c"}.mdi-power-plug-off:before{content:"\f06a6"}.mdi-power-plug-off-outline:before{content:"\f1424"}.mdi-power-plug-outline:before{content:"\f1425"}.mdi-power-settings:before{content:"\f0426"}.mdi-power-sleep:before{content:"\f0904"}.mdi-power-socket:before{content:"\f0427"}.mdi-power-socket-au:before{content:"\f0905"}.mdi-power-socket-ch:before{content:"\f0fb3"}.mdi-power-socket-de:before{content:"\f1107"}.mdi-power-socket-eu:before{content:"\f07e7"}.mdi-power-socket-fr:before{content:"\f1108"}.mdi-power-socket-it:before{content:"\f14ff"}.mdi-power-socket-jp:before{content:"\f1109"}.mdi-power-socket-uk:before{content:"\f07e8"}.mdi-power-socket-us:before{content:"\f07e9"}.mdi-power-standby:before{content:"\f0906"}.mdi-powershell:before{content:"\f0a0a"}.mdi-prescription:before{content:"\f0706"}.mdi-presentation:before{content:"\f0428"}.mdi-presentation-play:before{content:"\f0429"}.mdi-pretzel:before{content:"\f1562"}.mdi-printer:before{content:"\f042a"}.mdi-printer-3d:before{content:"\f042b"}.mdi-printer-3d-nozzle:before{content:"\f0e5b"}.mdi-printer-3d-nozzle-alert:before{content:"\f11c0"}.mdi-printer-3d-nozzle-alert-outline:before{content:"\f11c1"}.mdi-printer-3d-nozzle-heat:before{content:"\f18b8"}.mdi-printer-3d-nozzle-heat-outline:before{content:"\f18b9"}.mdi-printer-3d-nozzle-off:before{content:"\f1b19"}.mdi-printer-3d-nozzle-off-outline:before{content:"\f1b1a"}.mdi-printer-3d-nozzle-outline:before{content:"\f0e5c"}.mdi-printer-3d-off:before{content:"\f1b0e"}.mdi-printer-alert:before{content:"\f042c"}.mdi-printer-check:before{content:"\f1146"}.mdi-printer-eye:before{content:"\f1458"}.mdi-printer-off:before{content:"\f0e5d"}.mdi-printer-off-outline:before{content:"\f1785"}.mdi-printer-outline:before{content:"\f1786"}.mdi-printer-pos:before{content:"\f1057"}.mdi-printer-pos-alert:before{content:"\f1bbc"}.mdi-printer-pos-alert-outline:before{content:"\f1bbd"}.mdi-printer-pos-cancel:before{content:"\f1bbe"}.mdi-printer-pos-cancel-outline:before{content:"\f1bbf"}.mdi-printer-pos-check:before{content:"\f1bc0"}.mdi-printer-pos-check-outline:before{content:"\f1bc1"}.mdi-printer-pos-cog:before{content:"\f1bc2"}.mdi-printer-pos-cog-outline:before{content:"\f1bc3"}.mdi-printer-pos-edit:before{content:"\f1bc4"}.mdi-printer-pos-edit-outline:before{content:"\f1bc5"}.mdi-printer-pos-minus:before{content:"\f1bc6"}.mdi-printer-pos-minus-outline:before{content:"\f1bc7"}.mdi-printer-pos-network:before{content:"\f1bc8"}.mdi-printer-pos-network-outline:before{content:"\f1bc9"}.mdi-printer-pos-off:before{content:"\f1bca"}.mdi-printer-pos-off-outline:before{content:"\f1bcb"}.mdi-printer-pos-outline:before{content:"\f1bcc"}.mdi-printer-pos-pause:before{content:"\f1bcd"}.mdi-printer-pos-pause-outline:before{content:"\f1bce"}.mdi-printer-pos-play:before{content:"\f1bcf"}.mdi-printer-pos-play-outline:before{content:"\f1bd0"}.mdi-printer-pos-plus:before{content:"\f1bd1"}.mdi-printer-pos-plus-outline:before{content:"\f1bd2"}.mdi-printer-pos-refresh:before{content:"\f1bd3"}.mdi-printer-pos-refresh-outline:before{content:"\f1bd4"}.mdi-printer-pos-remove:before{content:"\f1bd5"}.mdi-printer-pos-remove-outline:before{content:"\f1bd6"}.mdi-printer-pos-star:before{content:"\f1bd7"}.mdi-printer-pos-star-outline:before{content:"\f1bd8"}.mdi-printer-pos-stop:before{content:"\f1bd9"}.mdi-printer-pos-stop-outline:before{content:"\f1bda"}.mdi-printer-pos-sync:before{content:"\f1bdb"}.mdi-printer-pos-sync-outline:before{content:"\f1bdc"}.mdi-printer-pos-wrench:before{content:"\f1bdd"}.mdi-printer-pos-wrench-outline:before{content:"\f1bde"}.mdi-printer-search:before{content:"\f1457"}.mdi-printer-settings:before{content:"\f0707"}.mdi-printer-wireless:before{content:"\f0a0b"}.mdi-priority-high:before{content:"\f0603"}.mdi-priority-low:before{content:"\f0604"}.mdi-professional-hexagon:before{content:"\f042d"}.mdi-progress-alert:before{content:"\f0cbc"}.mdi-progress-check:before{content:"\f0995"}.mdi-progress-clock:before{content:"\f0996"}.mdi-progress-close:before{content:"\f110a"}.mdi-progress-download:before{content:"\f0997"}.mdi-progress-helper:before{content:"\f1ba2"}.mdi-progress-pencil:before{content:"\f1787"}.mdi-progress-question:before{content:"\f1522"}.mdi-progress-star:before{content:"\f1788"}.mdi-progress-star-four-points:before{content:"\f1c3d"}.mdi-progress-tag:before{content:"\f1d0d"}.mdi-progress-upload:before{content:"\f0998"}.mdi-progress-wrench:before{content:"\f0cbd"}.mdi-projector:before{content:"\f042e"}.mdi-projector-off:before{content:"\f1a23"}.mdi-projector-screen:before{content:"\f042f"}.mdi-projector-screen-off:before{content:"\f180d"}.mdi-projector-screen-off-outline:before{content:"\f180e"}.mdi-projector-screen-outline:before{content:"\f1724"}.mdi-projector-screen-variant:before{content:"\f180f"}.mdi-projector-screen-variant-off:before{content:"\f1810"}.mdi-projector-screen-variant-off-outline:before{content:"\f1811"}.mdi-projector-screen-variant-outline:before{content:"\f1812"}.mdi-propane-tank:before{content:"\f1357"}.mdi-propane-tank-outline:before{content:"\f1358"}.mdi-protocol:before{content:"\f0fd8"}.mdi-publish:before{content:"\f06a7"}.mdi-publish-off:before{content:"\f1945"}.mdi-pulse:before{content:"\f0430"}.mdi-pump:before{content:"\f1402"}.mdi-pump-off:before{content:"\f1b22"}.mdi-pumpkin:before{content:"\f0bbf"}.mdi-purse:before{content:"\f0f1c"}.mdi-purse-outline:before{content:"\f0f1d"}.mdi-puzzle:before{content:"\f0431"}.mdi-puzzle-check:before{content:"\f1426"}.mdi-puzzle-check-outline:before{content:"\f1427"}.mdi-puzzle-edit:before{content:"\f14d3"}.mdi-puzzle-edit-outline:before{content:"\f14d9"}.mdi-puzzle-heart:before{content:"\f14d4"}.mdi-puzzle-heart-outline:before{content:"\f14da"}.mdi-puzzle-minus:before{content:"\f14d1"}.mdi-puzzle-minus-outline:before{content:"\f14d7"}.mdi-puzzle-outline:before{content:"\f0a66"}.mdi-puzzle-plus:before{content:"\f14d0"}.mdi-puzzle-plus-outline:before{content:"\f14d6"}.mdi-puzzle-remove:before{content:"\f14d2"}.mdi-puzzle-remove-outline:before{content:"\f14d8"}.mdi-puzzle-star:before{content:"\f14d5"}.mdi-puzzle-star-outline:before{content:"\f14db"}.mdi-pyramid:before{content:"\f1952"}.mdi-pyramid-off:before{content:"\f1953"}.mdi-qi:before{content:"\f0999"}.mdi-qqchat:before{content:"\f0605"}.mdi-qrcode:before{content:"\f0432"}.mdi-qrcode-edit:before{content:"\f08b8"}.mdi-qrcode-minus:before{content:"\f118c"}.mdi-qrcode-plus:before{content:"\f118b"}.mdi-qrcode-remove:before{content:"\f118d"}.mdi-qrcode-scan:before{content:"\f0433"}.mdi-quadcopter:before{content:"\f0434"}.mdi-quality-high:before{content:"\f0435"}.mdi-quality-low:before{content:"\f0a0c"}.mdi-quality-medium:before{content:"\f0a0d"}.mdi-queue-first-in-last-out:before{content:"\f1caf"}.mdi-quora:before{content:"\f0d29"}.mdi-rabbit:before{content:"\f0907"}.mdi-rabbit-variant:before{content:"\f1a61"}.mdi-rabbit-variant-outline:before{content:"\f1a62"}.mdi-racing-helmet:before{content:"\f0d93"}.mdi-racquetball:before{content:"\f0d94"}.mdi-radar:before{content:"\f0437"}.mdi-radiator:before{content:"\f0438"}.mdi-radiator-disabled:before{content:"\f0ad7"}.mdi-radiator-off:before{content:"\f0ad8"}.mdi-radio:before{content:"\f0439"}.mdi-radio-am:before{content:"\f0cbe"}.mdi-radio-fm:before{content:"\f0cbf"}.mdi-radio-handheld:before{content:"\f043a"}.mdi-radio-off:before{content:"\f121c"}.mdi-radio-tower:before{content:"\f043b"}.mdi-radioactive:before{content:"\f043c"}.mdi-radioactive-circle:before{content:"\f185d"}.mdi-radioactive-circle-outline:before{content:"\f185e"}.mdi-radioactive-off:before{content:"\f0ec1"}.mdi-radiobox-blank:before{content:"\f043d"}.mdi-radiobox-indeterminate-variant:before{content:"\f1c5e"}.mdi-radiobox-marked:before{content:"\f043e"}.mdi-radiology-box:before{content:"\f14c5"}.mdi-radiology-box-outline:before{content:"\f14c6"}.mdi-radius:before{content:"\f0cc0"}.mdi-radius-outline:before{content:"\f0cc1"}.mdi-railroad-light:before{content:"\f0f1e"}.mdi-rake:before{content:"\f1544"}.mdi-raspberry-pi:before{content:"\f043f"}.mdi-raw:before{content:"\f1a0f"}.mdi-raw-off:before{content:"\f1a10"}.mdi-ray-end:before{content:"\f0440"}.mdi-ray-end-arrow:before{content:"\f0441"}.mdi-ray-start:before{content:"\f0442"}.mdi-ray-start-arrow:before{content:"\f0443"}.mdi-ray-start-end:before{content:"\f0444"}.mdi-ray-start-vertex-end:before{content:"\f15d8"}.mdi-ray-vertex:before{content:"\f0445"}.mdi-razor-double-edge:before{content:"\f1997"}.mdi-razor-single-edge:before{content:"\f1998"}.mdi-react:before{content:"\f0708"}.mdi-read:before{content:"\f0447"}.mdi-receipt:before{content:"\f0824"}.mdi-receipt-clock:before{content:"\f1c3e"}.mdi-receipt-clock-outline:before{content:"\f1c3f"}.mdi-receipt-outline:before{content:"\f04f7"}.mdi-receipt-send:before{content:"\f1c40"}.mdi-receipt-send-outline:before{content:"\f1c41"}.mdi-receipt-text:before{content:"\f0449"}.mdi-receipt-text-arrow-left:before{content:"\f1c42"}.mdi-receipt-text-arrow-left-outline:before{content:"\f1c43"}.mdi-receipt-text-arrow-right:before{content:"\f1c44"}.mdi-receipt-text-arrow-right-outline:before{content:"\f1c45"}.mdi-receipt-text-check:before{content:"\f1a63"}.mdi-receipt-text-check-outline:before{content:"\f1a64"}.mdi-receipt-text-clock:before{content:"\f1c46"}.mdi-receipt-text-clock-outline:before{content:"\f1c47"}.mdi-receipt-text-edit:before{content:"\f1c48"}.mdi-receipt-text-edit-outline:before{content:"\f1c49"}.mdi-receipt-text-minus:before{content:"\f1a65"}.mdi-receipt-text-minus-outline:before{content:"\f1a66"}.mdi-receipt-text-outline:before{content:"\f19dc"}.mdi-receipt-text-plus:before{content:"\f1a67"}.mdi-receipt-text-plus-outline:before{content:"\f1a68"}.mdi-receipt-text-remove:before{content:"\f1a69"}.mdi-receipt-text-remove-outline:before{content:"\f1a6a"}.mdi-receipt-text-send:before{content:"\f1c4a"}.mdi-receipt-text-send-outline:before{content:"\f1c4b"}.mdi-record:before{content:"\f044a"}.mdi-record-circle:before{content:"\f0ec2"}.mdi-record-circle-outline:before{content:"\f0ec3"}.mdi-record-player:before{content:"\f099a"}.mdi-record-rec:before{content:"\f044b"}.mdi-rectangle:before{content:"\f0e5e"}.mdi-rectangle-outline:before{content:"\f0e5f"}.mdi-recycle:before{content:"\f044c"}.mdi-recycle-variant:before{content:"\f139d"}.mdi-reddit:before{content:"\f044d"}.mdi-redhat:before{content:"\f111b"}.mdi-redo:before{content:"\f044e"}.mdi-redo-variant:before{content:"\f044f"}.mdi-reflect-horizontal:before{content:"\f0a0e"}.mdi-reflect-vertical:before{content:"\f0a0f"}.mdi-refresh:before{content:"\f0450"}.mdi-refresh-auto:before{content:"\f18f2"}.mdi-refresh-circle:before{content:"\f1377"}.mdi-regex:before{content:"\f0451"}.mdi-registered-trademark:before{content:"\f0a67"}.mdi-reiterate:before{content:"\f1588"}.mdi-relation-many-to-many:before{content:"\f1496"}.mdi-relation-many-to-one:before{content:"\f1497"}.mdi-relation-many-to-one-or-many:before{content:"\f1498"}.mdi-relation-many-to-only-one:before{content:"\f1499"}.mdi-relation-many-to-zero-or-many:before{content:"\f149a"}.mdi-relation-many-to-zero-or-one:before{content:"\f149b"}.mdi-relation-one-or-many-to-many:before{content:"\f149c"}.mdi-relation-one-or-many-to-one:before{content:"\f149d"}.mdi-relation-one-or-many-to-one-or-many:before{content:"\f149e"}.mdi-relation-one-or-many-to-only-one:before{content:"\f149f"}.mdi-relation-one-or-many-to-zero-or-many:before{content:"\f14a0"}.mdi-relation-one-or-many-to-zero-or-one:before{content:"\f14a1"}.mdi-relation-one-to-many:before{content:"\f14a2"}.mdi-relation-one-to-one:before{content:"\f14a3"}.mdi-relation-one-to-one-or-many:before{content:"\f14a4"}.mdi-relation-one-to-only-one:before{content:"\f14a5"}.mdi-relation-one-to-zero-or-many:before{content:"\f14a6"}.mdi-relation-one-to-zero-or-one:before{content:"\f14a7"}.mdi-relation-only-one-to-many:before{content:"\f14a8"}.mdi-relation-only-one-to-one:before{content:"\f14a9"}.mdi-relation-only-one-to-one-or-many:before{content:"\f14aa"}.mdi-relation-only-one-to-only-one:before{content:"\f14ab"}.mdi-relation-only-one-to-zero-or-many:before{content:"\f14ac"}.mdi-relation-only-one-to-zero-or-one:before{content:"\f14ad"}.mdi-relation-zero-or-many-to-many:before{content:"\f14ae"}.mdi-relation-zero-or-many-to-one:before{content:"\f14af"}.mdi-relation-zero-or-many-to-one-or-many:before{content:"\f14b0"}.mdi-relation-zero-or-many-to-only-one:before{content:"\f14b1"}.mdi-relation-zero-or-many-to-zero-or-many:before{content:"\f14b2"}.mdi-relation-zero-or-many-to-zero-or-one:before{content:"\f14b3"}.mdi-relation-zero-or-one-to-many:before{content:"\f14b4"}.mdi-relation-zero-or-one-to-one:before{content:"\f14b5"}.mdi-relation-zero-or-one-to-one-or-many:before{content:"\f14b6"}.mdi-relation-zero-or-one-to-only-one:before{content:"\f14b7"}.mdi-relation-zero-or-one-to-zero-or-many:before{content:"\f14b8"}.mdi-relation-zero-or-one-to-zero-or-one:before{content:"\f14b9"}.mdi-relative-scale:before{content:"\f0452"}.mdi-reload:before{content:"\f0453"}.mdi-reload-alert:before{content:"\f110b"}.mdi-reminder:before{content:"\f088c"}.mdi-remote:before{content:"\f0454"}.mdi-remote-desktop:before{content:"\f08b9"}.mdi-remote-off:before{content:"\f0ec4"}.mdi-remote-tv:before{content:"\f0ec5"}.mdi-remote-tv-off:before{content:"\f0ec6"}.mdi-rename:before{content:"\f1c18"}.mdi-rename-box:before{content:"\f0455"}.mdi-rename-box-outline:before{content:"\f1c19"}.mdi-rename-outline:before{content:"\f1c1a"}.mdi-reorder-horizontal:before{content:"\f0688"}.mdi-reorder-vertical:before{content:"\f0689"}.mdi-repeat:before{content:"\f0456"}.mdi-repeat-off:before{content:"\f0457"}.mdi-repeat-once:before{content:"\f0458"}.mdi-repeat-variant:before{content:"\f0547"}.mdi-replay:before{content:"\f0459"}.mdi-reply:before{content:"\f045a"}.mdi-reply-all:before{content:"\f045b"}.mdi-reply-all-outline:before{content:"\f0f1f"}.mdi-reply-circle:before{content:"\f11ae"}.mdi-reply-outline:before{content:"\f0f20"}.mdi-reproduction:before{content:"\f045c"}.mdi-resistor:before{content:"\f0b44"}.mdi-resistor-nodes:before{content:"\f0b45"}.mdi-resize:before{content:"\f0a68"}.mdi-resize-bottom-right:before{content:"\f045d"}.mdi-responsive:before{content:"\f045e"}.mdi-restart:before{content:"\f0709"}.mdi-restart-alert:before{content:"\f110c"}.mdi-restart-off:before{content:"\f0d95"}.mdi-restore:before{content:"\f099b"}.mdi-restore-alert:before{content:"\f110d"}.mdi-rewind:before{content:"\f045f"}.mdi-rewind-10:before{content:"\f0d2a"}.mdi-rewind-15:before{content:"\f1946"}.mdi-rewind-30:before{content:"\f0d96"}.mdi-rewind-45:before{content:"\f1b13"}.mdi-rewind-5:before{content:"\f11f9"}.mdi-rewind-60:before{content:"\f160c"}.mdi-rewind-outline:before{content:"\f070a"}.mdi-rhombus:before{content:"\f070b"}.mdi-rhombus-medium:before{content:"\f0a10"}.mdi-rhombus-medium-outline:before{content:"\f14dc"}.mdi-rhombus-outline:before{content:"\f070c"}.mdi-rhombus-split:before{content:"\f0a11"}.mdi-rhombus-split-outline:before{content:"\f14dd"}.mdi-ribbon:before{content:"\f0460"}.mdi-rice:before{content:"\f07ea"}.mdi-rickshaw:before{content:"\f15bb"}.mdi-rickshaw-electric:before{content:"\f15bc"}.mdi-ring:before{content:"\f07eb"}.mdi-rivet:before{content:"\f0e60"}.mdi-road:before{content:"\f0461"}.mdi-road-variant:before{content:"\f0462"}.mdi-robber:before{content:"\f1058"}.mdi-robot:before{content:"\f06a9"}.mdi-robot-angry:before{content:"\f169d"}.mdi-robot-angry-outline:before{content:"\f169e"}.mdi-robot-confused:before{content:"\f169f"}.mdi-robot-confused-outline:before{content:"\f16a0"}.mdi-robot-dead:before{content:"\f16a1"}.mdi-robot-dead-outline:before{content:"\f16a2"}.mdi-robot-excited:before{content:"\f16a3"}.mdi-robot-excited-outline:before{content:"\f16a4"}.mdi-robot-happy:before{content:"\f1719"}.mdi-robot-happy-outline:before{content:"\f171a"}.mdi-robot-industrial:before{content:"\f0b46"}.mdi-robot-industrial-outline:before{content:"\f1a1a"}.mdi-robot-love:before{content:"\f16a5"}.mdi-robot-love-outline:before{content:"\f16a6"}.mdi-robot-mower:before{content:"\f11f7"}.mdi-robot-mower-outline:before{content:"\f11f3"}.mdi-robot-off:before{content:"\f16a7"}.mdi-robot-off-outline:before{content:"\f167b"}.mdi-robot-outline:before{content:"\f167a"}.mdi-robot-vacuum:before{content:"\f070d"}.mdi-robot-vacuum-alert:before{content:"\f1b5d"}.mdi-robot-vacuum-off:before{content:"\f1c01"}.mdi-robot-vacuum-variant:before{content:"\f0908"}.mdi-robot-vacuum-variant-alert:before{content:"\f1b5e"}.mdi-robot-vacuum-variant-off:before{content:"\f1c02"}.mdi-rocket:before{content:"\f0463"}.mdi-rocket-launch:before{content:"\f14de"}.mdi-rocket-launch-outline:before{content:"\f14df"}.mdi-rocket-outline:before{content:"\f13af"}.mdi-rodent:before{content:"\f1327"}.mdi-roller-shade:before{content:"\f1a6b"}.mdi-roller-shade-closed:before{content:"\f1a6c"}.mdi-roller-skate:before{content:"\f0d2b"}.mdi-roller-skate-off:before{content:"\f0145"}.mdi-rollerblade:before{content:"\f0d2c"}.mdi-rollerblade-off:before{content:"\f002e"}.mdi-rollupjs:before{content:"\f0bc0"}.mdi-rolodex:before{content:"\f1ab9"}.mdi-rolodex-outline:before{content:"\f1aba"}.mdi-roman-numeral-1:before{content:"\f1088"}.mdi-roman-numeral-10:before{content:"\f1091"}.mdi-roman-numeral-2:before{content:"\f1089"}.mdi-roman-numeral-3:before{content:"\f108a"}.mdi-roman-numeral-4:before{content:"\f108b"}.mdi-roman-numeral-5:before{content:"\f108c"}.mdi-roman-numeral-6:before{content:"\f108d"}.mdi-roman-numeral-7:before{content:"\f108e"}.mdi-roman-numeral-8:before{content:"\f108f"}.mdi-roman-numeral-9:before{content:"\f1090"}.mdi-room-service:before{content:"\f088d"}.mdi-room-service-outline:before{content:"\f0d97"}.mdi-rotate-360:before{content:"\f1999"}.mdi-rotate-3d:before{content:"\f0ec7"}.mdi-rotate-3d-variant:before{content:"\f0464"}.mdi-rotate-left:before{content:"\f0465"}.mdi-rotate-left-variant:before{content:"\f0466"}.mdi-rotate-orbit:before{content:"\f0d98"}.mdi-rotate-right:before{content:"\f0467"}.mdi-rotate-right-variant:before{content:"\f0468"}.mdi-rounded-corner:before{content:"\f0607"}.mdi-router:before{content:"\f11e2"}.mdi-router-network:before{content:"\f1087"}.mdi-router-network-wireless:before{content:"\f1c97"}.mdi-router-wireless:before{content:"\f0469"}.mdi-router-wireless-off:before{content:"\f15a3"}.mdi-router-wireless-settings:before{content:"\f0a69"}.mdi-routes:before{content:"\f046a"}.mdi-routes-clock:before{content:"\f1059"}.mdi-rowing:before{content:"\f0608"}.mdi-rss:before{content:"\f046b"}.mdi-rss-box:before{content:"\f046c"}.mdi-rss-off:before{content:"\f0f21"}.mdi-rug:before{content:"\f1475"}.mdi-rugby:before{content:"\f0d99"}.mdi-ruler:before{content:"\f046d"}.mdi-ruler-square:before{content:"\f0cc2"}.mdi-ruler-square-compass:before{content:"\f0ebe"}.mdi-run:before{content:"\f070e"}.mdi-run-fast:before{content:"\f046e"}.mdi-rv-truck:before{content:"\f11d4"}.mdi-sack:before{content:"\f0d2e"}.mdi-sack-outline:before{content:"\f1c4c"}.mdi-sack-percent:before{content:"\f0d2f"}.mdi-safe:before{content:"\f0a6a"}.mdi-safe-square:before{content:"\f127c"}.mdi-safe-square-outline:before{content:"\f127d"}.mdi-safety-goggles:before{content:"\f0d30"}.mdi-sail-boat:before{content:"\f0ec8"}.mdi-sail-boat-sink:before{content:"\f1aef"}.mdi-sale:before{content:"\f046f"}.mdi-sale-outline:before{content:"\f1a06"}.mdi-salesforce:before{content:"\f088e"}.mdi-sass:before{content:"\f07ec"}.mdi-satellite:before{content:"\f0470"}.mdi-satellite-uplink:before{content:"\f0909"}.mdi-satellite-variant:before{content:"\f0471"}.mdi-sausage:before{content:"\f08ba"}.mdi-sausage-off:before{content:"\f1789"}.mdi-saw-blade:before{content:"\f0e61"}.mdi-sawtooth-wave:before{content:"\f147a"}.mdi-saxophone:before{content:"\f0609"}.mdi-scale:before{content:"\f0472"}.mdi-scale-balance:before{content:"\f05d1"}.mdi-scale-bathroom:before{content:"\f0473"}.mdi-scale-off:before{content:"\f105a"}.mdi-scale-unbalanced:before{content:"\f19b8"}.mdi-scan-helper:before{content:"\f13d8"}.mdi-scanner:before{content:"\f06ab"}.mdi-scanner-off:before{content:"\f090a"}.mdi-scatter-plot:before{content:"\f0ec9"}.mdi-scatter-plot-outline:before{content:"\f0eca"}.mdi-scent:before{content:"\f1958"}.mdi-scent-off:before{content:"\f1959"}.mdi-school:before{content:"\f0474"}.mdi-school-outline:before{content:"\f1180"}.mdi-scissors-cutting:before{content:"\f0a6b"}.mdi-scooter:before{content:"\f15bd"}.mdi-scooter-electric:before{content:"\f15be"}.mdi-scoreboard:before{content:"\f127e"}.mdi-scoreboard-outline:before{content:"\f127f"}.mdi-screen-rotation:before{content:"\f0475"}.mdi-screen-rotation-lock:before{content:"\f0478"}.mdi-screw-flat-top:before{content:"\f0df3"}.mdi-screw-lag:before{content:"\f0df4"}.mdi-screw-machine-flat-top:before{content:"\f0df5"}.mdi-screw-machine-round-top:before{content:"\f0df6"}.mdi-screw-round-top:before{content:"\f0df7"}.mdi-screwdriver:before{content:"\f0476"}.mdi-script:before{content:"\f0bc1"}.mdi-script-outline:before{content:"\f0477"}.mdi-script-text:before{content:"\f0bc2"}.mdi-script-text-key:before{content:"\f1725"}.mdi-script-text-key-outline:before{content:"\f1726"}.mdi-script-text-outline:before{content:"\f0bc3"}.mdi-script-text-play:before{content:"\f1727"}.mdi-script-text-play-outline:before{content:"\f1728"}.mdi-sd:before{content:"\f0479"}.mdi-seal:before{content:"\f047a"}.mdi-seal-variant:before{content:"\f0fd9"}.mdi-search-web:before{content:"\f070f"}.mdi-seat:before{content:"\f0cc3"}.mdi-seat-flat:before{content:"\f047b"}.mdi-seat-flat-angled:before{content:"\f047c"}.mdi-seat-individual-suite:before{content:"\f047d"}.mdi-seat-legroom-extra:before{content:"\f047e"}.mdi-seat-legroom-normal:before{content:"\f047f"}.mdi-seat-legroom-reduced:before{content:"\f0480"}.mdi-seat-outline:before{content:"\f0cc4"}.mdi-seat-passenger:before{content:"\f1249"}.mdi-seat-recline-extra:before{content:"\f0481"}.mdi-seat-recline-normal:before{content:"\f0482"}.mdi-seatbelt:before{content:"\f0cc5"}.mdi-security:before{content:"\f0483"}.mdi-security-network:before{content:"\f0484"}.mdi-seed:before{content:"\f0e62"}.mdi-seed-off:before{content:"\f13fd"}.mdi-seed-off-outline:before{content:"\f13fe"}.mdi-seed-outline:before{content:"\f0e63"}.mdi-seed-plus:before{content:"\f1a6d"}.mdi-seed-plus-outline:before{content:"\f1a6e"}.mdi-seesaw:before{content:"\f15a4"}.mdi-segment:before{content:"\f0ecb"}.mdi-select:before{content:"\f0485"}.mdi-select-all:before{content:"\f0486"}.mdi-select-arrow-down:before{content:"\f1b59"}.mdi-select-arrow-up:before{content:"\f1b58"}.mdi-select-color:before{content:"\f0d31"}.mdi-select-compare:before{content:"\f0ad9"}.mdi-select-drag:before{content:"\f0a6c"}.mdi-select-group:before{content:"\f0f82"}.mdi-select-inverse:before{content:"\f0487"}.mdi-select-marker:before{content:"\f1280"}.mdi-select-multiple:before{content:"\f1281"}.mdi-select-multiple-marker:before{content:"\f1282"}.mdi-select-off:before{content:"\f0488"}.mdi-select-place:before{content:"\f0fda"}.mdi-select-remove:before{content:"\f17c1"}.mdi-select-search:before{content:"\f1204"}.mdi-selection:before{content:"\f0489"}.mdi-selection-drag:before{content:"\f0a6d"}.mdi-selection-ellipse:before{content:"\f0d32"}.mdi-selection-ellipse-arrow-inside:before{content:"\f0f22"}.mdi-selection-ellipse-remove:before{content:"\f17c2"}.mdi-selection-marker:before{content:"\f1283"}.mdi-selection-multiple:before{content:"\f1285"}.mdi-selection-multiple-marker:before{content:"\f1284"}.mdi-selection-off:before{content:"\f0777"}.mdi-selection-remove:before{content:"\f17c3"}.mdi-selection-search:before{content:"\f1205"}.mdi-semantic-web:before{content:"\f1316"}.mdi-send:before{content:"\f048a"}.mdi-send-check:before{content:"\f1161"}.mdi-send-check-outline:before{content:"\f1162"}.mdi-send-circle:before{content:"\f0df8"}.mdi-send-circle-outline:before{content:"\f0df9"}.mdi-send-clock:before{content:"\f1163"}.mdi-send-clock-outline:before{content:"\f1164"}.mdi-send-lock:before{content:"\f07ed"}.mdi-send-lock-outline:before{content:"\f1166"}.mdi-send-outline:before{content:"\f1165"}.mdi-send-variant:before{content:"\f1c4d"}.mdi-send-variant-clock:before{content:"\f1c7e"}.mdi-send-variant-clock-outline:before{content:"\f1c7f"}.mdi-send-variant-outline:before{content:"\f1c4e"}.mdi-serial-port:before{content:"\f065c"}.mdi-server:before{content:"\f048b"}.mdi-server-minus:before{content:"\f048c"}.mdi-server-minus-outline:before{content:"\f1c98"}.mdi-server-network:before{content:"\f048d"}.mdi-server-network-off:before{content:"\f048e"}.mdi-server-network-outline:before{content:"\f1c99"}.mdi-server-off:before{content:"\f048f"}.mdi-server-outline:before{content:"\f1c9a"}.mdi-server-plus:before{content:"\f0490"}.mdi-server-plus-outline:before{content:"\f1c9b"}.mdi-server-remove:before{content:"\f0491"}.mdi-server-security:before{content:"\f0492"}.mdi-set-all:before{content:"\f0778"}.mdi-set-center:before{content:"\f0779"}.mdi-set-center-right:before{content:"\f077a"}.mdi-set-left:before{content:"\f077b"}.mdi-set-left-center:before{content:"\f077c"}.mdi-set-left-right:before{content:"\f077d"}.mdi-set-merge:before{content:"\f14e0"}.mdi-set-none:before{content:"\f077e"}.mdi-set-right:before{content:"\f077f"}.mdi-set-split:before{content:"\f14e1"}.mdi-set-square:before{content:"\f145d"}.mdi-set-top-box:before{content:"\f099f"}.mdi-settings-helper:before{content:"\f0a6e"}.mdi-shaker:before{content:"\f110e"}.mdi-shaker-outline:before{content:"\f110f"}.mdi-shape:before{content:"\f0831"}.mdi-shape-circle-plus:before{content:"\f065d"}.mdi-shape-outline:before{content:"\f0832"}.mdi-shape-oval-plus:before{content:"\f11fa"}.mdi-shape-plus:before{content:"\f0495"}.mdi-shape-plus-outline:before{content:"\f1c4f"}.mdi-shape-polygon-plus:before{content:"\f065e"}.mdi-shape-rectangle-plus:before{content:"\f065f"}.mdi-shape-square-plus:before{content:"\f0660"}.mdi-shape-square-rounded-plus:before{content:"\f14fa"}.mdi-share:before{content:"\f0496"}.mdi-share-all:before{content:"\f11f4"}.mdi-share-all-outline:before{content:"\f11f5"}.mdi-share-circle:before{content:"\f11ad"}.mdi-share-off:before{content:"\f0f23"}.mdi-share-off-outline:before{content:"\f0f24"}.mdi-share-outline:before{content:"\f0932"}.mdi-share-variant:before{content:"\f0497"}.mdi-share-variant-outline:before{content:"\f1514"}.mdi-shark:before{content:"\f18ba"}.mdi-shark-fin:before{content:"\f1673"}.mdi-shark-fin-outline:before{content:"\f1674"}.mdi-shark-off:before{content:"\f18bb"}.mdi-sheep:before{content:"\f0cc6"}.mdi-shield:before{content:"\f0498"}.mdi-shield-account:before{content:"\f088f"}.mdi-shield-account-outline:before{content:"\f0a12"}.mdi-shield-account-variant:before{content:"\f15a7"}.mdi-shield-account-variant-outline:before{content:"\f15a8"}.mdi-shield-airplane:before{content:"\f06bb"}.mdi-shield-airplane-outline:before{content:"\f0cc7"}.mdi-shield-alert:before{content:"\f0ecc"}.mdi-shield-alert-outline:before{content:"\f0ecd"}.mdi-shield-bug:before{content:"\f13da"}.mdi-shield-bug-outline:before{content:"\f13db"}.mdi-shield-car:before{content:"\f0f83"}.mdi-shield-check:before{content:"\f0565"}.mdi-shield-check-outline:before{content:"\f0cc8"}.mdi-shield-cross:before{content:"\f0cc9"}.mdi-shield-cross-outline:before{content:"\f0cca"}.mdi-shield-crown:before{content:"\f18bc"}.mdi-shield-crown-outline:before{content:"\f18bd"}.mdi-shield-edit:before{content:"\f11a0"}.mdi-shield-edit-outline:before{content:"\f11a1"}.mdi-shield-half:before{content:"\f1360"}.mdi-shield-half-full:before{content:"\f0780"}.mdi-shield-home:before{content:"\f068a"}.mdi-shield-home-outline:before{content:"\f0ccb"}.mdi-shield-key:before{content:"\f0bc4"}.mdi-shield-key-outline:before{content:"\f0bc5"}.mdi-shield-link-variant:before{content:"\f0d33"}.mdi-shield-link-variant-outline:before{content:"\f0d34"}.mdi-shield-lock:before{content:"\f099d"}.mdi-shield-lock-open:before{content:"\f199a"}.mdi-shield-lock-open-outline:before{content:"\f199b"}.mdi-shield-lock-outline:before{content:"\f0ccc"}.mdi-shield-moon:before{content:"\f1828"}.mdi-shield-moon-outline:before{content:"\f1829"}.mdi-shield-off:before{content:"\f099e"}.mdi-shield-off-outline:before{content:"\f099c"}.mdi-shield-outline:before{content:"\f0499"}.mdi-shield-plus:before{content:"\f0ada"}.mdi-shield-plus-outline:before{content:"\f0adb"}.mdi-shield-refresh:before{content:"\f00aa"}.mdi-shield-refresh-outline:before{content:"\f01e0"}.mdi-shield-remove:before{content:"\f0adc"}.mdi-shield-remove-outline:before{content:"\f0add"}.mdi-shield-search:before{content:"\f0d9a"}.mdi-shield-star:before{content:"\f113b"}.mdi-shield-star-outline:before{content:"\f113c"}.mdi-shield-sun:before{content:"\f105d"}.mdi-shield-sun-outline:before{content:"\f105e"}.mdi-shield-sword:before{content:"\f18be"}.mdi-shield-sword-outline:before{content:"\f18bf"}.mdi-shield-sync:before{content:"\f11a2"}.mdi-shield-sync-outline:before{content:"\f11a3"}.mdi-shimmer:before{content:"\f1545"}.mdi-ship-wheel:before{content:"\f0833"}.mdi-shipping-pallet:before{content:"\f184e"}.mdi-shoe-ballet:before{content:"\f15ca"}.mdi-shoe-cleat:before{content:"\f15c7"}.mdi-shoe-formal:before{content:"\f0b47"}.mdi-shoe-heel:before{content:"\f0b48"}.mdi-shoe-print:before{content:"\f0dfa"}.mdi-shoe-sneaker:before{content:"\f15c8"}.mdi-shopping:before{content:"\f049a"}.mdi-shopping-music:before{content:"\f049b"}.mdi-shopping-outline:before{content:"\f11d5"}.mdi-shopping-search:before{content:"\f0f84"}.mdi-shopping-search-outline:before{content:"\f1a6f"}.mdi-shore:before{content:"\f14f9"}.mdi-shovel:before{content:"\f0710"}.mdi-shovel-off:before{content:"\f0711"}.mdi-shower:before{content:"\f09a0"}.mdi-shower-head:before{content:"\f09a1"}.mdi-shredder:before{content:"\f049c"}.mdi-shuffle:before{content:"\f049d"}.mdi-shuffle-disabled:before{content:"\f049e"}.mdi-shuffle-variant:before{content:"\f049f"}.mdi-shuriken:before{content:"\f137f"}.mdi-sickle:before{content:"\f18c0"}.mdi-sigma:before{content:"\f04a0"}.mdi-sigma-lower:before{content:"\f062b"}.mdi-sign-caution:before{content:"\f04a1"}.mdi-sign-direction:before{content:"\f0781"}.mdi-sign-direction-minus:before{content:"\f1000"}.mdi-sign-direction-plus:before{content:"\f0fdc"}.mdi-sign-direction-remove:before{content:"\f0fdd"}.mdi-sign-language:before{content:"\f1b4d"}.mdi-sign-language-outline:before{content:"\f1b4e"}.mdi-sign-pole:before{content:"\f14f8"}.mdi-sign-real-estate:before{content:"\f1118"}.mdi-sign-text:before{content:"\f0782"}.mdi-sign-yield:before{content:"\f1baf"}.mdi-signal:before{content:"\f04a2"}.mdi-signal-2g:before{content:"\f0712"}.mdi-signal-3g:before{content:"\f0713"}.mdi-signal-4g:before{content:"\f0714"}.mdi-signal-5g:before{content:"\f0a6f"}.mdi-signal-cellular-1:before{content:"\f08bc"}.mdi-signal-cellular-2:before{content:"\f08bd"}.mdi-signal-cellular-3:before{content:"\f08be"}.mdi-signal-cellular-outline:before{content:"\f08bf"}.mdi-signal-distance-variant:before{content:"\f0e64"}.mdi-signal-hspa:before{content:"\f0715"}.mdi-signal-hspa-plus:before{content:"\f0716"}.mdi-signal-off:before{content:"\f0783"}.mdi-signal-variant:before{content:"\f060a"}.mdi-signature:before{content:"\f0dfb"}.mdi-signature-freehand:before{content:"\f0dfc"}.mdi-signature-image:before{content:"\f0dfd"}.mdi-signature-text:before{content:"\f0dfe"}.mdi-silo:before{content:"\f1b9f"}.mdi-silo-outline:before{content:"\f0b49"}.mdi-silverware:before{content:"\f04a3"}.mdi-silverware-clean:before{content:"\f0fde"}.mdi-silverware-fork:before{content:"\f04a4"}.mdi-silverware-fork-knife:before{content:"\f0a70"}.mdi-silverware-spoon:before{content:"\f04a5"}.mdi-silverware-variant:before{content:"\f04a6"}.mdi-sim:before{content:"\f04a7"}.mdi-sim-alert:before{content:"\f04a8"}.mdi-sim-alert-outline:before{content:"\f15d3"}.mdi-sim-off:before{content:"\f04a9"}.mdi-sim-off-outline:before{content:"\f15d4"}.mdi-sim-outline:before{content:"\f15d5"}.mdi-simple-icons:before{content:"\f131d"}.mdi-sina-weibo:before{content:"\f0adf"}.mdi-sine-wave:before{content:"\f095b"}.mdi-sitemap:before{content:"\f04aa"}.mdi-sitemap-outline:before{content:"\f199c"}.mdi-size-l:before{content:"\f13a6"}.mdi-size-m:before{content:"\f13a5"}.mdi-size-s:before{content:"\f13a4"}.mdi-size-xl:before{content:"\f13a7"}.mdi-size-xs:before{content:"\f13a3"}.mdi-size-xxl:before{content:"\f13a8"}.mdi-size-xxs:before{content:"\f13a2"}.mdi-size-xxxl:before{content:"\f13a9"}.mdi-skate:before{content:"\f0d35"}.mdi-skate-off:before{content:"\f0699"}.mdi-skateboard:before{content:"\f14c2"}.mdi-skateboarding:before{content:"\f0501"}.mdi-skew-less:before{content:"\f0d36"}.mdi-skew-more:before{content:"\f0d37"}.mdi-ski:before{content:"\f1304"}.mdi-ski-cross-country:before{content:"\f1305"}.mdi-ski-water:before{content:"\f1306"}.mdi-skip-backward:before{content:"\f04ab"}.mdi-skip-backward-outline:before{content:"\f0f25"}.mdi-skip-forward:before{content:"\f04ac"}.mdi-skip-forward-outline:before{content:"\f0f26"}.mdi-skip-next:before{content:"\f04ad"}.mdi-skip-next-circle:before{content:"\f0661"}.mdi-skip-next-circle-outline:before{content:"\f0662"}.mdi-skip-next-outline:before{content:"\f0f27"}.mdi-skip-previous:before{content:"\f04ae"}.mdi-skip-previous-circle:before{content:"\f0663"}.mdi-skip-previous-circle-outline:before{content:"\f0664"}.mdi-skip-previous-outline:before{content:"\f0f28"}.mdi-skull:before{content:"\f068c"}.mdi-skull-crossbones:before{content:"\f0bc6"}.mdi-skull-crossbones-outline:before{content:"\f0bc7"}.mdi-skull-outline:before{content:"\f0bc8"}.mdi-skull-scan:before{content:"\f14c7"}.mdi-skull-scan-outline:before{content:"\f14c8"}.mdi-skype:before{content:"\f04af"}.mdi-skype-business:before{content:"\f04b0"}.mdi-slack:before{content:"\f04b1"}.mdi-slash-forward:before{content:"\f0fdf"}.mdi-slash-forward-box:before{content:"\f0fe0"}.mdi-sledding:before{content:"\f041b"}.mdi-sleep:before{content:"\f04b2"}.mdi-sleep-off:before{content:"\f04b3"}.mdi-slide:before{content:"\f15a5"}.mdi-slope-downhill:before{content:"\f0dff"}.mdi-slope-uphill:before{content:"\f0e00"}.mdi-slot-machine:before{content:"\f1114"}.mdi-slot-machine-outline:before{content:"\f1115"}.mdi-smart-card:before{content:"\f10bd"}.mdi-smart-card-off:before{content:"\f18f7"}.mdi-smart-card-off-outline:before{content:"\f18f8"}.mdi-smart-card-outline:before{content:"\f10be"}.mdi-smart-card-reader:before{content:"\f10bf"}.mdi-smart-card-reader-outline:before{content:"\f10c0"}.mdi-smog:before{content:"\f0a71"}.mdi-smoke:before{content:"\f1799"}.mdi-smoke-detector:before{content:"\f0392"}.mdi-smoke-detector-alert:before{content:"\f192e"}.mdi-smoke-detector-alert-outline:before{content:"\f192f"}.mdi-smoke-detector-off:before{content:"\f1809"}.mdi-smoke-detector-off-outline:before{content:"\f180a"}.mdi-smoke-detector-outline:before{content:"\f1808"}.mdi-smoke-detector-variant:before{content:"\f180b"}.mdi-smoke-detector-variant-alert:before{content:"\f1930"}.mdi-smoke-detector-variant-off:before{content:"\f180c"}.mdi-smoking:before{content:"\f04b4"}.mdi-smoking-off:before{content:"\f04b5"}.mdi-smoking-pipe:before{content:"\f140d"}.mdi-smoking-pipe-off:before{content:"\f1428"}.mdi-snail:before{content:"\f1677"}.mdi-snake:before{content:"\f150e"}.mdi-snapchat:before{content:"\f04b6"}.mdi-snowboard:before{content:"\f1307"}.mdi-snowflake:before{content:"\f0717"}.mdi-snowflake-alert:before{content:"\f0f29"}.mdi-snowflake-check:before{content:"\f1a70"}.mdi-snowflake-melt:before{content:"\f12cb"}.mdi-snowflake-off:before{content:"\f14e3"}.mdi-snowflake-thermometer:before{content:"\f1a71"}.mdi-snowflake-variant:before{content:"\f0f2a"}.mdi-snowman:before{content:"\f04b7"}.mdi-snowmobile:before{content:"\f06dd"}.mdi-snowshoeing:before{content:"\f1a72"}.mdi-soccer:before{content:"\f04b8"}.mdi-soccer-field:before{content:"\f0834"}.mdi-social-distance-2-meters:before{content:"\f1579"}.mdi-social-distance-6-feet:before{content:"\f157a"}.mdi-sofa:before{content:"\f04b9"}.mdi-sofa-outline:before{content:"\f156d"}.mdi-sofa-single:before{content:"\f156e"}.mdi-sofa-single-outline:before{content:"\f156f"}.mdi-solar-panel:before{content:"\f0d9b"}.mdi-solar-panel-large:before{content:"\f0d9c"}.mdi-solar-power:before{content:"\f0a72"}.mdi-solar-power-variant:before{content:"\f1a73"}.mdi-solar-power-variant-outline:before{content:"\f1a74"}.mdi-soldering-iron:before{content:"\f1092"}.mdi-solid:before{content:"\f068d"}.mdi-sony-playstation:before{content:"\f0414"}.mdi-sort:before{content:"\f04ba"}.mdi-sort-alphabetical-ascending:before{content:"\f05bd"}.mdi-sort-alphabetical-ascending-variant:before{content:"\f1148"}.mdi-sort-alphabetical-descending:before{content:"\f05bf"}.mdi-sort-alphabetical-descending-variant:before{content:"\f1149"}.mdi-sort-alphabetical-variant:before{content:"\f04bb"}.mdi-sort-ascending:before{content:"\f04bc"}.mdi-sort-bool-ascending:before{content:"\f1385"}.mdi-sort-bool-ascending-variant:before{content:"\f1386"}.mdi-sort-bool-descending:before{content:"\f1387"}.mdi-sort-bool-descending-variant:before{content:"\f1388"}.mdi-sort-calendar-ascending:before{content:"\f1547"}.mdi-sort-calendar-descending:before{content:"\f1548"}.mdi-sort-clock-ascending:before{content:"\f1549"}.mdi-sort-clock-ascending-outline:before{content:"\f154a"}.mdi-sort-clock-descending:before{content:"\f154b"}.mdi-sort-clock-descending-outline:before{content:"\f154c"}.mdi-sort-descending:before{content:"\f04bd"}.mdi-sort-numeric-ascending:before{content:"\f1389"}.mdi-sort-numeric-ascending-variant:before{content:"\f090d"}.mdi-sort-numeric-descending:before{content:"\f138a"}.mdi-sort-numeric-descending-variant:before{content:"\f0ad2"}.mdi-sort-numeric-variant:before{content:"\f04be"}.mdi-sort-reverse-variant:before{content:"\f033c"}.mdi-sort-variant:before{content:"\f04bf"}.mdi-sort-variant-lock:before{content:"\f0ccd"}.mdi-sort-variant-lock-open:before{content:"\f0cce"}.mdi-sort-variant-off:before{content:"\f1abb"}.mdi-sort-variant-remove:before{content:"\f1147"}.mdi-soundbar:before{content:"\f17db"}.mdi-soundcloud:before{content:"\f04c0"}.mdi-source-branch:before{content:"\f062c"}.mdi-source-branch-check:before{content:"\f14cf"}.mdi-source-branch-minus:before{content:"\f14cb"}.mdi-source-branch-plus:before{content:"\f14ca"}.mdi-source-branch-refresh:before{content:"\f14cd"}.mdi-source-branch-remove:before{content:"\f14cc"}.mdi-source-branch-sync:before{content:"\f14ce"}.mdi-source-commit:before{content:"\f0718"}.mdi-source-commit-end:before{content:"\f0719"}.mdi-source-commit-end-local:before{content:"\f071a"}.mdi-source-commit-local:before{content:"\f071b"}.mdi-source-commit-next-local:before{content:"\f071c"}.mdi-source-commit-start:before{content:"\f071d"}.mdi-source-commit-start-next-local:before{content:"\f071e"}.mdi-source-fork:before{content:"\f04c1"}.mdi-source-merge:before{content:"\f062d"}.mdi-source-pull:before{content:"\f04c2"}.mdi-source-repository:before{content:"\f0ccf"}.mdi-source-repository-multiple:before{content:"\f0cd0"}.mdi-soy-sauce:before{content:"\f07ee"}.mdi-soy-sauce-off:before{content:"\f13fc"}.mdi-spa:before{content:"\f0cd1"}.mdi-spa-outline:before{content:"\f0cd2"}.mdi-space-invaders:before{content:"\f0bc9"}.mdi-space-station:before{content:"\f1383"}.mdi-spade:before{content:"\f0e65"}.mdi-speaker:before{content:"\f04c3"}.mdi-speaker-bluetooth:before{content:"\f09a2"}.mdi-speaker-message:before{content:"\f1b11"}.mdi-speaker-multiple:before{content:"\f0d38"}.mdi-speaker-off:before{content:"\f04c4"}.mdi-speaker-pause:before{content:"\f1b73"}.mdi-speaker-play:before{content:"\f1b72"}.mdi-speaker-stop:before{content:"\f1b74"}.mdi-speaker-wireless:before{content:"\f071f"}.mdi-spear:before{content:"\f1845"}.mdi-speedometer:before{content:"\f04c5"}.mdi-speedometer-medium:before{content:"\f0f85"}.mdi-speedometer-slow:before{content:"\f0f86"}.mdi-spellcheck:before{content:"\f04c6"}.mdi-sphere:before{content:"\f1954"}.mdi-sphere-off:before{content:"\f1955"}.mdi-spider:before{content:"\f11ea"}.mdi-spider-outline:before{content:"\f1c75"}.mdi-spider-thread:before{content:"\f11eb"}.mdi-spider-web:before{content:"\f0bca"}.mdi-spirit-level:before{content:"\f14f1"}.mdi-spoon-sugar:before{content:"\f1429"}.mdi-spotify:before{content:"\f04c7"}.mdi-spotlight:before{content:"\f04c8"}.mdi-spotlight-beam:before{content:"\f04c9"}.mdi-spray:before{content:"\f0665"}.mdi-spray-bottle:before{content:"\f0ae0"}.mdi-sprinkler:before{content:"\f105f"}.mdi-sprinkler-fire:before{content:"\f199d"}.mdi-sprinkler-variant:before{content:"\f1060"}.mdi-sprout:before{content:"\f0e66"}.mdi-sprout-outline:before{content:"\f0e67"}.mdi-square:before{content:"\f0764"}.mdi-square-circle:before{content:"\f1500"}.mdi-square-circle-outline:before{content:"\f1c50"}.mdi-square-edit-outline:before{content:"\f090c"}.mdi-square-medium:before{content:"\f0a13"}.mdi-square-medium-outline:before{content:"\f0a14"}.mdi-square-off:before{content:"\f12ee"}.mdi-square-off-outline:before{content:"\f12ef"}.mdi-square-opacity:before{content:"\f1854"}.mdi-square-outline:before{content:"\f0763"}.mdi-square-root:before{content:"\f0784"}.mdi-square-root-box:before{content:"\f09a3"}.mdi-square-rounded:before{content:"\f14fb"}.mdi-square-rounded-badge:before{content:"\f1a07"}.mdi-square-rounded-badge-outline:before{content:"\f1a08"}.mdi-square-rounded-outline:before{content:"\f14fc"}.mdi-square-small:before{content:"\f0a15"}.mdi-square-wave:before{content:"\f147b"}.mdi-squeegee:before{content:"\f0ae1"}.mdi-ssh:before{content:"\f08c0"}.mdi-stack-exchange:before{content:"\f060b"}.mdi-stack-overflow:before{content:"\f04cc"}.mdi-stackpath:before{content:"\f0359"}.mdi-stadium:before{content:"\f0ff9"}.mdi-stadium-outline:before{content:"\f1b03"}.mdi-stadium-variant:before{content:"\f0720"}.mdi-stairs:before{content:"\f04cd"}.mdi-stairs-box:before{content:"\f139e"}.mdi-stairs-down:before{content:"\f12be"}.mdi-stairs-up:before{content:"\f12bd"}.mdi-stamper:before{content:"\f0d39"}.mdi-standard-definition:before{content:"\f07ef"}.mdi-star:before{content:"\f04ce"}.mdi-star-box:before{content:"\f0a73"}.mdi-star-box-multiple:before{content:"\f1286"}.mdi-star-box-multiple-outline:before{content:"\f1287"}.mdi-star-box-outline:before{content:"\f0a74"}.mdi-star-check:before{content:"\f1566"}.mdi-star-check-outline:before{content:"\f156a"}.mdi-star-circle:before{content:"\f04cf"}.mdi-star-circle-outline:before{content:"\f09a4"}.mdi-star-cog:before{content:"\f1668"}.mdi-star-cog-outline:before{content:"\f1669"}.mdi-star-crescent:before{content:"\f0979"}.mdi-star-david:before{content:"\f097a"}.mdi-star-face:before{content:"\f09a5"}.mdi-star-four-points:before{content:"\f0ae2"}.mdi-star-four-points-box:before{content:"\f1c51"}.mdi-star-four-points-box-outline:before{content:"\f1c52"}.mdi-star-four-points-circle:before{content:"\f1c53"}.mdi-star-four-points-circle-outline:before{content:"\f1c54"}.mdi-star-four-points-outline:before{content:"\f0ae3"}.mdi-star-four-points-small:before{content:"\f1c55"}.mdi-star-half:before{content:"\f0246"}.mdi-star-half-full:before{content:"\f04d0"}.mdi-star-minus:before{content:"\f1564"}.mdi-star-minus-outline:before{content:"\f1568"}.mdi-star-off:before{content:"\f04d1"}.mdi-star-off-outline:before{content:"\f155b"}.mdi-star-outline:before{content:"\f04d2"}.mdi-star-plus:before{content:"\f1563"}.mdi-star-plus-outline:before{content:"\f1567"}.mdi-star-remove:before{content:"\f1565"}.mdi-star-remove-outline:before{content:"\f1569"}.mdi-star-settings:before{content:"\f166a"}.mdi-star-settings-outline:before{content:"\f166b"}.mdi-star-shooting:before{content:"\f1741"}.mdi-star-shooting-outline:before{content:"\f1742"}.mdi-star-three-points:before{content:"\f0ae4"}.mdi-star-three-points-outline:before{content:"\f0ae5"}.mdi-state-machine:before{content:"\f11ef"}.mdi-steam:before{content:"\f04d3"}.mdi-steering:before{content:"\f04d4"}.mdi-steering-off:before{content:"\f090e"}.mdi-step-backward:before{content:"\f04d5"}.mdi-step-backward-2:before{content:"\f04d6"}.mdi-step-forward:before{content:"\f04d7"}.mdi-step-forward-2:before{content:"\f04d8"}.mdi-stethoscope:before{content:"\f04d9"}.mdi-sticker:before{content:"\f1364"}.mdi-sticker-alert:before{content:"\f1365"}.mdi-sticker-alert-outline:before{content:"\f1366"}.mdi-sticker-check:before{content:"\f1367"}.mdi-sticker-check-outline:before{content:"\f1368"}.mdi-sticker-circle-outline:before{content:"\f05d0"}.mdi-sticker-emoji:before{content:"\f0785"}.mdi-sticker-minus:before{content:"\f1369"}.mdi-sticker-minus-outline:before{content:"\f136a"}.mdi-sticker-outline:before{content:"\f136b"}.mdi-sticker-plus:before{content:"\f136c"}.mdi-sticker-plus-outline:before{content:"\f136d"}.mdi-sticker-remove:before{content:"\f136e"}.mdi-sticker-remove-outline:before{content:"\f136f"}.mdi-sticker-text:before{content:"\f178e"}.mdi-sticker-text-outline:before{content:"\f178f"}.mdi-stocking:before{content:"\f04da"}.mdi-stomach:before{content:"\f1093"}.mdi-stool:before{content:"\f195d"}.mdi-stool-outline:before{content:"\f195e"}.mdi-stop:before{content:"\f04db"}.mdi-stop-circle:before{content:"\f0666"}.mdi-stop-circle-outline:before{content:"\f0667"}.mdi-storage-tank:before{content:"\f1a75"}.mdi-storage-tank-outline:before{content:"\f1a76"}.mdi-store:before{content:"\f04dc"}.mdi-store-24-hour:before{content:"\f04dd"}.mdi-store-alert:before{content:"\f18c1"}.mdi-store-alert-outline:before{content:"\f18c2"}.mdi-store-check:before{content:"\f18c3"}.mdi-store-check-outline:before{content:"\f18c4"}.mdi-store-clock:before{content:"\f18c5"}.mdi-store-clock-outline:before{content:"\f18c6"}.mdi-store-cog:before{content:"\f18c7"}.mdi-store-cog-outline:before{content:"\f18c8"}.mdi-store-edit:before{content:"\f18c9"}.mdi-store-edit-outline:before{content:"\f18ca"}.mdi-store-marker:before{content:"\f18cb"}.mdi-store-marker-outline:before{content:"\f18cc"}.mdi-store-minus:before{content:"\f165e"}.mdi-store-minus-outline:before{content:"\f18cd"}.mdi-store-off:before{content:"\f18ce"}.mdi-store-off-outline:before{content:"\f18cf"}.mdi-store-outline:before{content:"\f1361"}.mdi-store-plus:before{content:"\f165f"}.mdi-store-plus-outline:before{content:"\f18d0"}.mdi-store-remove:before{content:"\f1660"}.mdi-store-remove-outline:before{content:"\f18d1"}.mdi-store-search:before{content:"\f18d2"}.mdi-store-search-outline:before{content:"\f18d3"}.mdi-store-settings:before{content:"\f18d4"}.mdi-store-settings-outline:before{content:"\f18d5"}.mdi-storefront:before{content:"\f07c7"}.mdi-storefront-check:before{content:"\f1b7d"}.mdi-storefront-check-outline:before{content:"\f1b7e"}.mdi-storefront-edit:before{content:"\f1b7f"}.mdi-storefront-edit-outline:before{content:"\f1b80"}.mdi-storefront-minus:before{content:"\f1b83"}.mdi-storefront-minus-outline:before{content:"\f1b84"}.mdi-storefront-outline:before{content:"\f10c1"}.mdi-storefront-plus:before{content:"\f1b81"}.mdi-storefront-plus-outline:before{content:"\f1b82"}.mdi-storefront-remove:before{content:"\f1b85"}.mdi-storefront-remove-outline:before{content:"\f1b86"}.mdi-stove:before{content:"\f04de"}.mdi-strategy:before{content:"\f11d6"}.mdi-stretch-to-page:before{content:"\f0f2b"}.mdi-stretch-to-page-outline:before{content:"\f0f2c"}.mdi-string-lights:before{content:"\f12ba"}.mdi-string-lights-off:before{content:"\f12bb"}.mdi-subdirectory-arrow-left:before{content:"\f060c"}.mdi-subdirectory-arrow-right:before{content:"\f060d"}.mdi-submarine:before{content:"\f156c"}.mdi-subtitles:before{content:"\f0a16"}.mdi-subtitles-outline:before{content:"\f0a17"}.mdi-subway:before{content:"\f06ac"}.mdi-subway-alert-variant:before{content:"\f0d9d"}.mdi-subway-variant:before{content:"\f04df"}.mdi-summit:before{content:"\f0786"}.mdi-sun-angle:before{content:"\f1b27"}.mdi-sun-angle-outline:before{content:"\f1b28"}.mdi-sun-clock:before{content:"\f1a77"}.mdi-sun-clock-outline:before{content:"\f1a78"}.mdi-sun-compass:before{content:"\f19a5"}.mdi-sun-snowflake:before{content:"\f1796"}.mdi-sun-snowflake-variant:before{content:"\f1a79"}.mdi-sun-thermometer:before{content:"\f18d6"}.mdi-sun-thermometer-outline:before{content:"\f18d7"}.mdi-sun-wireless:before{content:"\f17fe"}.mdi-sun-wireless-outline:before{content:"\f17ff"}.mdi-sunglasses:before{content:"\f04e0"}.mdi-surfing:before{content:"\f1746"}.mdi-surround-sound:before{content:"\f05c5"}.mdi-surround-sound-2-0:before{content:"\f07f0"}.mdi-surround-sound-2-1:before{content:"\f1729"}.mdi-surround-sound-3-1:before{content:"\f07f1"}.mdi-surround-sound-5-1:before{content:"\f07f2"}.mdi-surround-sound-5-1-2:before{content:"\f172a"}.mdi-surround-sound-7-1:before{content:"\f07f3"}.mdi-svg:before{content:"\f0721"}.mdi-swap-horizontal:before{content:"\f04e1"}.mdi-swap-horizontal-bold:before{content:"\f0bcd"}.mdi-swap-horizontal-circle:before{content:"\f0fe1"}.mdi-swap-horizontal-circle-outline:before{content:"\f0fe2"}.mdi-swap-horizontal-hidden:before{content:"\f1d0e"}.mdi-swap-horizontal-variant:before{content:"\f08c1"}.mdi-swap-vertical:before{content:"\f04e2"}.mdi-swap-vertical-bold:before{content:"\f0bce"}.mdi-swap-vertical-circle:before{content:"\f0fe3"}.mdi-swap-vertical-circle-outline:before{content:"\f0fe4"}.mdi-swap-vertical-variant:before{content:"\f08c2"}.mdi-swim:before{content:"\f04e3"}.mdi-switch:before{content:"\f04e4"}.mdi-sword:before{content:"\f04e5"}.mdi-sword-cross:before{content:"\f0787"}.mdi-syllabary-hangul:before{content:"\f1333"}.mdi-syllabary-hiragana:before{content:"\f1334"}.mdi-syllabary-katakana:before{content:"\f1335"}.mdi-syllabary-katakana-halfwidth:before{content:"\f1336"}.mdi-symbol:before{content:"\f1501"}.mdi-symfony:before{content:"\f0ae6"}.mdi-synagogue:before{content:"\f1b04"}.mdi-synagogue-outline:before{content:"\f1b05"}.mdi-sync:before{content:"\f04e6"}.mdi-sync-alert:before{content:"\f04e7"}.mdi-sync-circle:before{content:"\f1378"}.mdi-sync-off:before{content:"\f04e8"}.mdi-tab:before{content:"\f04e9"}.mdi-tab-minus:before{content:"\f0b4b"}.mdi-tab-plus:before{content:"\f075c"}.mdi-tab-remove:before{content:"\f0b4c"}.mdi-tab-search:before{content:"\f199e"}.mdi-tab-unselected:before{content:"\f04ea"}.mdi-table:before{content:"\f04eb"}.mdi-table-account:before{content:"\f13b9"}.mdi-table-alert:before{content:"\f13ba"}.mdi-table-arrow-down:before{content:"\f13bb"}.mdi-table-arrow-left:before{content:"\f13bc"}.mdi-table-arrow-right:before{content:"\f13bd"}.mdi-table-arrow-up:before{content:"\f13be"}.mdi-table-border:before{content:"\f0a18"}.mdi-table-cancel:before{content:"\f13bf"}.mdi-table-chair:before{content:"\f1061"}.mdi-table-check:before{content:"\f13c0"}.mdi-table-clock:before{content:"\f13c1"}.mdi-table-cog:before{content:"\f13c2"}.mdi-table-column:before{content:"\f0835"}.mdi-table-column-plus-after:before{content:"\f04ec"}.mdi-table-column-plus-before:before{content:"\f04ed"}.mdi-table-column-remove:before{content:"\f04ee"}.mdi-table-column-width:before{content:"\f04ef"}.mdi-table-edit:before{content:"\f04f0"}.mdi-table-eye:before{content:"\f1094"}.mdi-table-eye-off:before{content:"\f13c3"}.mdi-table-filter:before{content:"\f1b8c"}.mdi-table-furniture:before{content:"\f05bc"}.mdi-table-headers-eye:before{content:"\f121d"}.mdi-table-headers-eye-off:before{content:"\f121e"}.mdi-table-heart:before{content:"\f13c4"}.mdi-table-key:before{content:"\f13c5"}.mdi-table-large:before{content:"\f04f1"}.mdi-table-large-plus:before{content:"\f0f87"}.mdi-table-large-remove:before{content:"\f0f88"}.mdi-table-lock:before{content:"\f13c6"}.mdi-table-merge-cells:before{content:"\f09a6"}.mdi-table-minus:before{content:"\f13c7"}.mdi-table-multiple:before{content:"\f13c8"}.mdi-table-network:before{content:"\f13c9"}.mdi-table-of-contents:before{content:"\f0836"}.mdi-table-off:before{content:"\f13ca"}.mdi-table-picnic:before{content:"\f1743"}.mdi-table-pivot:before{content:"\f183c"}.mdi-table-plus:before{content:"\f0a75"}.mdi-table-question:before{content:"\f1b21"}.mdi-table-refresh:before{content:"\f13a0"}.mdi-table-remove:before{content:"\f0a76"}.mdi-table-row:before{content:"\f0837"}.mdi-table-row-height:before{content:"\f04f2"}.mdi-table-row-plus-after:before{content:"\f04f3"}.mdi-table-row-plus-before:before{content:"\f04f4"}.mdi-table-row-remove:before{content:"\f04f5"}.mdi-table-search:before{content:"\f090f"}.mdi-table-settings:before{content:"\f0838"}.mdi-table-split-cell:before{content:"\f142a"}.mdi-table-star:before{content:"\f13cb"}.mdi-table-sync:before{content:"\f13a1"}.mdi-table-tennis:before{content:"\f0e68"}.mdi-tablet:before{content:"\f04f6"}.mdi-tablet-cellphone:before{content:"\f09a7"}.mdi-tablet-dashboard:before{content:"\f0ece"}.mdi-taco:before{content:"\f0762"}.mdi-tag:before{content:"\f04f9"}.mdi-tag-arrow-down:before{content:"\f172b"}.mdi-tag-arrow-down-outline:before{content:"\f172c"}.mdi-tag-arrow-left:before{content:"\f172d"}.mdi-tag-arrow-left-outline:before{content:"\f172e"}.mdi-tag-arrow-right:before{content:"\f172f"}.mdi-tag-arrow-right-outline:before{content:"\f1730"}.mdi-tag-arrow-up:before{content:"\f1731"}.mdi-tag-arrow-up-outline:before{content:"\f1732"}.mdi-tag-check:before{content:"\f1a7a"}.mdi-tag-check-outline:before{content:"\f1a7b"}.mdi-tag-edit:before{content:"\f1c9c"}.mdi-tag-edit-outline:before{content:"\f1c9d"}.mdi-tag-faces:before{content:"\f04fa"}.mdi-tag-heart:before{content:"\f068b"}.mdi-tag-heart-outline:before{content:"\f0bcf"}.mdi-tag-hidden:before{content:"\f1c76"}.mdi-tag-minus:before{content:"\f0910"}.mdi-tag-minus-outline:before{content:"\f121f"}.mdi-tag-multiple:before{content:"\f04fb"}.mdi-tag-multiple-outline:before{content:"\f12f7"}.mdi-tag-off:before{content:"\f1220"}.mdi-tag-off-outline:before{content:"\f1221"}.mdi-tag-outline:before{content:"\f04fc"}.mdi-tag-plus:before{content:"\f0722"}.mdi-tag-plus-outline:before{content:"\f1222"}.mdi-tag-remove:before{content:"\f0723"}.mdi-tag-remove-outline:before{content:"\f1223"}.mdi-tag-search:before{content:"\f1907"}.mdi-tag-search-outline:before{content:"\f1908"}.mdi-tag-text:before{content:"\f1224"}.mdi-tag-text-outline:before{content:"\f04fd"}.mdi-tailwind:before{content:"\f13ff"}.mdi-tally-mark-1:before{content:"\f1abc"}.mdi-tally-mark-2:before{content:"\f1abd"}.mdi-tally-mark-3:before{content:"\f1abe"}.mdi-tally-mark-4:before{content:"\f1abf"}.mdi-tally-mark-5:before{content:"\f1ac0"}.mdi-tangram:before{content:"\f04f8"}.mdi-tank:before{content:"\f0d3a"}.mdi-tanker-truck:before{content:"\f0fe5"}.mdi-tape-drive:before{content:"\f16df"}.mdi-tape-measure:before{content:"\f0b4d"}.mdi-target:before{content:"\f04fe"}.mdi-target-account:before{content:"\f0bd0"}.mdi-target-variant:before{content:"\f0a77"}.mdi-taxi:before{content:"\f04ff"}.mdi-tea:before{content:"\f0d9e"}.mdi-tea-outline:before{content:"\f0d9f"}.mdi-teamviewer:before{content:"\f0500"}.mdi-teddy-bear:before{content:"\f18fb"}.mdi-telescope:before{content:"\f0b4e"}.mdi-television:before{content:"\f0502"}.mdi-television-ambient-light:before{content:"\f1356"}.mdi-television-box:before{content:"\f0839"}.mdi-television-classic:before{content:"\f07f4"}.mdi-television-classic-off:before{content:"\f083a"}.mdi-television-guide:before{content:"\f0503"}.mdi-television-off:before{content:"\f083b"}.mdi-television-pause:before{content:"\f0f89"}.mdi-television-play:before{content:"\f0ecf"}.mdi-television-shimmer:before{content:"\f1110"}.mdi-television-speaker:before{content:"\f1b1b"}.mdi-television-speaker-off:before{content:"\f1b1c"}.mdi-television-stop:before{content:"\f0f8a"}.mdi-temperature-celsius:before{content:"\f0504"}.mdi-temperature-fahrenheit:before{content:"\f0505"}.mdi-temperature-kelvin:before{content:"\f0506"}.mdi-temple-buddhist:before{content:"\f1b06"}.mdi-temple-buddhist-outline:before{content:"\f1b07"}.mdi-temple-hindu:before{content:"\f1b08"}.mdi-temple-hindu-outline:before{content:"\f1b09"}.mdi-tennis:before{content:"\f0da0"}.mdi-tennis-ball:before{content:"\f0507"}.mdi-tennis-ball-outline:before{content:"\f1c5f"}.mdi-tent:before{content:"\f0508"}.mdi-terraform:before{content:"\f1062"}.mdi-terrain:before{content:"\f0509"}.mdi-test-tube:before{content:"\f0668"}.mdi-test-tube-empty:before{content:"\f0911"}.mdi-test-tube-off:before{content:"\f0912"}.mdi-text:before{content:"\f09a8"}.mdi-text-account:before{content:"\f1570"}.mdi-text-box:before{content:"\f021a"}.mdi-text-box-check:before{content:"\f0ea6"}.mdi-text-box-check-outline:before{content:"\f0ea7"}.mdi-text-box-edit:before{content:"\f1a7c"}.mdi-text-box-edit-outline:before{content:"\f1a7d"}.mdi-text-box-minus:before{content:"\f0ea8"}.mdi-text-box-minus-outline:before{content:"\f0ea9"}.mdi-text-box-multiple:before{content:"\f0ab7"}.mdi-text-box-multiple-outline:before{content:"\f0ab8"}.mdi-text-box-outline:before{content:"\f09ed"}.mdi-text-box-plus:before{content:"\f0eaa"}.mdi-text-box-plus-outline:before{content:"\f0eab"}.mdi-text-box-remove:before{content:"\f0eac"}.mdi-text-box-remove-outline:before{content:"\f0ead"}.mdi-text-box-search:before{content:"\f0eae"}.mdi-text-box-search-outline:before{content:"\f0eaf"}.mdi-text-long:before{content:"\f09aa"}.mdi-text-recognition:before{content:"\f113d"}.mdi-text-search:before{content:"\f13b8"}.mdi-text-search-variant:before{content:"\f1a7e"}.mdi-text-shadow:before{content:"\f0669"}.mdi-text-short:before{content:"\f09a9"}.mdi-texture:before{content:"\f050c"}.mdi-texture-box:before{content:"\f0fe6"}.mdi-theater:before{content:"\f050d"}.mdi-theme-light-dark:before{content:"\f050e"}.mdi-thermometer:before{content:"\f050f"}.mdi-thermometer-alert:before{content:"\f0e01"}.mdi-thermometer-auto:before{content:"\f1b0f"}.mdi-thermometer-bluetooth:before{content:"\f1895"}.mdi-thermometer-check:before{content:"\f1a7f"}.mdi-thermometer-chevron-down:before{content:"\f0e02"}.mdi-thermometer-chevron-up:before{content:"\f0e03"}.mdi-thermometer-high:before{content:"\f10c2"}.mdi-thermometer-lines:before{content:"\f0510"}.mdi-thermometer-low:before{content:"\f10c3"}.mdi-thermometer-minus:before{content:"\f0e04"}.mdi-thermometer-off:before{content:"\f1531"}.mdi-thermometer-plus:before{content:"\f0e05"}.mdi-thermometer-probe:before{content:"\f1b2b"}.mdi-thermometer-probe-off:before{content:"\f1b2c"}.mdi-thermometer-water:before{content:"\f1a80"}.mdi-thermostat:before{content:"\f0393"}.mdi-thermostat-auto:before{content:"\f1b17"}.mdi-thermostat-box:before{content:"\f0891"}.mdi-thermostat-box-auto:before{content:"\f1b18"}.mdi-thermostat-cog:before{content:"\f1c80"}.mdi-thought-bubble:before{content:"\f07f6"}.mdi-thought-bubble-outline:before{content:"\f07f7"}.mdi-thumb-down:before{content:"\f0511"}.mdi-thumb-down-outline:before{content:"\f0512"}.mdi-thumb-up:before{content:"\f0513"}.mdi-thumb-up-outline:before{content:"\f0514"}.mdi-thumbs-up-down:before{content:"\f0515"}.mdi-thumbs-up-down-outline:before{content:"\f1914"}.mdi-ticket:before{content:"\f0516"}.mdi-ticket-account:before{content:"\f0517"}.mdi-ticket-confirmation:before{content:"\f0518"}.mdi-ticket-confirmation-outline:before{content:"\f13aa"}.mdi-ticket-outline:before{content:"\f0913"}.mdi-ticket-percent:before{content:"\f0724"}.mdi-ticket-percent-outline:before{content:"\f142b"}.mdi-tie:before{content:"\f0519"}.mdi-tilde:before{content:"\f0725"}.mdi-tilde-off:before{content:"\f18f3"}.mdi-timelapse:before{content:"\f051a"}.mdi-timeline:before{content:"\f0bd1"}.mdi-timeline-alert:before{content:"\f0f95"}.mdi-timeline-alert-outline:before{content:"\f0f98"}.mdi-timeline-check:before{content:"\f1532"}.mdi-timeline-check-outline:before{content:"\f1533"}.mdi-timeline-clock:before{content:"\f11fb"}.mdi-timeline-clock-outline:before{content:"\f11fc"}.mdi-timeline-minus:before{content:"\f1534"}.mdi-timeline-minus-outline:before{content:"\f1535"}.mdi-timeline-outline:before{content:"\f0bd2"}.mdi-timeline-plus:before{content:"\f0f96"}.mdi-timeline-plus-outline:before{content:"\f0f97"}.mdi-timeline-question:before{content:"\f0f99"}.mdi-timeline-question-outline:before{content:"\f0f9a"}.mdi-timeline-remove:before{content:"\f1536"}.mdi-timeline-remove-outline:before{content:"\f1537"}.mdi-timeline-text:before{content:"\f0bd3"}.mdi-timeline-text-outline:before{content:"\f0bd4"}.mdi-timer:before{content:"\f13ab"}.mdi-timer-10:before{content:"\f051c"}.mdi-timer-3:before{content:"\f051d"}.mdi-timer-alert:before{content:"\f1acc"}.mdi-timer-alert-outline:before{content:"\f1acd"}.mdi-timer-cancel:before{content:"\f1ace"}.mdi-timer-cancel-outline:before{content:"\f1acf"}.mdi-timer-check:before{content:"\f1ad0"}.mdi-timer-check-outline:before{content:"\f1ad1"}.mdi-timer-cog:before{content:"\f1925"}.mdi-timer-cog-outline:before{content:"\f1926"}.mdi-timer-edit:before{content:"\f1ad2"}.mdi-timer-edit-outline:before{content:"\f1ad3"}.mdi-timer-lock:before{content:"\f1ad4"}.mdi-timer-lock-open:before{content:"\f1ad5"}.mdi-timer-lock-open-outline:before{content:"\f1ad6"}.mdi-timer-lock-outline:before{content:"\f1ad7"}.mdi-timer-marker:before{content:"\f1ad8"}.mdi-timer-marker-outline:before{content:"\f1ad9"}.mdi-timer-minus:before{content:"\f1ada"}.mdi-timer-minus-outline:before{content:"\f1adb"}.mdi-timer-music:before{content:"\f1adc"}.mdi-timer-music-outline:before{content:"\f1add"}.mdi-timer-off:before{content:"\f13ac"}.mdi-timer-off-outline:before{content:"\f051e"}.mdi-timer-outline:before{content:"\f051b"}.mdi-timer-pause:before{content:"\f1ade"}.mdi-timer-pause-outline:before{content:"\f1adf"}.mdi-timer-play:before{content:"\f1ae0"}.mdi-timer-play-outline:before{content:"\f1ae1"}.mdi-timer-plus:before{content:"\f1ae2"}.mdi-timer-plus-outline:before{content:"\f1ae3"}.mdi-timer-refresh:before{content:"\f1ae4"}.mdi-timer-refresh-outline:before{content:"\f1ae5"}.mdi-timer-remove:before{content:"\f1ae6"}.mdi-timer-remove-outline:before{content:"\f1ae7"}.mdi-timer-sand:before{content:"\f051f"}.mdi-timer-sand-complete:before{content:"\f199f"}.mdi-timer-sand-empty:before{content:"\f06ad"}.mdi-timer-sand-full:before{content:"\f078c"}.mdi-timer-sand-paused:before{content:"\f19a0"}.mdi-timer-settings:before{content:"\f1923"}.mdi-timer-settings-outline:before{content:"\f1924"}.mdi-timer-star:before{content:"\f1ae8"}.mdi-timer-star-outline:before{content:"\f1ae9"}.mdi-timer-stop:before{content:"\f1aea"}.mdi-timer-stop-outline:before{content:"\f1aeb"}.mdi-timer-sync:before{content:"\f1aec"}.mdi-timer-sync-outline:before{content:"\f1aed"}.mdi-timetable:before{content:"\f0520"}.mdi-tire:before{content:"\f1896"}.mdi-toaster:before{content:"\f1063"}.mdi-toaster-off:before{content:"\f11b7"}.mdi-toaster-oven:before{content:"\f0cd3"}.mdi-toggle-switch:before{content:"\f0521"}.mdi-toggle-switch-off:before{content:"\f0522"}.mdi-toggle-switch-off-outline:before{content:"\f0a19"}.mdi-toggle-switch-outline:before{content:"\f0a1a"}.mdi-toggle-switch-variant:before{content:"\f1a25"}.mdi-toggle-switch-variant-off:before{content:"\f1a26"}.mdi-toilet:before{content:"\f09ab"}.mdi-toolbox:before{content:"\f09ac"}.mdi-toolbox-outline:before{content:"\f09ad"}.mdi-tools:before{content:"\f1064"}.mdi-tooltip:before{content:"\f0523"}.mdi-tooltip-account:before{content:"\f000c"}.mdi-tooltip-cellphone:before{content:"\f183b"}.mdi-tooltip-check:before{content:"\f155c"}.mdi-tooltip-check-outline:before{content:"\f155d"}.mdi-tooltip-edit:before{content:"\f0524"}.mdi-tooltip-edit-outline:before{content:"\f12c5"}.mdi-tooltip-image:before{content:"\f0525"}.mdi-tooltip-image-outline:before{content:"\f0bd5"}.mdi-tooltip-minus:before{content:"\f155e"}.mdi-tooltip-minus-outline:before{content:"\f155f"}.mdi-tooltip-outline:before{content:"\f0526"}.mdi-tooltip-plus:before{content:"\f0bd6"}.mdi-tooltip-plus-outline:before{content:"\f0527"}.mdi-tooltip-question:before{content:"\f1bba"}.mdi-tooltip-question-outline:before{content:"\f1bbb"}.mdi-tooltip-remove:before{content:"\f1560"}.mdi-tooltip-remove-outline:before{content:"\f1561"}.mdi-tooltip-text:before{content:"\f0528"}.mdi-tooltip-text-outline:before{content:"\f0bd7"}.mdi-tooth:before{content:"\f08c3"}.mdi-tooth-outline:before{content:"\f0529"}.mdi-toothbrush:before{content:"\f1129"}.mdi-toothbrush-electric:before{content:"\f112c"}.mdi-toothbrush-paste:before{content:"\f112a"}.mdi-torch:before{content:"\f1606"}.mdi-tortoise:before{content:"\f0d3b"}.mdi-toslink:before{content:"\f12b8"}.mdi-touch-text-outline:before{content:"\f1c60"}.mdi-tournament:before{content:"\f09ae"}.mdi-tow-truck:before{content:"\f083c"}.mdi-tower-beach:before{content:"\f0681"}.mdi-tower-fire:before{content:"\f0682"}.mdi-town-hall:before{content:"\f1875"}.mdi-toy-brick:before{content:"\f1288"}.mdi-toy-brick-marker:before{content:"\f1289"}.mdi-toy-brick-marker-outline:before{content:"\f128a"}.mdi-toy-brick-minus:before{content:"\f128b"}.mdi-toy-brick-minus-outline:before{content:"\f128c"}.mdi-toy-brick-outline:before{content:"\f128d"}.mdi-toy-brick-plus:before{content:"\f128e"}.mdi-toy-brick-plus-outline:before{content:"\f128f"}.mdi-toy-brick-remove:before{content:"\f1290"}.mdi-toy-brick-remove-outline:before{content:"\f1291"}.mdi-toy-brick-search:before{content:"\f1292"}.mdi-toy-brick-search-outline:before{content:"\f1293"}.mdi-track-light:before{content:"\f0914"}.mdi-track-light-off:before{content:"\f1b01"}.mdi-trackpad:before{content:"\f07f8"}.mdi-trackpad-lock:before{content:"\f0933"}.mdi-tractor:before{content:"\f0892"}.mdi-tractor-variant:before{content:"\f14c4"}.mdi-trademark:before{content:"\f0a78"}.mdi-traffic-cone:before{content:"\f137c"}.mdi-traffic-light:before{content:"\f052b"}.mdi-traffic-light-outline:before{content:"\f182a"}.mdi-train:before{content:"\f052c"}.mdi-train-bus:before{content:"\f1cc7"}.mdi-train-car:before{content:"\f0bd8"}.mdi-train-car-autorack:before{content:"\f1b2d"}.mdi-train-car-box:before{content:"\f1b2e"}.mdi-train-car-box-full:before{content:"\f1b2f"}.mdi-train-car-box-open:before{content:"\f1b30"}.mdi-train-car-caboose:before{content:"\f1b31"}.mdi-train-car-centerbeam:before{content:"\f1b32"}.mdi-train-car-centerbeam-full:before{content:"\f1b33"}.mdi-train-car-container:before{content:"\f1b34"}.mdi-train-car-flatbed:before{content:"\f1b35"}.mdi-train-car-flatbed-car:before{content:"\f1b36"}.mdi-train-car-flatbed-tank:before{content:"\f1b37"}.mdi-train-car-gondola:before{content:"\f1b38"}.mdi-train-car-gondola-full:before{content:"\f1b39"}.mdi-train-car-hopper:before{content:"\f1b3a"}.mdi-train-car-hopper-covered:before{content:"\f1b3b"}.mdi-train-car-hopper-full:before{content:"\f1b3c"}.mdi-train-car-intermodal:before{content:"\f1b3d"}.mdi-train-car-passenger:before{content:"\f1733"}.mdi-train-car-passenger-door:before{content:"\f1734"}.mdi-train-car-passenger-door-open:before{content:"\f1735"}.mdi-train-car-passenger-variant:before{content:"\f1736"}.mdi-train-car-tank:before{content:"\f1b3e"}.mdi-train-variant:before{content:"\f08c4"}.mdi-tram:before{content:"\f052d"}.mdi-tram-side:before{content:"\f0fe7"}.mdi-transcribe:before{content:"\f052e"}.mdi-transcribe-close:before{content:"\f052f"}.mdi-transfer:before{content:"\f1065"}.mdi-transfer-down:before{content:"\f0da1"}.mdi-transfer-left:before{content:"\f0da2"}.mdi-transfer-right:before{content:"\f0530"}.mdi-transfer-up:before{content:"\f0da3"}.mdi-transit-connection:before{content:"\f0d3c"}.mdi-transit-connection-horizontal:before{content:"\f1546"}.mdi-transit-connection-variant:before{content:"\f0d3d"}.mdi-transit-detour:before{content:"\f0f8b"}.mdi-transit-skip:before{content:"\f1515"}.mdi-transit-transfer:before{content:"\f06ae"}.mdi-transition:before{content:"\f0915"}.mdi-transition-masked:before{content:"\f0916"}.mdi-translate:before{content:"\f05ca"}.mdi-translate-off:before{content:"\f0e06"}.mdi-translate-variant:before{content:"\f1b99"}.mdi-transmission-tower:before{content:"\f0d3e"}.mdi-transmission-tower-export:before{content:"\f192c"}.mdi-transmission-tower-import:before{content:"\f192d"}.mdi-transmission-tower-off:before{content:"\f19dd"}.mdi-trash-can:before{content:"\f0a79"}.mdi-trash-can-outline:before{content:"\f0a7a"}.mdi-tray:before{content:"\f1294"}.mdi-tray-alert:before{content:"\f1295"}.mdi-tray-arrow-down:before{content:"\f0120"}.mdi-tray-arrow-up:before{content:"\f011d"}.mdi-tray-full:before{content:"\f1296"}.mdi-tray-minus:before{content:"\f1297"}.mdi-tray-plus:before{content:"\f1298"}.mdi-tray-remove:before{content:"\f1299"}.mdi-treasure-chest:before{content:"\f0726"}.mdi-treasure-chest-outline:before{content:"\f1c77"}.mdi-tree:before{content:"\f0531"}.mdi-tree-outline:before{content:"\f0e69"}.mdi-trello:before{content:"\f0532"}.mdi-trending-down:before{content:"\f0533"}.mdi-trending-neutral:before{content:"\f0534"}.mdi-trending-up:before{content:"\f0535"}.mdi-triangle:before{content:"\f0536"}.mdi-triangle-down:before{content:"\f1c56"}.mdi-triangle-down-outline:before{content:"\f1c57"}.mdi-triangle-outline:before{content:"\f0537"}.mdi-triangle-small-down:before{content:"\f1a09"}.mdi-triangle-small-up:before{content:"\f1a0a"}.mdi-triangle-wave:before{content:"\f147c"}.mdi-triforce:before{content:"\f0bd9"}.mdi-trophy:before{content:"\f0538"}.mdi-trophy-award:before{content:"\f0539"}.mdi-trophy-broken:before{content:"\f0da4"}.mdi-trophy-outline:before{content:"\f053a"}.mdi-trophy-variant:before{content:"\f053b"}.mdi-trophy-variant-outline:before{content:"\f053c"}.mdi-truck:before{content:"\f053d"}.mdi-truck-alert:before{content:"\f19de"}.mdi-truck-alert-outline:before{content:"\f19df"}.mdi-truck-cargo-container:before{content:"\f18d8"}.mdi-truck-check:before{content:"\f0cd4"}.mdi-truck-check-outline:before{content:"\f129a"}.mdi-truck-delivery:before{content:"\f053e"}.mdi-truck-delivery-outline:before{content:"\f129b"}.mdi-truck-fast:before{content:"\f0788"}.mdi-truck-fast-outline:before{content:"\f129c"}.mdi-truck-flatbed:before{content:"\f1891"}.mdi-truck-minus:before{content:"\f19ae"}.mdi-truck-minus-outline:before{content:"\f19bd"}.mdi-truck-off-road:before{content:"\f1c9e"}.mdi-truck-off-road-off:before{content:"\f1c9f"}.mdi-truck-outline:before{content:"\f129d"}.mdi-truck-plus:before{content:"\f19ad"}.mdi-truck-plus-outline:before{content:"\f19bc"}.mdi-truck-remove:before{content:"\f19af"}.mdi-truck-remove-outline:before{content:"\f19be"}.mdi-truck-snowflake:before{content:"\f19a6"}.mdi-truck-trailer:before{content:"\f0727"}.mdi-trumpet:before{content:"\f1096"}.mdi-tshirt-crew:before{content:"\f0a7b"}.mdi-tshirt-crew-outline:before{content:"\f053f"}.mdi-tshirt-v:before{content:"\f0a7c"}.mdi-tshirt-v-outline:before{content:"\f0540"}.mdi-tsunami:before{content:"\f1a81"}.mdi-tumble-dryer:before{content:"\f0917"}.mdi-tumble-dryer-alert:before{content:"\f11ba"}.mdi-tumble-dryer-off:before{content:"\f11bb"}.mdi-tune:before{content:"\f062e"}.mdi-tune-variant:before{content:"\f1542"}.mdi-tune-vertical:before{content:"\f066a"}.mdi-tune-vertical-variant:before{content:"\f1543"}.mdi-tunnel:before{content:"\f183d"}.mdi-tunnel-outline:before{content:"\f183e"}.mdi-turbine:before{content:"\f1a82"}.mdi-turkey:before{content:"\f171b"}.mdi-turnstile:before{content:"\f0cd5"}.mdi-turnstile-outline:before{content:"\f0cd6"}.mdi-turtle:before{content:"\f0cd7"}.mdi-twitch:before{content:"\f0543"}.mdi-twitter:before{content:"\f0544"}.mdi-two-factor-authentication:before{content:"\f09af"}.mdi-typewriter:before{content:"\f0f2d"}.mdi-ubisoft:before{content:"\f0bda"}.mdi-ubuntu:before{content:"\f0548"}.mdi-ufo:before{content:"\f10c4"}.mdi-ufo-outline:before{content:"\f10c5"}.mdi-ultra-high-definition:before{content:"\f07f9"}.mdi-umbraco:before{content:"\f0549"}.mdi-umbrella:before{content:"\f054a"}.mdi-umbrella-beach:before{content:"\f188a"}.mdi-umbrella-beach-outline:before{content:"\f188b"}.mdi-umbrella-closed:before{content:"\f09b0"}.mdi-umbrella-closed-outline:before{content:"\f13e2"}.mdi-umbrella-closed-variant:before{content:"\f13e1"}.mdi-umbrella-outline:before{content:"\f054b"}.mdi-underwear-outline:before{content:"\f1d0f"}.mdi-undo:before{content:"\f054c"}.mdi-undo-variant:before{content:"\f054d"}.mdi-unfold-less-horizontal:before{content:"\f054e"}.mdi-unfold-less-vertical:before{content:"\f0760"}.mdi-unfold-more-horizontal:before{content:"\f054f"}.mdi-unfold-more-vertical:before{content:"\f0761"}.mdi-ungroup:before{content:"\f0550"}.mdi-unicode:before{content:"\f0ed0"}.mdi-unicorn:before{content:"\f15c2"}.mdi-unicorn-variant:before{content:"\f15c3"}.mdi-unicycle:before{content:"\f15e5"}.mdi-unity:before{content:"\f06af"}.mdi-unreal:before{content:"\f09b1"}.mdi-update:before{content:"\f06b0"}.mdi-upload:before{content:"\f0552"}.mdi-upload-box:before{content:"\f1d10"}.mdi-upload-box-outline:before{content:"\f1d11"}.mdi-upload-circle:before{content:"\f1d12"}.mdi-upload-circle-outline:before{content:"\f1d13"}.mdi-upload-lock:before{content:"\f1373"}.mdi-upload-lock-outline:before{content:"\f1374"}.mdi-upload-multiple:before{content:"\f083d"}.mdi-upload-multiple-outline:before{content:"\f1d14"}.mdi-upload-network:before{content:"\f06f6"}.mdi-upload-network-outline:before{content:"\f0cd8"}.mdi-upload-off:before{content:"\f10c6"}.mdi-upload-off-outline:before{content:"\f10c7"}.mdi-upload-outline:before{content:"\f0e07"}.mdi-usb:before{content:"\f0553"}.mdi-usb-c-port:before{content:"\f1cbf"}.mdi-usb-flash-drive:before{content:"\f129e"}.mdi-usb-flash-drive-outline:before{content:"\f129f"}.mdi-usb-port:before{content:"\f11f0"}.mdi-vacuum:before{content:"\f19a1"}.mdi-vacuum-outline:before{content:"\f19a2"}.mdi-valve:before{content:"\f1066"}.mdi-valve-closed:before{content:"\f1067"}.mdi-valve-open:before{content:"\f1068"}.mdi-van-passenger:before{content:"\f07fa"}.mdi-van-utility:before{content:"\f07fb"}.mdi-vanish:before{content:"\f07fc"}.mdi-vanish-quarter:before{content:"\f1554"}.mdi-vanity-light:before{content:"\f11e1"}.mdi-variable:before{content:"\f0ae7"}.mdi-variable-box:before{content:"\f1111"}.mdi-vector-arrange-above:before{content:"\f0554"}.mdi-vector-arrange-below:before{content:"\f0555"}.mdi-vector-bezier:before{content:"\f0ae8"}.mdi-vector-circle:before{content:"\f0556"}.mdi-vector-circle-variant:before{content:"\f0557"}.mdi-vector-combine:before{content:"\f0558"}.mdi-vector-curve:before{content:"\f0559"}.mdi-vector-difference:before{content:"\f055a"}.mdi-vector-difference-ab:before{content:"\f055b"}.mdi-vector-difference-ba:before{content:"\f055c"}.mdi-vector-ellipse:before{content:"\f0893"}.mdi-vector-intersection:before{content:"\f055d"}.mdi-vector-line:before{content:"\f055e"}.mdi-vector-link:before{content:"\f0fe8"}.mdi-vector-point:before{content:"\f01c4"}.mdi-vector-point-edit:before{content:"\f09e8"}.mdi-vector-point-minus:before{content:"\f1b78"}.mdi-vector-point-plus:before{content:"\f1b79"}.mdi-vector-point-select:before{content:"\f055f"}.mdi-vector-polygon:before{content:"\f0560"}.mdi-vector-polygon-variant:before{content:"\f1856"}.mdi-vector-polyline:before{content:"\f0561"}.mdi-vector-polyline-edit:before{content:"\f1225"}.mdi-vector-polyline-minus:before{content:"\f1226"}.mdi-vector-polyline-plus:before{content:"\f1227"}.mdi-vector-polyline-remove:before{content:"\f1228"}.mdi-vector-radius:before{content:"\f074a"}.mdi-vector-rectangle:before{content:"\f05c6"}.mdi-vector-selection:before{content:"\f0562"}.mdi-vector-square:before{content:"\f0001"}.mdi-vector-square-close:before{content:"\f1857"}.mdi-vector-square-edit:before{content:"\f18d9"}.mdi-vector-square-minus:before{content:"\f18da"}.mdi-vector-square-open:before{content:"\f1858"}.mdi-vector-square-plus:before{content:"\f18db"}.mdi-vector-square-remove:before{content:"\f18dc"}.mdi-vector-triangle:before{content:"\f0563"}.mdi-vector-union:before{content:"\f0564"}.mdi-vhs:before{content:"\f0a1b"}.mdi-vibrate:before{content:"\f0566"}.mdi-vibrate-off:before{content:"\f0cd9"}.mdi-video:before{content:"\f0567"}.mdi-video-2d:before{content:"\f1a1c"}.mdi-video-3d:before{content:"\f07fd"}.mdi-video-3d-off:before{content:"\f13d9"}.mdi-video-3d-variant:before{content:"\f0ed1"}.mdi-video-4k-box:before{content:"\f083e"}.mdi-video-account:before{content:"\f0919"}.mdi-video-box:before{content:"\f00fd"}.mdi-video-box-off:before{content:"\f00fe"}.mdi-video-check:before{content:"\f1069"}.mdi-video-check-outline:before{content:"\f106a"}.mdi-video-high-definition:before{content:"\f152e"}.mdi-video-image:before{content:"\f091a"}.mdi-video-input-antenna:before{content:"\f083f"}.mdi-video-input-component:before{content:"\f0840"}.mdi-video-input-hdmi:before{content:"\f0841"}.mdi-video-input-scart:before{content:"\f0f8c"}.mdi-video-input-svideo:before{content:"\f0842"}.mdi-video-marker:before{content:"\f19a9"}.mdi-video-marker-outline:before{content:"\f19aa"}.mdi-video-minus:before{content:"\f09b2"}.mdi-video-minus-outline:before{content:"\f02ba"}.mdi-video-off:before{content:"\f0568"}.mdi-video-off-outline:before{content:"\f0bdb"}.mdi-video-outline:before{content:"\f0bdc"}.mdi-video-plus:before{content:"\f09b3"}.mdi-video-plus-outline:before{content:"\f01d3"}.mdi-video-stabilization:before{content:"\f091b"}.mdi-video-standard-definition:before{content:"\f1ca0"}.mdi-video-switch:before{content:"\f0569"}.mdi-video-switch-outline:before{content:"\f0790"}.mdi-video-vintage:before{content:"\f0a1c"}.mdi-video-wireless:before{content:"\f0ed2"}.mdi-video-wireless-outline:before{content:"\f0ed3"}.mdi-view-agenda:before{content:"\f056a"}.mdi-view-agenda-outline:before{content:"\f11d8"}.mdi-view-array:before{content:"\f056b"}.mdi-view-array-outline:before{content:"\f1485"}.mdi-view-carousel:before{content:"\f056c"}.mdi-view-carousel-outline:before{content:"\f1486"}.mdi-view-column:before{content:"\f056d"}.mdi-view-column-outline:before{content:"\f1487"}.mdi-view-comfy:before{content:"\f0e6a"}.mdi-view-comfy-outline:before{content:"\f1488"}.mdi-view-compact:before{content:"\f0e6b"}.mdi-view-compact-outline:before{content:"\f0e6c"}.mdi-view-dashboard:before{content:"\f056e"}.mdi-view-dashboard-edit:before{content:"\f1947"}.mdi-view-dashboard-edit-outline:before{content:"\f1948"}.mdi-view-dashboard-outline:before{content:"\f0a1d"}.mdi-view-dashboard-variant:before{content:"\f0843"}.mdi-view-dashboard-variant-outline:before{content:"\f1489"}.mdi-view-day:before{content:"\f056f"}.mdi-view-day-outline:before{content:"\f148a"}.mdi-view-gallery:before{content:"\f1888"}.mdi-view-gallery-outline:before{content:"\f1889"}.mdi-view-grid:before{content:"\f0570"}.mdi-view-grid-compact:before{content:"\f1c61"}.mdi-view-grid-outline:before{content:"\f11d9"}.mdi-view-grid-plus:before{content:"\f0f8d"}.mdi-view-grid-plus-outline:before{content:"\f11da"}.mdi-view-headline:before{content:"\f0571"}.mdi-view-list:before{content:"\f0572"}.mdi-view-list-outline:before{content:"\f148b"}.mdi-view-module:before{content:"\f0573"}.mdi-view-module-outline:before{content:"\f148c"}.mdi-view-parallel:before{content:"\f0728"}.mdi-view-parallel-outline:before{content:"\f148d"}.mdi-view-quilt:before{content:"\f0574"}.mdi-view-quilt-outline:before{content:"\f148e"}.mdi-view-sequential:before{content:"\f0729"}.mdi-view-sequential-outline:before{content:"\f148f"}.mdi-view-split-horizontal:before{content:"\f0bcb"}.mdi-view-split-vertical:before{content:"\f0bcc"}.mdi-view-stream:before{content:"\f0575"}.mdi-view-stream-outline:before{content:"\f1490"}.mdi-view-week:before{content:"\f0576"}.mdi-view-week-outline:before{content:"\f1491"}.mdi-vimeo:before{content:"\f0577"}.mdi-violin:before{content:"\f060f"}.mdi-virtual-reality:before{content:"\f0894"}.mdi-virus:before{content:"\f13b6"}.mdi-virus-off:before{content:"\f18e1"}.mdi-virus-off-outline:before{content:"\f18e2"}.mdi-virus-outline:before{content:"\f13b7"}.mdi-vlc:before{content:"\f057c"}.mdi-voicemail:before{content:"\f057d"}.mdi-volcano:before{content:"\f1a83"}.mdi-volcano-outline:before{content:"\f1a84"}.mdi-volleyball:before{content:"\f09b4"}.mdi-volume-equal:before{content:"\f1b10"}.mdi-volume-high:before{content:"\f057e"}.mdi-volume-low:before{content:"\f057f"}.mdi-volume-medium:before{content:"\f0580"}.mdi-volume-minus:before{content:"\f075e"}.mdi-volume-mute:before{content:"\f075f"}.mdi-volume-off:before{content:"\f0581"}.mdi-volume-plus:before{content:"\f075d"}.mdi-volume-source:before{content:"\f1120"}.mdi-volume-variant-off:before{content:"\f0e08"}.mdi-volume-vibrate:before{content:"\f1121"}.mdi-vote:before{content:"\f0a1f"}.mdi-vote-outline:before{content:"\f0a20"}.mdi-vpn:before{content:"\f0582"}.mdi-vuejs:before{content:"\f0844"}.mdi-vuetify:before{content:"\f0e6d"}.mdi-walk:before{content:"\f0583"}.mdi-wall:before{content:"\f07fe"}.mdi-wall-fire:before{content:"\f1a11"}.mdi-wall-sconce:before{content:"\f091c"}.mdi-wall-sconce-flat:before{content:"\f091d"}.mdi-wall-sconce-flat-outline:before{content:"\f17c9"}.mdi-wall-sconce-flat-variant:before{content:"\f041c"}.mdi-wall-sconce-flat-variant-outline:before{content:"\f17ca"}.mdi-wall-sconce-outline:before{content:"\f17cb"}.mdi-wall-sconce-round:before{content:"\f0748"}.mdi-wall-sconce-round-outline:before{content:"\f17cc"}.mdi-wall-sconce-round-variant:before{content:"\f091e"}.mdi-wall-sconce-round-variant-outline:before{content:"\f17cd"}.mdi-wallet:before{content:"\f0584"}.mdi-wallet-bifold:before{content:"\f1c58"}.mdi-wallet-bifold-outline:before{content:"\f1c59"}.mdi-wallet-giftcard:before{content:"\f0585"}.mdi-wallet-membership:before{content:"\f0586"}.mdi-wallet-outline:before{content:"\f0bdd"}.mdi-wallet-plus:before{content:"\f0f8e"}.mdi-wallet-plus-outline:before{content:"\f0f8f"}.mdi-wallet-travel:before{content:"\f0587"}.mdi-wallpaper:before{content:"\f0e09"}.mdi-wan:before{content:"\f0588"}.mdi-wardrobe:before{content:"\f0f90"}.mdi-wardrobe-outline:before{content:"\f0f91"}.mdi-warehouse:before{content:"\f0f81"}.mdi-washing-machine:before{content:"\f072a"}.mdi-washing-machine-alert:before{content:"\f11bc"}.mdi-washing-machine-off:before{content:"\f11bd"}.mdi-watch:before{content:"\f0589"}.mdi-watch-export:before{content:"\f058a"}.mdi-watch-export-variant:before{content:"\f0895"}.mdi-watch-import:before{content:"\f058b"}.mdi-watch-import-variant:before{content:"\f0896"}.mdi-watch-variant:before{content:"\f0897"}.mdi-watch-vibrate:before{content:"\f06b1"}.mdi-watch-vibrate-off:before{content:"\f0cda"}.mdi-water:before{content:"\f058c"}.mdi-water-alert:before{content:"\f1502"}.mdi-water-alert-outline:before{content:"\f1503"}.mdi-water-boiler:before{content:"\f0f92"}.mdi-water-boiler-alert:before{content:"\f11b3"}.mdi-water-boiler-auto:before{content:"\f1b98"}.mdi-water-boiler-off:before{content:"\f11b4"}.mdi-water-check:before{content:"\f1504"}.mdi-water-check-outline:before{content:"\f1505"}.mdi-water-circle:before{content:"\f1806"}.mdi-water-minus:before{content:"\f1506"}.mdi-water-minus-outline:before{content:"\f1507"}.mdi-water-off:before{content:"\f058d"}.mdi-water-off-outline:before{content:"\f1508"}.mdi-water-opacity:before{content:"\f1855"}.mdi-water-outline:before{content:"\f0e0a"}.mdi-water-percent:before{content:"\f058e"}.mdi-water-percent-alert:before{content:"\f1509"}.mdi-water-plus:before{content:"\f150a"}.mdi-water-plus-outline:before{content:"\f150b"}.mdi-water-polo:before{content:"\f12a0"}.mdi-water-pump:before{content:"\f058f"}.mdi-water-pump-off:before{content:"\f0f93"}.mdi-water-remove:before{content:"\f150c"}.mdi-water-remove-outline:before{content:"\f150d"}.mdi-water-sync:before{content:"\f17c6"}.mdi-water-thermometer:before{content:"\f1a85"}.mdi-water-thermometer-outline:before{content:"\f1a86"}.mdi-water-well:before{content:"\f106b"}.mdi-water-well-outline:before{content:"\f106c"}.mdi-waterfall:before{content:"\f1849"}.mdi-watering-can:before{content:"\f1481"}.mdi-watering-can-outline:before{content:"\f1482"}.mdi-watermark:before{content:"\f0612"}.mdi-wave:before{content:"\f0f2e"}.mdi-wave-arrow-down:before{content:"\f1cb0"}.mdi-wave-arrow-up:before{content:"\f1cb1"}.mdi-wave-undercurrent:before{content:"\f1cc0"}.mdi-waveform:before{content:"\f147d"}.mdi-waves:before{content:"\f078d"}.mdi-waves-arrow-left:before{content:"\f1859"}.mdi-waves-arrow-right:before{content:"\f185a"}.mdi-waves-arrow-up:before{content:"\f185b"}.mdi-waze:before{content:"\f0bde"}.mdi-weather-cloudy:before{content:"\f0590"}.mdi-weather-cloudy-alert:before{content:"\f0f2f"}.mdi-weather-cloudy-arrow-right:before{content:"\f0e6e"}.mdi-weather-cloudy-clock:before{content:"\f18f6"}.mdi-weather-dust:before{content:"\f1b5a"}.mdi-weather-fog:before{content:"\f0591"}.mdi-weather-hail:before{content:"\f0592"}.mdi-weather-hazy:before{content:"\f0f30"}.mdi-weather-hurricane:before{content:"\f0898"}.mdi-weather-hurricane-outline:before{content:"\f1c78"}.mdi-weather-lightning:before{content:"\f0593"}.mdi-weather-lightning-rainy:before{content:"\f067e"}.mdi-weather-moonset:before{content:"\f1d15"}.mdi-weather-moonset-down:before{content:"\f1d16"}.mdi-weather-moonset-up:before{content:"\f1d17"}.mdi-weather-night:before{content:"\f0594"}.mdi-weather-night-partly-cloudy:before{content:"\f0f31"}.mdi-weather-partly-cloudy:before{content:"\f0595"}.mdi-weather-partly-lightning:before{content:"\f0f32"}.mdi-weather-partly-rainy:before{content:"\f0f33"}.mdi-weather-partly-snowy:before{content:"\f0f34"}.mdi-weather-partly-snowy-rainy:before{content:"\f0f35"}.mdi-weather-pouring:before{content:"\f0596"}.mdi-weather-rainy:before{content:"\f0597"}.mdi-weather-snowy:before{content:"\f0598"}.mdi-weather-snowy-heavy:before{content:"\f0f36"}.mdi-weather-snowy-rainy:before{content:"\f067f"}.mdi-weather-sunny:before{content:"\f0599"}.mdi-weather-sunny-alert:before{content:"\f0f37"}.mdi-weather-sunny-off:before{content:"\f14e4"}.mdi-weather-sunset:before{content:"\f059a"}.mdi-weather-sunset-down:before{content:"\f059b"}.mdi-weather-sunset-up:before{content:"\f059c"}.mdi-weather-tornado:before{content:"\f0f38"}.mdi-weather-windy:before{content:"\f059d"}.mdi-weather-windy-variant:before{content:"\f059e"}.mdi-web:before{content:"\f059f"}.mdi-web-box:before{content:"\f0f94"}.mdi-web-cancel:before{content:"\f1790"}.mdi-web-check:before{content:"\f0789"}.mdi-web-clock:before{content:"\f124a"}.mdi-web-minus:before{content:"\f10a0"}.mdi-web-off:before{content:"\f0a8e"}.mdi-web-plus:before{content:"\f0033"}.mdi-web-refresh:before{content:"\f1791"}.mdi-web-remove:before{content:"\f0551"}.mdi-web-sync:before{content:"\f1792"}.mdi-webcam:before{content:"\f05a0"}.mdi-webcam-off:before{content:"\f1737"}.mdi-webhook:before{content:"\f062f"}.mdi-webpack:before{content:"\f072b"}.mdi-webrtc:before{content:"\f1248"}.mdi-wechat:before{content:"\f0611"}.mdi-weight:before{content:"\f05a1"}.mdi-weight-gram:before{content:"\f0d3f"}.mdi-weight-kilogram:before{content:"\f05a2"}.mdi-weight-lifter:before{content:"\f115d"}.mdi-weight-pound:before{content:"\f09b5"}.mdi-whatsapp:before{content:"\f05a3"}.mdi-wheel-barrow:before{content:"\f14f2"}.mdi-wheelchair:before{content:"\f1a87"}.mdi-wheelchair-accessibility:before{content:"\f05a4"}.mdi-whistle:before{content:"\f09b6"}.mdi-whistle-outline:before{content:"\f12bc"}.mdi-white-balance-auto:before{content:"\f05a5"}.mdi-white-balance-incandescent:before{content:"\f05a6"}.mdi-white-balance-iridescent:before{content:"\f05a7"}.mdi-white-balance-sunny:before{content:"\f05a8"}.mdi-widgets:before{content:"\f072c"}.mdi-widgets-outline:before{content:"\f1355"}.mdi-wifi:before{content:"\f05a9"}.mdi-wifi-alert:before{content:"\f16b5"}.mdi-wifi-arrow-down:before{content:"\f16b6"}.mdi-wifi-arrow-left:before{content:"\f16b7"}.mdi-wifi-arrow-left-right:before{content:"\f16b8"}.mdi-wifi-arrow-right:before{content:"\f16b9"}.mdi-wifi-arrow-up:before{content:"\f16ba"}.mdi-wifi-arrow-up-down:before{content:"\f16bb"}.mdi-wifi-cancel:before{content:"\f16bc"}.mdi-wifi-check:before{content:"\f16bd"}.mdi-wifi-cog:before{content:"\f16be"}.mdi-wifi-lock:before{content:"\f16bf"}.mdi-wifi-lock-open:before{content:"\f16c0"}.mdi-wifi-marker:before{content:"\f16c1"}.mdi-wifi-minus:before{content:"\f16c2"}.mdi-wifi-off:before{content:"\f05aa"}.mdi-wifi-plus:before{content:"\f16c3"}.mdi-wifi-refresh:before{content:"\f16c4"}.mdi-wifi-remove:before{content:"\f16c5"}.mdi-wifi-settings:before{content:"\f16c6"}.mdi-wifi-star:before{content:"\f0e0b"}.mdi-wifi-strength-1:before{content:"\f091f"}.mdi-wifi-strength-1-alert:before{content:"\f0920"}.mdi-wifi-strength-1-lock:before{content:"\f0921"}.mdi-wifi-strength-1-lock-open:before{content:"\f16cb"}.mdi-wifi-strength-2:before{content:"\f0922"}.mdi-wifi-strength-2-alert:before{content:"\f0923"}.mdi-wifi-strength-2-lock:before{content:"\f0924"}.mdi-wifi-strength-2-lock-open:before{content:"\f16cc"}.mdi-wifi-strength-3:before{content:"\f0925"}.mdi-wifi-strength-3-alert:before{content:"\f0926"}.mdi-wifi-strength-3-lock:before{content:"\f0927"}.mdi-wifi-strength-3-lock-open:before{content:"\f16cd"}.mdi-wifi-strength-4:before{content:"\f0928"}.mdi-wifi-strength-4-alert:before{content:"\f0929"}.mdi-wifi-strength-4-lock:before{content:"\f092a"}.mdi-wifi-strength-4-lock-open:before{content:"\f16ce"}.mdi-wifi-strength-alert-outline:before{content:"\f092b"}.mdi-wifi-strength-lock-open-outline:before{content:"\f16cf"}.mdi-wifi-strength-lock-outline:before{content:"\f092c"}.mdi-wifi-strength-off:before{content:"\f092d"}.mdi-wifi-strength-off-outline:before{content:"\f092e"}.mdi-wifi-strength-outline:before{content:"\f092f"}.mdi-wifi-sync:before{content:"\f16c7"}.mdi-wikipedia:before{content:"\f05ac"}.mdi-wind-power:before{content:"\f1a88"}.mdi-wind-power-outline:before{content:"\f1a89"}.mdi-wind-turbine:before{content:"\f0da5"}.mdi-wind-turbine-alert:before{content:"\f19ab"}.mdi-wind-turbine-check:before{content:"\f19ac"}.mdi-window-close:before{content:"\f05ad"}.mdi-window-closed:before{content:"\f05ae"}.mdi-window-closed-variant:before{content:"\f11db"}.mdi-window-maximize:before{content:"\f05af"}.mdi-window-minimize:before{content:"\f05b0"}.mdi-window-open:before{content:"\f05b1"}.mdi-window-open-variant:before{content:"\f11dc"}.mdi-window-restore:before{content:"\f05b2"}.mdi-window-shutter:before{content:"\f111c"}.mdi-window-shutter-alert:before{content:"\f111d"}.mdi-window-shutter-auto:before{content:"\f1ba3"}.mdi-window-shutter-cog:before{content:"\f1a8a"}.mdi-window-shutter-open:before{content:"\f111e"}.mdi-window-shutter-settings:before{content:"\f1a8b"}.mdi-windsock:before{content:"\f15fa"}.mdi-wiper:before{content:"\f0ae9"}.mdi-wiper-wash:before{content:"\f0da6"}.mdi-wiper-wash-alert:before{content:"\f18df"}.mdi-wizard-hat:before{content:"\f1477"}.mdi-wordpress:before{content:"\f05b4"}.mdi-wrap:before{content:"\f05b6"}.mdi-wrap-disabled:before{content:"\f0bdf"}.mdi-wrench:before{content:"\f05b7"}.mdi-wrench-check:before{content:"\f1b8f"}.mdi-wrench-check-outline:before{content:"\f1b90"}.mdi-wrench-clock:before{content:"\f19a3"}.mdi-wrench-clock-outline:before{content:"\f1b93"}.mdi-wrench-cog:before{content:"\f1b91"}.mdi-wrench-cog-outline:before{content:"\f1b92"}.mdi-wrench-outline:before{content:"\f0be0"}.mdi-xamarin:before{content:"\f0845"}.mdi-xml:before{content:"\f05c0"}.mdi-xmpp:before{content:"\f07ff"}.mdi-yahoo:before{content:"\f0b4f"}.mdi-yeast:before{content:"\f05c1"}.mdi-yin-yang:before{content:"\f0680"}.mdi-yoga:before{content:"\f117c"}.mdi-youtube:before{content:"\f05c3"}.mdi-youtube-gaming:before{content:"\f0848"}.mdi-youtube-studio:before{content:"\f0847"}.mdi-youtube-subscription:before{content:"\f0d40"}.mdi-youtube-tv:before{content:"\f0448"}.mdi-yurt:before{content:"\f1516"}.mdi-z-wave:before{content:"\f0aea"}.mdi-zend:before{content:"\f0aeb"}.mdi-zigbee:before{content:"\f0d41"}.mdi-zip-box:before{content:"\f05c4"}.mdi-zip-box-outline:before{content:"\f0ffa"}.mdi-zip-disk:before{content:"\f0a23"}.mdi-zodiac-aquarius:before{content:"\f0a7d"}.mdi-zodiac-aries:before{content:"\f0a7e"}.mdi-zodiac-cancer:before{content:"\f0a7f"}.mdi-zodiac-capricorn:before{content:"\f0a80"}.mdi-zodiac-gemini:before{content:"\f0a81"}.mdi-zodiac-leo:before{content:"\f0a82"}.mdi-zodiac-libra:before{content:"\f0a83"}.mdi-zodiac-pisces:before{content:"\f0a84"}.mdi-zodiac-sagittarius:before{content:"\f0a85"}.mdi-zodiac-scorpio:before{content:"\f0a86"}.mdi-zodiac-taurus:before{content:"\f0a87"}.mdi-zodiac-virgo:before{content:"\f0a88"}.mdi-blank:before{content:"\f68c";visibility:hidden}.mdi-18px.mdi-set,.mdi-18px.mdi:before{font-size:18px}.mdi-24px.mdi-set,.mdi-24px.mdi:before{font-size:24px}.mdi-36px.mdi-set,.mdi-36px.mdi:before{font-size:36px}.mdi-48px.mdi-set,.mdi-48px.mdi:before{font-size:48px}.mdi-dark:before{color:#0000008a}.mdi-dark.mdi-inactive:before{color:#00000042}.mdi-light:before{color:#fff}.mdi-light.mdi-inactive:before{color:#ffffff4d}.mdi-rotate-45:before{-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.mdi-rotate-90:before{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.mdi-rotate-135:before{-webkit-transform:rotate(135deg);-ms-transform:rotate(135deg);transform:rotate(135deg)}.mdi-rotate-180:before{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.mdi-rotate-225:before{-webkit-transform:rotate(225deg);-ms-transform:rotate(225deg);transform:rotate(225deg)}.mdi-rotate-270:before{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.mdi-rotate-315:before{-webkit-transform:rotate(315deg);-ms-transform:rotate(315deg);transform:rotate(315deg)}.mdi-flip-h:before{-webkit-transform:scaleX(-1);transform:scaleX(-1);filter:FlipH;-ms-filter:"FlipH"}.mdi-flip-v:before{-webkit-transform:scaleY(-1);transform:scaleY(-1);filter:FlipV;-ms-filter:"FlipV"}.mdi-spin:before{-webkit-animation:mdi-spin 2s infinite linear;animation:mdi-spin 2s infinite linear}@-webkit-keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.flatpickr-calendar{background:transparent;opacity:0;display:none;text-align:center;visibility:hidden;padding:0;-webkit-animation:none;animation:none;direction:ltr;border:0;font-size:14px;line-height:24px;border-radius:5px;position:absolute;width:307.875px;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-touch-action:manipulation;touch-action:manipulation;background:#fff;-webkit-box-shadow:1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,.08);box-shadow:1px 0 #e6e6e6,-1px 0 #e6e6e6,0 1px #e6e6e6,0 -1px #e6e6e6,0 3px 13px #00000014}.flatpickr-calendar.open,.flatpickr-calendar.inline{opacity:1;max-height:640px;visibility:visible}.flatpickr-calendar.open{display:inline-block;z-index:99999}.flatpickr-calendar.animate.open{-webkit-animation:fpFadeInDown .3s cubic-bezier(.23,1,.32,1);animation:fpFadeInDown .3s cubic-bezier(.23,1,.32,1)}.flatpickr-calendar.inline{display:block;position:relative;top:2px}.flatpickr-calendar.static{position:absolute;top:calc(100% + 2px)}.flatpickr-calendar.static.open{z-index:999;display:block}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+1) .flatpickr-day.inRange:nth-child(7n+7){-webkit-box-shadow:none!important;box-shadow:none!important}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1){-webkit-box-shadow:-2px 0 0 #e6e6e6,5px 0 0 #e6e6e6;box-shadow:-2px 0 #e6e6e6,5px 0 #e6e6e6}.flatpickr-calendar .hasWeeks .dayContainer,.flatpickr-calendar .hasTime .dayContainer{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.flatpickr-calendar .hasWeeks .dayContainer{border-left:0}.flatpickr-calendar.hasTime .flatpickr-time{height:40px;border-top:1px solid #e6e6e6}.flatpickr-calendar.noCalendar.hasTime .flatpickr-time{height:auto}.flatpickr-calendar:before,.flatpickr-calendar:after{position:absolute;display:block;pointer-events:none;border:solid transparent;content:"";height:0;width:0;left:22px}.flatpickr-calendar.rightMost:before,.flatpickr-calendar.arrowRight:before,.flatpickr-calendar.rightMost:after,.flatpickr-calendar.arrowRight:after{left:auto;right:22px}.flatpickr-calendar.arrowCenter:before,.flatpickr-calendar.arrowCenter:after{left:50%;right:50%}.flatpickr-calendar:before{border-width:5px;margin:0 -5px}.flatpickr-calendar:after{border-width:4px;margin:0 -4px}.flatpickr-calendar.arrowTop:before,.flatpickr-calendar.arrowTop:after{bottom:100%}.flatpickr-calendar.arrowTop:before{border-bottom-color:#e6e6e6}.flatpickr-calendar.arrowTop:after{border-bottom-color:#fff}.flatpickr-calendar.arrowBottom:before,.flatpickr-calendar.arrowBottom:after{top:100%}.flatpickr-calendar.arrowBottom:before{border-top-color:#e6e6e6}.flatpickr-calendar.arrowBottom:after{border-top-color:#fff}.flatpickr-calendar:focus{outline:0}.flatpickr-wrapper{position:relative;display:inline-block}.flatpickr-months{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.flatpickr-months .flatpickr-month{background:transparent;color:#000000e6;fill:#000000e6;height:34px;line-height:1;text-align:center;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:hidden;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.flatpickr-months .flatpickr-prev-month,.flatpickr-months .flatpickr-next-month{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-decoration:none;cursor:pointer;position:absolute;top:0;height:34px;padding:10px;z-index:3;color:#000000e6;fill:#000000e6}.flatpickr-months .flatpickr-prev-month.flatpickr-disabled,.flatpickr-months .flatpickr-next-month.flatpickr-disabled{display:none}.flatpickr-months .flatpickr-prev-month i,.flatpickr-months .flatpickr-next-month i{position:relative}.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month,.flatpickr-months .flatpickr-next-month.flatpickr-prev-month{left:0}.flatpickr-months .flatpickr-prev-month.flatpickr-next-month,.flatpickr-months .flatpickr-next-month.flatpickr-next-month{right:0}.flatpickr-months .flatpickr-prev-month:hover,.flatpickr-months .flatpickr-next-month:hover{color:#959ea9}.flatpickr-months .flatpickr-prev-month:hover svg,.flatpickr-months .flatpickr-next-month:hover svg{fill:#f64747}.flatpickr-months .flatpickr-prev-month svg,.flatpickr-months .flatpickr-next-month svg{width:14px;height:14px}.flatpickr-months .flatpickr-prev-month svg path,.flatpickr-months .flatpickr-next-month svg path{-webkit-transition:fill .1s;transition:fill .1s;fill:inherit}.numInputWrapper{position:relative;height:auto}.numInputWrapper input,.numInputWrapper span{display:inline-block}.numInputWrapper input{width:100%}.numInputWrapper input::-ms-clear{display:none}.numInputWrapper input::-webkit-outer-spin-button,.numInputWrapper input::-webkit-inner-spin-button{margin:0;-webkit-appearance:none}.numInputWrapper span{position:absolute;right:0;width:14px;padding:0 4px 0 2px;height:50%;line-height:50%;opacity:0;cursor:pointer;border:1px solid rgba(57,57,57,.15);-webkit-box-sizing:border-box;box-sizing:border-box}.numInputWrapper span:hover{background:rgba(0,0,0,.1)}.numInputWrapper span:active{background:rgba(0,0,0,.2)}.numInputWrapper span:after{display:block;content:"";position:absolute}.numInputWrapper span.arrowUp{top:0;border-bottom:0}.numInputWrapper span.arrowUp:after{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:4px solid rgba(57,57,57,.6);top:26%}.numInputWrapper span.arrowDown{top:50%}.numInputWrapper span.arrowDown:after{border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid rgba(57,57,57,.6);top:40%}.numInputWrapper span svg{width:inherit;height:auto}.numInputWrapper span svg path{fill:#00000080}.numInputWrapper:hover{background:rgba(0,0,0,.05)}.numInputWrapper:hover span{opacity:1}.flatpickr-current-month{font-size:135%;line-height:inherit;font-weight:300;color:inherit;position:absolute;width:75%;left:12.5%;padding:7.48px 0 0;line-height:1;height:34px;display:inline-block;text-align:center;-webkit-transform:translate3d(0,0,0);transform:translate(0)}.flatpickr-current-month span.cur-month{font-family:inherit;font-weight:700;color:inherit;display:inline-block;margin-left:.5ch;padding:0}.flatpickr-current-month span.cur-month:hover{background:rgba(0,0,0,.05)}.flatpickr-current-month .numInputWrapper{width:6ch;width:7ch\fffd;display:inline-block}.flatpickr-current-month .numInputWrapper span.arrowUp:after{border-bottom-color:#000000e6}.flatpickr-current-month .numInputWrapper span.arrowDown:after{border-top-color:#000000e6}.flatpickr-current-month input.cur-year{background:transparent;-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;cursor:text;padding:0 0 0 .5ch;margin:0;display:inline-block;font-size:inherit;font-family:inherit;font-weight:300;line-height:inherit;height:auto;border:0;border-radius:0;vertical-align:initial;-webkit-appearance:textfield;-moz-appearance:textfield;appearance:textfield}.flatpickr-current-month input.cur-year:focus{outline:0}.flatpickr-current-month input.cur-year[disabled],.flatpickr-current-month input.cur-year[disabled]:hover{font-size:100%;color:#00000080;background:transparent;pointer-events:none}.flatpickr-current-month .flatpickr-monthDropdown-months{appearance:menulist;background:transparent;border:none;border-radius:0;box-sizing:border-box;color:inherit;cursor:pointer;font-size:inherit;font-family:inherit;font-weight:300;height:auto;line-height:inherit;margin:-1px 0 0;outline:none;padding:0 0 0 .5ch;position:relative;vertical-align:initial;-webkit-box-sizing:border-box;-webkit-appearance:menulist;-moz-appearance:menulist;width:auto}.flatpickr-current-month .flatpickr-monthDropdown-months:focus,.flatpickr-current-month .flatpickr-monthDropdown-months:active{outline:none}.flatpickr-current-month .flatpickr-monthDropdown-months:hover{background:rgba(0,0,0,.05)}.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month{background-color:transparent;outline:none;padding:0}.flatpickr-weekdays{background:transparent;text-align:center;overflow:hidden;width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:28px}.flatpickr-weekdays .flatpickr-weekdaycontainer{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}span.flatpickr-weekday{cursor:default;font-size:90%;background:transparent;color:#0000008a;line-height:1;margin:0;text-align:center;display:block;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-weight:bolder}.dayContainer,.flatpickr-weeks{padding:1px 0 0}.flatpickr-days{position:relative;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:start;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;width:307.875px}.flatpickr-days:focus{outline:0}.dayContainer{padding:0;outline:0;text-align:left;width:307.875px;min-width:307.875px;max-width:307.875px;-webkit-box-sizing:border-box;box-sizing:border-box;display:inline-block;display:-ms-flexbox;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-wrap:wrap;-ms-flex-pack:justify;-webkit-justify-content:space-around;justify-content:space-around;-webkit-transform:translate3d(0,0,0);transform:translate(0);opacity:1}.dayContainer+.dayContainer{-webkit-box-shadow:-1px 0 0 #e6e6e6;box-shadow:-1px 0 #e6e6e6}.flatpickr-day{background:none;border:1px solid transparent;border-radius:150px;-webkit-box-sizing:border-box;box-sizing:border-box;color:#393939;cursor:pointer;font-weight:400;width:14.2857143%;-webkit-flex-basis:14.2857143%;-ms-flex-preferred-size:14.2857143%;flex-basis:14.2857143%;max-width:39px;height:39px;line-height:39px;margin:0;display:inline-block;position:relative;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center}.flatpickr-day.inRange,.flatpickr-day.prevMonthDay.inRange,.flatpickr-day.nextMonthDay.inRange,.flatpickr-day.today.inRange,.flatpickr-day.prevMonthDay.today.inRange,.flatpickr-day.nextMonthDay.today.inRange,.flatpickr-day:hover,.flatpickr-day.prevMonthDay:hover,.flatpickr-day.nextMonthDay:hover,.flatpickr-day:focus,.flatpickr-day.prevMonthDay:focus,.flatpickr-day.nextMonthDay:focus{cursor:pointer;outline:0;background:#e6e6e6;border-color:#e6e6e6}.flatpickr-day.today{border-color:#959ea9}.flatpickr-day.today:hover,.flatpickr-day.today:focus{border-color:#959ea9;background:#959ea9;color:#fff}.flatpickr-day.selected,.flatpickr-day.startRange,.flatpickr-day.endRange,.flatpickr-day.selected.inRange,.flatpickr-day.startRange.inRange,.flatpickr-day.endRange.inRange,.flatpickr-day.selected:focus,.flatpickr-day.startRange:focus,.flatpickr-day.endRange:focus,.flatpickr-day.selected:hover,.flatpickr-day.startRange:hover,.flatpickr-day.endRange:hover,.flatpickr-day.selected.prevMonthDay,.flatpickr-day.startRange.prevMonthDay,.flatpickr-day.endRange.prevMonthDay,.flatpickr-day.selected.nextMonthDay,.flatpickr-day.startRange.nextMonthDay,.flatpickr-day.endRange.nextMonthDay{background:#569ff7;-webkit-box-shadow:none;box-shadow:none;color:#fff;border-color:#569ff7}.flatpickr-day.selected.startRange,.flatpickr-day.startRange.startRange,.flatpickr-day.endRange.startRange{border-radius:50px 0 0 50px}.flatpickr-day.selected.endRange,.flatpickr-day.startRange.endRange,.flatpickr-day.endRange.endRange{border-radius:0 50px 50px 0}.flatpickr-day.selected.startRange+.endRange:not(:nth-child(7n+1)),.flatpickr-day.startRange.startRange+.endRange:not(:nth-child(7n+1)),.flatpickr-day.endRange.startRange+.endRange:not(:nth-child(7n+1)){-webkit-box-shadow:-10px 0 0 #569ff7;box-shadow:-10px 0 #569ff7}.flatpickr-day.selected.startRange.endRange,.flatpickr-day.startRange.startRange.endRange,.flatpickr-day.endRange.startRange.endRange{border-radius:50px}.flatpickr-day.inRange{border-radius:0;-webkit-box-shadow:-5px 0 0 #e6e6e6,5px 0 0 #e6e6e6;box-shadow:-5px 0 #e6e6e6,5px 0 #e6e6e6}.flatpickr-day.flatpickr-disabled,.flatpickr-day.flatpickr-disabled:hover,.flatpickr-day.prevMonthDay,.flatpickr-day.nextMonthDay,.flatpickr-day.notAllowed,.flatpickr-day.notAllowed.prevMonthDay,.flatpickr-day.notAllowed.nextMonthDay{color:#3939394d;background:transparent;border-color:transparent;cursor:default}.flatpickr-day.flatpickr-disabled,.flatpickr-day.flatpickr-disabled:hover{cursor:not-allowed;color:#3939391a}.flatpickr-day.week.selected{border-radius:0;-webkit-box-shadow:-5px 0 0 #569ff7,5px 0 0 #569ff7;box-shadow:-5px 0 #569ff7,5px 0 #569ff7}.flatpickr-day.hidden{visibility:hidden}.rangeMode .flatpickr-day{margin-top:1px}.flatpickr-weekwrapper{float:left}.flatpickr-weekwrapper .flatpickr-weeks{padding:0 12px;-webkit-box-shadow:1px 0 0 #e6e6e6;box-shadow:1px 0 #e6e6e6}.flatpickr-weekwrapper .flatpickr-weekday{float:none;width:100%;line-height:28px}.flatpickr-weekwrapper span.flatpickr-day,.flatpickr-weekwrapper span.flatpickr-day:hover{display:block;width:100%;max-width:none;color:#3939394d;background:transparent;cursor:default;border:none}.flatpickr-innerContainer{display:block;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden}.flatpickr-rContainer{display:inline-block;padding:0;-webkit-box-sizing:border-box;box-sizing:border-box}.flatpickr-time{text-align:center;outline:0;display:block;height:0;line-height:40px;max-height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.flatpickr-time:after{content:"";display:table;clear:both}.flatpickr-time .numInputWrapper{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;width:40%;height:40px;float:left}.flatpickr-time .numInputWrapper span.arrowUp:after{border-bottom-color:#393939}.flatpickr-time .numInputWrapper span.arrowDown:after{border-top-color:#393939}.flatpickr-time.hasSeconds .numInputWrapper{width:26%}.flatpickr-time.time24hr .numInputWrapper{width:49%}.flatpickr-time input{background:transparent;-webkit-box-shadow:none;box-shadow:none;border:0;border-radius:0;text-align:center;margin:0;padding:0;height:inherit;line-height:inherit;color:#393939;font-size:14px;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:textfield;-moz-appearance:textfield;appearance:textfield}.flatpickr-time input.flatpickr-hour{font-weight:700}.flatpickr-time input.flatpickr-minute,.flatpickr-time input.flatpickr-second{font-weight:400}.flatpickr-time input:focus{outline:0;border:0}.flatpickr-time .flatpickr-time-separator,.flatpickr-time .flatpickr-am-pm{height:inherit;float:left;line-height:inherit;color:#393939;font-weight:700;width:2%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}.flatpickr-time .flatpickr-am-pm{outline:0;width:18%;cursor:pointer;text-align:center;font-weight:400}.flatpickr-time input:hover,.flatpickr-time .flatpickr-am-pm:hover,.flatpickr-time input:focus,.flatpickr-time .flatpickr-am-pm:focus{background:#eee}.flatpickr-input[readonly]{cursor:pointer}@-webkit-keyframes fpFadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translateY(-20px)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate(0)}}@keyframes fpFadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translateY(-20px)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate(0)}}.grid-stack{position:relative}.grid-stack-rtl{direction:ltr}.grid-stack-rtl>.grid-stack-item{direction:rtl}.grid-stack-placeholder>.placeholder-content{background-color:#0000001a;margin:0;position:absolute;width:auto;z-index:0!important}.grid-stack>.grid-stack-item{position:absolute;padding:0}.grid-stack>.grid-stack-item>.grid-stack-item-content{margin:0;position:absolute;width:auto;overflow-x:hidden;overflow-y:auto}.grid-stack>.grid-stack-item.size-to-content:not(.size-to-content-max)>.grid-stack-item-content{overflow-y:hidden}.grid-stack-item>.ui-resizable-handle{position:absolute;font-size:.1px;display:block;-ms-touch-action:none;touch-action:none}.grid-stack-item.ui-resizable-autohide>.ui-resizable-handle,.grid-stack-item.ui-resizable-disabled>.ui-resizable-handle{display:none}.grid-stack-item>.ui-resizable-ne,.grid-stack-item>.ui-resizable-nw,.grid-stack-item>.ui-resizable-se,.grid-stack-item>.ui-resizable-sw{background-image:url('data:image/svg+xml;utf8,');background-repeat:no-repeat;background-position:center}.grid-stack-item>.ui-resizable-ne{transform:translateY(10px) rotate(45deg)}.grid-stack-item>.ui-resizable-sw{transform:rotate(45deg)}.grid-stack-item>.ui-resizable-nw{transform:translateY(10px) rotate(-45deg)}.grid-stack-item>.ui-resizable-se{transform:rotate(-45deg)}.grid-stack-item>.ui-resizable-nw{cursor:nw-resize;width:20px;height:20px;top:0}.grid-stack-item>.ui-resizable-n{cursor:n-resize;height:10px;top:0;left:25px;right:25px}.grid-stack-item>.ui-resizable-ne{cursor:ne-resize;width:20px;height:20px;top:0}.grid-stack-item>.ui-resizable-e{cursor:e-resize;width:10px;top:15px;bottom:15px}.grid-stack-item>.ui-resizable-se{cursor:se-resize;width:20px;height:20px}.grid-stack-item>.ui-resizable-s{cursor:s-resize;height:10px;left:25px;bottom:0;right:25px}.grid-stack-item>.ui-resizable-sw{cursor:sw-resize;width:20px;height:20px}.grid-stack-item>.ui-resizable-w{cursor:w-resize;width:10px;top:15px;bottom:15px}.grid-stack-item.ui-draggable-dragging>.ui-resizable-handle{display:none!important}.grid-stack-item.ui-draggable-dragging{will-change:left,top;cursor:move}.grid-stack-item.ui-resizable-resizing{will-change:width,height}.ui-draggable-dragging,.ui-resizable-resizing{z-index:10000}.ui-draggable-dragging>.grid-stack-item-content,.ui-resizable-resizing>.grid-stack-item-content{box-shadow:1px 4px 6px #0003;opacity:.8}.grid-stack-animate,.grid-stack-animate .grid-stack-item{transition:left .3s,top .3s,height .3s,width .3s}.grid-stack-animate .grid-stack-item.grid-stack-placeholder,.grid-stack-animate .grid-stack-item.ui-draggable-dragging,.grid-stack-animate .grid-stack-item.ui-resizable-resizing{transition:left 0s,top 0s,height 0s,width 0s}.grid-stack>.grid-stack-item[gs-y="0"]{top:0}.grid-stack>.grid-stack-item[gs-x="0"]{left:0}.gs-12>.grid-stack-item{width:8.333%}.gs-12>.grid-stack-item[gs-x="1"]{left:8.333%}.gs-12>.grid-stack-item[gs-w="2"]{width:16.667%}.gs-12>.grid-stack-item[gs-x="2"]{left:16.667%}.gs-12>.grid-stack-item[gs-w="3"]{width:25%}.gs-12>.grid-stack-item[gs-x="3"]{left:25%}.gs-12>.grid-stack-item[gs-w="4"]{width:33.333%}.gs-12>.grid-stack-item[gs-x="4"]{left:33.333%}.gs-12>.grid-stack-item[gs-w="5"]{width:41.667%}.gs-12>.grid-stack-item[gs-x="5"]{left:41.667%}.gs-12>.grid-stack-item[gs-w="6"]{width:50%}.gs-12>.grid-stack-item[gs-x="6"]{left:50%}.gs-12>.grid-stack-item[gs-w="7"]{width:58.333%}.gs-12>.grid-stack-item[gs-x="7"]{left:58.333%}.gs-12>.grid-stack-item[gs-w="8"]{width:66.667%}.gs-12>.grid-stack-item[gs-x="8"]{left:66.667%}.gs-12>.grid-stack-item[gs-w="9"]{width:75%}.gs-12>.grid-stack-item[gs-x="9"]{left:75%}.gs-12>.grid-stack-item[gs-w="10"]{width:83.333%}.gs-12>.grid-stack-item[gs-x="10"]{left:83.333%}.gs-12>.grid-stack-item[gs-w="11"]{width:91.667%}.gs-12>.grid-stack-item[gs-x="11"]{left:91.667%}.gs-12>.grid-stack-item[gs-w="12"]{width:100%}.gs-1>.grid-stack-item{width:100%}@font-face{font-family:Inter;font-style:normal;font-weight:100;font-display:swap;src:url(./Inter-Thin-TCINWRKS.woff2?v=3.18) format("woff2"),url(./Inter-Thin-MEKOZMGA.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:100;font-display:swap;src:url(./Inter-ThinItalic-WRULPH3M.woff2?v=3.18) format("woff2"),url(./Inter-ThinItalic-ZFOL6JFL.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:200;font-display:swap;src:url(./Inter-ExtraLight-DVW3SATI.woff2?v=3.18) format("woff2"),url(./Inter-ExtraLight-A7NHN7KT.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:200;font-display:swap;src:url(./Inter-ExtraLightItalic-KXZYPKOE.woff2?v=3.18) format("woff2"),url(./Inter-ExtraLightItalic-SKGXKLLA.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:300;font-display:swap;src:url(./Inter-Light-XBRUIIRZ.woff2?v=3.18) format("woff2"),url(./Inter-Light-ARG5Y6MT.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:300;font-display:swap;src:url(./Inter-LightItalic-4OWY6V5R.woff2?v=3.18) format("woff2"),url(./Inter-LightItalic-WFS25IQZ.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(./Inter-Regular-373XMOLM.woff2?v=3.18) format("woff2"),url(./Inter-Regular-DR3VC6YG.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:400;font-display:swap;src:url(./Inter-Italic-RW2ZALTO.woff2?v=3.18) format("woff2"),url(./Inter-Italic-GHDVOOKA.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(./Inter-Medium-JV63DMR7.woff2?v=3.18) format("woff2"),url(./Inter-Medium-KH2CGZVE.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:500;font-display:swap;src:url(./Inter-MediumItalic-P7H6BBYU.woff2?v=3.18) format("woff2"),url(./Inter-MediumItalic-PO2E4FIG.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(./Inter-SemiBold-UMRILHOG.woff2?v=3.18) format("woff2"),url(./Inter-SemiBold-TJ6HPMMH.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:600;font-display:swap;src:url(./Inter-SemiBoldItalic-ABO2DCC7.woff2?v=3.18) format("woff2"),url(./Inter-SemiBoldItalic-DM4JJSFU.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(./Inter-Bold-6AVOS7AV.woff2?v=3.18) format("woff2"),url(./Inter-Bold-RTIBL5DO.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:700;font-display:swap;src:url(./Inter-BoldItalic-AID2XGS3.woff2?v=3.18) format("woff2"),url(./Inter-BoldItalic-UU7SOVRI.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:800;font-display:swap;src:url(./Inter-ExtraBold-VAPCTO5F.woff2?v=3.18) format("woff2"),url(./Inter-ExtraBold-B5RSZK6R.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:800;font-display:swap;src:url(./Inter-ExtraBoldItalic-W26CJYY7.woff2?v=3.18) format("woff2"),url(./Inter-ExtraBoldItalic-R7DLWYIK.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:900;font-display:swap;src:url(./Inter-Black-NU3KAVPI.woff2?v=3.18) format("woff2"),url(./Inter-Black-7VL4YR6G.woff?v=3.18) format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:900;font-display:swap;src:url(./Inter-BlackItalic-4BVAVUVI.woff2?v=3.18) format("woff2"),url(./Inter-BlackItalic-HJLUEYVW.woff?v=3.18) format("woff")}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(./Inter-roman.var-EDENVCUS.woff2?v=3.18) format("woff2")}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(./Inter-italic.var-7LS4VCXC.woff2?v=3.18) format("woff2")}@font-face{font-family:Inter var experimental;font-weight:100 900;font-display:swap;font-style:oblique 0deg 10deg;src:url(./Inter.var-FTIQFLGO.woff2?v=3.18) format("woff2")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:100;src:local("Roboto Mono Thin "),local("Roboto Mono-Thin"),url(./roboto-mono-latin-100-JQJ4Z5FD.woff2) format("woff2"),url(./roboto-mono-latin-100-SMKVIC22.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:100;src:local("Roboto Mono Thin italic"),local("Roboto Mono-Thinitalic"),url(./roboto-mono-latin-100italic-SF7I3UQS.woff2) format("woff2"),url(./roboto-mono-latin-100italic-DARDHGP2.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:200;src:local("Roboto Mono Extra Light "),local("Roboto Mono-Extra Light"),url(./roboto-mono-latin-200-GAB3DPCB.woff2) format("woff2"),url(./roboto-mono-latin-200-GNXGWAU7.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:200;src:local("Roboto Mono Extra Light italic"),local("Roboto Mono-Extra Lightitalic"),url(./roboto-mono-latin-200italic-YC3AN6RQ.woff2) format("woff2"),url(./roboto-mono-latin-200italic-MBEOZD5A.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:300;src:local("Roboto Mono Light "),local("Roboto Mono-Light"),url(./roboto-mono-latin-300-76QG53G7.woff2) format("woff2"),url(./roboto-mono-latin-300-KB4HCXJB.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:300;src:local("Roboto Mono Light italic"),local("Roboto Mono-Lightitalic"),url(./roboto-mono-latin-300italic-ATWQWJG4.woff2) format("woff2"),url(./roboto-mono-latin-300italic-UCTTXECO.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:400;src:local("Roboto Mono Regular "),local("Roboto Mono-Regular"),url(./roboto-mono-latin-400-OKRWGZOX.woff2) format("woff2"),url(./roboto-mono-latin-400-L25YYWY3.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:400;src:local("Roboto Mono Regular italic"),local("Roboto Mono-Regularitalic"),url(./roboto-mono-latin-400italic-UPQRVZWX.woff2) format("woff2"),url(./roboto-mono-latin-400italic-3G7XMJ7A.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:500;src:local("Roboto Mono Medium "),local("Roboto Mono-Medium"),url(./roboto-mono-latin-500-67YXJMLO.woff2) format("woff2"),url(./roboto-mono-latin-500-4ZB2P7GK.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:500;src:local("Roboto Mono Medium italic"),local("Roboto Mono-Mediumitalic"),url(./roboto-mono-latin-500italic-U7WRIR22.woff2) format("woff2"),url(./roboto-mono-latin-500italic-RXGCSZS4.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:600;src:local("Roboto Mono SemiBold "),local("Roboto Mono-SemiBold"),url(./roboto-mono-latin-600-ZRZYGQNG.woff2) format("woff2"),url(./roboto-mono-latin-600-USMEYMTS.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:600;src:local("Roboto Mono SemiBold italic"),local("Roboto Mono-SemiBolditalic"),url(./roboto-mono-latin-600italic-THUILB76.woff2) format("woff2"),url(./roboto-mono-latin-600italic-TLIW3M4P.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:700;src:local("Roboto Mono Bold "),local("Roboto Mono-Bold"),url(./roboto-mono-latin-700-QNNSSZIK.woff2) format("woff2"),url(./roboto-mono-latin-700-PEQVO3WV.woff) format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:700;src:local("Roboto Mono Bold italic"),local("Roboto Mono-Bolditalic"),url(./roboto-mono-latin-700italic-WWLRMKIN.woff2) format("woff2"),url(./roboto-mono-latin-700italic-KTWD2UYU.woff) format("woff")} +@font-face{font-family:Material Design Icons;src:url("./materialdesignicons-webfont-A5TO3SM5.eot?v=7.4.47");src:url("./materialdesignicons-webfont-A5TO3SM5.eot?#iefix&v=7.4.47") format("embedded-opentype"),url("./materialdesignicons-webfont-7OXPFKOV.woff2?v=7.4.47") format("woff2"),url("./materialdesignicons-webfont-4ZM36VVG.woff?v=7.4.47") format("woff"),url("./materialdesignicons-webfont-OFFE53R5.ttf?v=7.4.47") format("truetype");font-weight:400;font-style:normal}.mdi:before,.mdi-set{display:inline-block;font: 24px/1 Material Design Icons;font-size:inherit;text-rendering:auto;line-height:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.mdi-ab-testing:before{content:"\f01c9"}.mdi-abacus:before{content:"\f16e0"}.mdi-abjad-arabic:before{content:"\f1328"}.mdi-abjad-hebrew:before{content:"\f1329"}.mdi-abugida-devanagari:before{content:"\f132a"}.mdi-abugida-thai:before{content:"\f132b"}.mdi-access-point:before{content:"\f0003"}.mdi-access-point-check:before{content:"\f1538"}.mdi-access-point-minus:before{content:"\f1539"}.mdi-access-point-network:before{content:"\f0002"}.mdi-access-point-network-off:before{content:"\f0be1"}.mdi-access-point-off:before{content:"\f1511"}.mdi-access-point-plus:before{content:"\f153a"}.mdi-access-point-remove:before{content:"\f153b"}.mdi-account:before{content:"\f0004"}.mdi-account-alert:before{content:"\f0005"}.mdi-account-alert-outline:before{content:"\f0b50"}.mdi-account-arrow-down:before{content:"\f1868"}.mdi-account-arrow-down-outline:before{content:"\f1869"}.mdi-account-arrow-left:before{content:"\f0b51"}.mdi-account-arrow-left-outline:before{content:"\f0b52"}.mdi-account-arrow-right:before{content:"\f0b53"}.mdi-account-arrow-right-outline:before{content:"\f0b54"}.mdi-account-arrow-up:before{content:"\f1867"}.mdi-account-arrow-up-outline:before{content:"\f186a"}.mdi-account-badge:before{content:"\f1b0a"}.mdi-account-badge-outline:before{content:"\f1b0b"}.mdi-account-box:before{content:"\f0006"}.mdi-account-box-edit-outline:before{content:"\f1cc8"}.mdi-account-box-minus-outline:before{content:"\f1cc9"}.mdi-account-box-multiple:before{content:"\f0934"}.mdi-account-box-multiple-outline:before{content:"\f100a"}.mdi-account-box-outline:before{content:"\f0007"}.mdi-account-box-plus-outline:before{content:"\f1cca"}.mdi-account-cancel:before{content:"\f12df"}.mdi-account-cancel-outline:before{content:"\f12e0"}.mdi-account-card:before{content:"\f1ba4"}.mdi-account-card-outline:before{content:"\f1ba5"}.mdi-account-cash:before{content:"\f1097"}.mdi-account-cash-outline:before{content:"\f1098"}.mdi-account-check:before{content:"\f0008"}.mdi-account-check-outline:before{content:"\f0be2"}.mdi-account-child:before{content:"\f0a89"}.mdi-account-child-circle:before{content:"\f0a8a"}.mdi-account-child-outline:before{content:"\f10c8"}.mdi-account-circle:before{content:"\f0009"}.mdi-account-circle-outline:before{content:"\f0b55"}.mdi-account-clock:before{content:"\f0b56"}.mdi-account-clock-outline:before{content:"\f0b57"}.mdi-account-cog:before{content:"\f1370"}.mdi-account-cog-outline:before{content:"\f1371"}.mdi-account-convert:before{content:"\f000a"}.mdi-account-convert-outline:before{content:"\f1301"}.mdi-account-cowboy-hat:before{content:"\f0e9b"}.mdi-account-cowboy-hat-outline:before{content:"\f17f3"}.mdi-account-credit-card:before{content:"\f1ba6"}.mdi-account-credit-card-outline:before{content:"\f1ba7"}.mdi-account-details:before{content:"\f0631"}.mdi-account-details-outline:before{content:"\f1372"}.mdi-account-edit:before{content:"\f06bc"}.mdi-account-edit-outline:before{content:"\f0ffb"}.mdi-account-eye:before{content:"\f0420"}.mdi-account-eye-outline:before{content:"\f127b"}.mdi-account-file:before{content:"\f1ca7"}.mdi-account-file-outline:before{content:"\f1ca8"}.mdi-account-file-text:before{content:"\f1ca9"}.mdi-account-file-text-outline:before{content:"\f1caa"}.mdi-account-filter:before{content:"\f0936"}.mdi-account-filter-outline:before{content:"\f0f9d"}.mdi-account-group:before{content:"\f0849"}.mdi-account-group-outline:before{content:"\f0b58"}.mdi-account-hard-hat:before{content:"\f05b5"}.mdi-account-hard-hat-outline:before{content:"\f1a1f"}.mdi-account-heart:before{content:"\f0899"}.mdi-account-heart-outline:before{content:"\f0be3"}.mdi-account-injury:before{content:"\f1815"}.mdi-account-injury-outline:before{content:"\f1816"}.mdi-account-key:before{content:"\f000b"}.mdi-account-key-outline:before{content:"\f0be4"}.mdi-account-lock:before{content:"\f115e"}.mdi-account-lock-open:before{content:"\f1960"}.mdi-account-lock-open-outline:before{content:"\f1961"}.mdi-account-lock-outline:before{content:"\f115f"}.mdi-account-minus:before{content:"\f000d"}.mdi-account-minus-outline:before{content:"\f0aec"}.mdi-account-multiple:before{content:"\f000e"}.mdi-account-multiple-check:before{content:"\f08c5"}.mdi-account-multiple-check-outline:before{content:"\f11fe"}.mdi-account-multiple-minus:before{content:"\f05d3"}.mdi-account-multiple-minus-outline:before{content:"\f0be5"}.mdi-account-multiple-outline:before{content:"\f000f"}.mdi-account-multiple-plus:before{content:"\f0010"}.mdi-account-multiple-plus-outline:before{content:"\f0800"}.mdi-account-multiple-remove:before{content:"\f120a"}.mdi-account-multiple-remove-outline:before{content:"\f120b"}.mdi-account-music:before{content:"\f0803"}.mdi-account-music-outline:before{content:"\f0ce9"}.mdi-account-network:before{content:"\f0011"}.mdi-account-network-off:before{content:"\f1af1"}.mdi-account-network-off-outline:before{content:"\f1af2"}.mdi-account-network-outline:before{content:"\f0be6"}.mdi-account-off:before{content:"\f0012"}.mdi-account-off-outline:before{content:"\f0be7"}.mdi-account-outline:before{content:"\f0013"}.mdi-account-plus:before{content:"\f0014"}.mdi-account-plus-outline:before{content:"\f0801"}.mdi-account-question:before{content:"\f0b59"}.mdi-account-question-outline:before{content:"\f0b5a"}.mdi-account-reactivate:before{content:"\f152b"}.mdi-account-reactivate-outline:before{content:"\f152c"}.mdi-account-remove:before{content:"\f0015"}.mdi-account-remove-outline:before{content:"\f0aed"}.mdi-account-school:before{content:"\f1a20"}.mdi-account-school-outline:before{content:"\f1a21"}.mdi-account-search:before{content:"\f0016"}.mdi-account-search-outline:before{content:"\f0935"}.mdi-account-settings:before{content:"\f0630"}.mdi-account-settings-outline:before{content:"\f10c9"}.mdi-account-star:before{content:"\f0017"}.mdi-account-star-outline:before{content:"\f0be8"}.mdi-account-supervisor:before{content:"\f0a8b"}.mdi-account-supervisor-circle:before{content:"\f0a8c"}.mdi-account-supervisor-circle-outline:before{content:"\f14ec"}.mdi-account-supervisor-outline:before{content:"\f112d"}.mdi-account-switch:before{content:"\f0019"}.mdi-account-switch-outline:before{content:"\f04cb"}.mdi-account-sync:before{content:"\f191b"}.mdi-account-sync-outline:before{content:"\f191c"}.mdi-account-tag:before{content:"\f1c1b"}.mdi-account-tag-outline:before{content:"\f1c1c"}.mdi-account-tie:before{content:"\f0ce3"}.mdi-account-tie-hat:before{content:"\f1898"}.mdi-account-tie-hat-outline:before{content:"\f1899"}.mdi-account-tie-outline:before{content:"\f10ca"}.mdi-account-tie-voice:before{content:"\f1308"}.mdi-account-tie-voice-off:before{content:"\f130a"}.mdi-account-tie-voice-off-outline:before{content:"\f130b"}.mdi-account-tie-voice-outline:before{content:"\f1309"}.mdi-account-tie-woman:before{content:"\f1a8c"}.mdi-account-voice:before{content:"\f05cb"}.mdi-account-voice-off:before{content:"\f0ed4"}.mdi-account-wrench:before{content:"\f189a"}.mdi-account-wrench-outline:before{content:"\f189b"}.mdi-adjust:before{content:"\f001a"}.mdi-advertisements:before{content:"\f192a"}.mdi-advertisements-off:before{content:"\f192b"}.mdi-air-conditioner:before{content:"\f001b"}.mdi-air-filter:before{content:"\f0d43"}.mdi-air-horn:before{content:"\f0dac"}.mdi-air-humidifier:before{content:"\f1099"}.mdi-air-humidifier-off:before{content:"\f1466"}.mdi-air-purifier:before{content:"\f0d44"}.mdi-air-purifier-off:before{content:"\f1b57"}.mdi-airbag:before{content:"\f0be9"}.mdi-airballoon:before{content:"\f001c"}.mdi-airballoon-outline:before{content:"\f100b"}.mdi-airplane:before{content:"\f001d"}.mdi-airplane-alert:before{content:"\f187a"}.mdi-airplane-check:before{content:"\f187b"}.mdi-airplane-clock:before{content:"\f187c"}.mdi-airplane-cog:before{content:"\f187d"}.mdi-airplane-edit:before{content:"\f187e"}.mdi-airplane-landing:before{content:"\f05d4"}.mdi-airplane-marker:before{content:"\f187f"}.mdi-airplane-minus:before{content:"\f1880"}.mdi-airplane-off:before{content:"\f001e"}.mdi-airplane-plus:before{content:"\f1881"}.mdi-airplane-remove:before{content:"\f1882"}.mdi-airplane-search:before{content:"\f1883"}.mdi-airplane-settings:before{content:"\f1884"}.mdi-airplane-takeoff:before{content:"\f05d5"}.mdi-airport:before{content:"\f084b"}.mdi-alarm:before{content:"\f0020"}.mdi-alarm-bell:before{content:"\f078e"}.mdi-alarm-check:before{content:"\f0021"}.mdi-alarm-light:before{content:"\f078f"}.mdi-alarm-light-off:before{content:"\f171e"}.mdi-alarm-light-off-outline:before{content:"\f171f"}.mdi-alarm-light-outline:before{content:"\f0bea"}.mdi-alarm-multiple:before{content:"\f0022"}.mdi-alarm-note:before{content:"\f0e71"}.mdi-alarm-note-off:before{content:"\f0e72"}.mdi-alarm-off:before{content:"\f0023"}.mdi-alarm-panel:before{content:"\f15c4"}.mdi-alarm-panel-outline:before{content:"\f15c5"}.mdi-alarm-plus:before{content:"\f0024"}.mdi-alarm-snooze:before{content:"\f068e"}.mdi-album:before{content:"\f0025"}.mdi-alert:before{content:"\f0026"}.mdi-alert-box:before{content:"\f0027"}.mdi-alert-box-outline:before{content:"\f0ce4"}.mdi-alert-circle:before{content:"\f0028"}.mdi-alert-circle-check:before{content:"\f11ed"}.mdi-alert-circle-check-outline:before{content:"\f11ee"}.mdi-alert-circle-outline:before{content:"\f05d6"}.mdi-alert-decagram:before{content:"\f06bd"}.mdi-alert-decagram-outline:before{content:"\f0ce5"}.mdi-alert-minus:before{content:"\f14bb"}.mdi-alert-minus-outline:before{content:"\f14be"}.mdi-alert-octagon:before{content:"\f0029"}.mdi-alert-octagon-outline:before{content:"\f0ce6"}.mdi-alert-octagram:before{content:"\f0767"}.mdi-alert-octagram-outline:before{content:"\f0ce7"}.mdi-alert-outline:before{content:"\f002a"}.mdi-alert-plus:before{content:"\f14ba"}.mdi-alert-plus-outline:before{content:"\f14bd"}.mdi-alert-remove:before{content:"\f14bc"}.mdi-alert-remove-outline:before{content:"\f14bf"}.mdi-alert-rhombus:before{content:"\f11ce"}.mdi-alert-rhombus-outline:before{content:"\f11cf"}.mdi-alien:before{content:"\f089a"}.mdi-alien-outline:before{content:"\f10cb"}.mdi-align-horizontal-center:before{content:"\f11c3"}.mdi-align-horizontal-distribute:before{content:"\f1962"}.mdi-align-horizontal-left:before{content:"\f11c2"}.mdi-align-horizontal-right:before{content:"\f11c4"}.mdi-align-vertical-bottom:before{content:"\f11c5"}.mdi-align-vertical-center:before{content:"\f11c6"}.mdi-align-vertical-distribute:before{content:"\f1963"}.mdi-align-vertical-top:before{content:"\f11c7"}.mdi-all-inclusive:before{content:"\f06be"}.mdi-all-inclusive-box:before{content:"\f188d"}.mdi-all-inclusive-box-outline:before{content:"\f188e"}.mdi-allergy:before{content:"\f1258"}.mdi-alpha:before{content:"\f002b"}.mdi-alpha-a:before{content:"\f0aee"}.mdi-alpha-a-box:before{content:"\f0b08"}.mdi-alpha-a-box-outline:before{content:"\f0beb"}.mdi-alpha-a-circle:before{content:"\f0bec"}.mdi-alpha-a-circle-outline:before{content:"\f0bed"}.mdi-alpha-b:before{content:"\f0aef"}.mdi-alpha-b-box:before{content:"\f0b09"}.mdi-alpha-b-box-outline:before{content:"\f0bee"}.mdi-alpha-b-circle:before{content:"\f0bef"}.mdi-alpha-b-circle-outline:before{content:"\f0bf0"}.mdi-alpha-c:before{content:"\f0af0"}.mdi-alpha-c-box:before{content:"\f0b0a"}.mdi-alpha-c-box-outline:before{content:"\f0bf1"}.mdi-alpha-c-circle:before{content:"\f0bf2"}.mdi-alpha-c-circle-outline:before{content:"\f0bf3"}.mdi-alpha-d:before{content:"\f0af1"}.mdi-alpha-d-box:before{content:"\f0b0b"}.mdi-alpha-d-box-outline:before{content:"\f0bf4"}.mdi-alpha-d-circle:before{content:"\f0bf5"}.mdi-alpha-d-circle-outline:before{content:"\f0bf6"}.mdi-alpha-e:before{content:"\f0af2"}.mdi-alpha-e-box:before{content:"\f0b0c"}.mdi-alpha-e-box-outline:before{content:"\f0bf7"}.mdi-alpha-e-circle:before{content:"\f0bf8"}.mdi-alpha-e-circle-outline:before{content:"\f0bf9"}.mdi-alpha-f:before{content:"\f0af3"}.mdi-alpha-f-box:before{content:"\f0b0d"}.mdi-alpha-f-box-outline:before{content:"\f0bfa"}.mdi-alpha-f-circle:before{content:"\f0bfb"}.mdi-alpha-f-circle-outline:before{content:"\f0bfc"}.mdi-alpha-g:before{content:"\f0af4"}.mdi-alpha-g-box:before{content:"\f0b0e"}.mdi-alpha-g-box-outline:before{content:"\f0bfd"}.mdi-alpha-g-circle:before{content:"\f0bfe"}.mdi-alpha-g-circle-outline:before{content:"\f0bff"}.mdi-alpha-h:before{content:"\f0af5"}.mdi-alpha-h-box:before{content:"\f0b0f"}.mdi-alpha-h-box-outline:before{content:"\f0c00"}.mdi-alpha-h-circle:before{content:"\f0c01"}.mdi-alpha-h-circle-outline:before{content:"\f0c02"}.mdi-alpha-i:before{content:"\f0af6"}.mdi-alpha-i-box:before{content:"\f0b10"}.mdi-alpha-i-box-outline:before{content:"\f0c03"}.mdi-alpha-i-circle:before{content:"\f0c04"}.mdi-alpha-i-circle-outline:before{content:"\f0c05"}.mdi-alpha-j:before{content:"\f0af7"}.mdi-alpha-j-box:before{content:"\f0b11"}.mdi-alpha-j-box-outline:before{content:"\f0c06"}.mdi-alpha-j-circle:before{content:"\f0c07"}.mdi-alpha-j-circle-outline:before{content:"\f0c08"}.mdi-alpha-k:before{content:"\f0af8"}.mdi-alpha-k-box:before{content:"\f0b12"}.mdi-alpha-k-box-outline:before{content:"\f0c09"}.mdi-alpha-k-circle:before{content:"\f0c0a"}.mdi-alpha-k-circle-outline:before{content:"\f0c0b"}.mdi-alpha-l:before{content:"\f0af9"}.mdi-alpha-l-box:before{content:"\f0b13"}.mdi-alpha-l-box-outline:before{content:"\f0c0c"}.mdi-alpha-l-circle:before{content:"\f0c0d"}.mdi-alpha-l-circle-outline:before{content:"\f0c0e"}.mdi-alpha-m:before{content:"\f0afa"}.mdi-alpha-m-box:before{content:"\f0b14"}.mdi-alpha-m-box-outline:before{content:"\f0c0f"}.mdi-alpha-m-circle:before{content:"\f0c10"}.mdi-alpha-m-circle-outline:before{content:"\f0c11"}.mdi-alpha-n:before{content:"\f0afb"}.mdi-alpha-n-box:before{content:"\f0b15"}.mdi-alpha-n-box-outline:before{content:"\f0c12"}.mdi-alpha-n-circle:before{content:"\f0c13"}.mdi-alpha-n-circle-outline:before{content:"\f0c14"}.mdi-alpha-o:before{content:"\f0afc"}.mdi-alpha-o-box:before{content:"\f0b16"}.mdi-alpha-o-box-outline:before{content:"\f0c15"}.mdi-alpha-o-circle:before{content:"\f0c16"}.mdi-alpha-o-circle-outline:before{content:"\f0c17"}.mdi-alpha-p:before{content:"\f0afd"}.mdi-alpha-p-box:before{content:"\f0b17"}.mdi-alpha-p-box-outline:before{content:"\f0c18"}.mdi-alpha-p-circle:before{content:"\f0c19"}.mdi-alpha-p-circle-outline:before{content:"\f0c1a"}.mdi-alpha-q:before{content:"\f0afe"}.mdi-alpha-q-box:before{content:"\f0b18"}.mdi-alpha-q-box-outline:before{content:"\f0c1b"}.mdi-alpha-q-circle:before{content:"\f0c1c"}.mdi-alpha-q-circle-outline:before{content:"\f0c1d"}.mdi-alpha-r:before{content:"\f0aff"}.mdi-alpha-r-box:before{content:"\f0b19"}.mdi-alpha-r-box-outline:before{content:"\f0c1e"}.mdi-alpha-r-circle:before{content:"\f0c1f"}.mdi-alpha-r-circle-outline:before{content:"\f0c20"}.mdi-alpha-s:before{content:"\f0b00"}.mdi-alpha-s-box:before{content:"\f0b1a"}.mdi-alpha-s-box-outline:before{content:"\f0c21"}.mdi-alpha-s-circle:before{content:"\f0c22"}.mdi-alpha-s-circle-outline:before{content:"\f0c23"}.mdi-alpha-t:before{content:"\f0b01"}.mdi-alpha-t-box:before{content:"\f0b1b"}.mdi-alpha-t-box-outline:before{content:"\f0c24"}.mdi-alpha-t-circle:before{content:"\f0c25"}.mdi-alpha-t-circle-outline:before{content:"\f0c26"}.mdi-alpha-u:before{content:"\f0b02"}.mdi-alpha-u-box:before{content:"\f0b1c"}.mdi-alpha-u-box-outline:before{content:"\f0c27"}.mdi-alpha-u-circle:before{content:"\f0c28"}.mdi-alpha-u-circle-outline:before{content:"\f0c29"}.mdi-alpha-v:before{content:"\f0b03"}.mdi-alpha-v-box:before{content:"\f0b1d"}.mdi-alpha-v-box-outline:before{content:"\f0c2a"}.mdi-alpha-v-circle:before{content:"\f0c2b"}.mdi-alpha-v-circle-outline:before{content:"\f0c2c"}.mdi-alpha-w:before{content:"\f0b04"}.mdi-alpha-w-box:before{content:"\f0b1e"}.mdi-alpha-w-box-outline:before{content:"\f0c2d"}.mdi-alpha-w-circle:before{content:"\f0c2e"}.mdi-alpha-w-circle-outline:before{content:"\f0c2f"}.mdi-alpha-x:before{content:"\f0b05"}.mdi-alpha-x-box:before{content:"\f0b1f"}.mdi-alpha-x-box-outline:before{content:"\f0c30"}.mdi-alpha-x-circle:before{content:"\f0c31"}.mdi-alpha-x-circle-outline:before{content:"\f0c32"}.mdi-alpha-y:before{content:"\f0b06"}.mdi-alpha-y-box:before{content:"\f0b20"}.mdi-alpha-y-box-outline:before{content:"\f0c33"}.mdi-alpha-y-circle:before{content:"\f0c34"}.mdi-alpha-y-circle-outline:before{content:"\f0c35"}.mdi-alpha-z:before{content:"\f0b07"}.mdi-alpha-z-box:before{content:"\f0b21"}.mdi-alpha-z-box-outline:before{content:"\f0c36"}.mdi-alpha-z-circle:before{content:"\f0c37"}.mdi-alpha-z-circle-outline:before{content:"\f0c38"}.mdi-alphabet-aurebesh:before{content:"\f132c"}.mdi-alphabet-cyrillic:before{content:"\f132d"}.mdi-alphabet-greek:before{content:"\f132e"}.mdi-alphabet-latin:before{content:"\f132f"}.mdi-alphabet-piqad:before{content:"\f1330"}.mdi-alphabet-tengwar:before{content:"\f1337"}.mdi-alphabetical:before{content:"\f002c"}.mdi-alphabetical-off:before{content:"\f100c"}.mdi-alphabetical-variant:before{content:"\f100d"}.mdi-alphabetical-variant-off:before{content:"\f100e"}.mdi-altimeter:before{content:"\f05d7"}.mdi-ambulance:before{content:"\f002f"}.mdi-ammunition:before{content:"\f0ce8"}.mdi-ampersand:before{content:"\f0a8d"}.mdi-amplifier:before{content:"\f0030"}.mdi-amplifier-off:before{content:"\f11b5"}.mdi-anchor:before{content:"\f0031"}.mdi-android:before{content:"\f0032"}.mdi-android-studio:before{content:"\f0034"}.mdi-angle-acute:before{content:"\f0937"}.mdi-angle-obtuse:before{content:"\f0938"}.mdi-angle-right:before{content:"\f0939"}.mdi-angular:before{content:"\f06b2"}.mdi-angularjs:before{content:"\f06bf"}.mdi-animation:before{content:"\f05d8"}.mdi-animation-outline:before{content:"\f0a8f"}.mdi-animation-play:before{content:"\f093a"}.mdi-animation-play-outline:before{content:"\f0a90"}.mdi-ansible:before{content:"\f109a"}.mdi-antenna:before{content:"\f1119"}.mdi-anvil:before{content:"\f089b"}.mdi-apache-kafka:before{content:"\f100f"}.mdi-api:before{content:"\f109b"}.mdi-api-off:before{content:"\f1257"}.mdi-apple:before{content:"\f0035"}.mdi-apple-finder:before{content:"\f0036"}.mdi-apple-icloud:before{content:"\f0038"}.mdi-apple-ios:before{content:"\f0037"}.mdi-apple-keyboard-caps:before{content:"\f0632"}.mdi-apple-keyboard-command:before{content:"\f0633"}.mdi-apple-keyboard-control:before{content:"\f0634"}.mdi-apple-keyboard-option:before{content:"\f0635"}.mdi-apple-keyboard-shift:before{content:"\f0636"}.mdi-apple-safari:before{content:"\f0039"}.mdi-application:before{content:"\f08c6"}.mdi-application-array:before{content:"\f10f5"}.mdi-application-array-outline:before{content:"\f10f6"}.mdi-application-braces:before{content:"\f10f7"}.mdi-application-braces-outline:before{content:"\f10f8"}.mdi-application-brackets:before{content:"\f0c8b"}.mdi-application-brackets-outline:before{content:"\f0c8c"}.mdi-application-cog:before{content:"\f0675"}.mdi-application-cog-outline:before{content:"\f1577"}.mdi-application-edit:before{content:"\f00ae"}.mdi-application-edit-outline:before{content:"\f0619"}.mdi-application-export:before{content:"\f0dad"}.mdi-application-import:before{content:"\f0dae"}.mdi-application-outline:before{content:"\f0614"}.mdi-application-parentheses:before{content:"\f10f9"}.mdi-application-parentheses-outline:before{content:"\f10fa"}.mdi-application-settings:before{content:"\f0b60"}.mdi-application-settings-outline:before{content:"\f1555"}.mdi-application-variable:before{content:"\f10fb"}.mdi-application-variable-outline:before{content:"\f10fc"}.mdi-approximately-equal:before{content:"\f0f9e"}.mdi-approximately-equal-box:before{content:"\f0f9f"}.mdi-apps:before{content:"\f003b"}.mdi-apps-box:before{content:"\f0d46"}.mdi-arch:before{content:"\f08c7"}.mdi-archive:before{content:"\f003c"}.mdi-archive-alert:before{content:"\f14fd"}.mdi-archive-alert-outline:before{content:"\f14fe"}.mdi-archive-arrow-down:before{content:"\f1259"}.mdi-archive-arrow-down-outline:before{content:"\f125a"}.mdi-archive-arrow-up:before{content:"\f125b"}.mdi-archive-arrow-up-outline:before{content:"\f125c"}.mdi-archive-cancel:before{content:"\f174b"}.mdi-archive-cancel-outline:before{content:"\f174c"}.mdi-archive-check:before{content:"\f174d"}.mdi-archive-check-outline:before{content:"\f174e"}.mdi-archive-clock:before{content:"\f174f"}.mdi-archive-clock-outline:before{content:"\f1750"}.mdi-archive-cog:before{content:"\f1751"}.mdi-archive-cog-outline:before{content:"\f1752"}.mdi-archive-edit:before{content:"\f1753"}.mdi-archive-edit-outline:before{content:"\f1754"}.mdi-archive-eye:before{content:"\f1755"}.mdi-archive-eye-outline:before{content:"\f1756"}.mdi-archive-lock:before{content:"\f1757"}.mdi-archive-lock-open:before{content:"\f1758"}.mdi-archive-lock-open-outline:before{content:"\f1759"}.mdi-archive-lock-outline:before{content:"\f175a"}.mdi-archive-marker:before{content:"\f175b"}.mdi-archive-marker-outline:before{content:"\f175c"}.mdi-archive-minus:before{content:"\f175d"}.mdi-archive-minus-outline:before{content:"\f175e"}.mdi-archive-music:before{content:"\f175f"}.mdi-archive-music-outline:before{content:"\f1760"}.mdi-archive-off:before{content:"\f1761"}.mdi-archive-off-outline:before{content:"\f1762"}.mdi-archive-outline:before{content:"\f120e"}.mdi-archive-plus:before{content:"\f1763"}.mdi-archive-plus-outline:before{content:"\f1764"}.mdi-archive-refresh:before{content:"\f1765"}.mdi-archive-refresh-outline:before{content:"\f1766"}.mdi-archive-remove:before{content:"\f1767"}.mdi-archive-remove-outline:before{content:"\f1768"}.mdi-archive-search:before{content:"\f1769"}.mdi-archive-search-outline:before{content:"\f176a"}.mdi-archive-settings:before{content:"\f176b"}.mdi-archive-settings-outline:before{content:"\f176c"}.mdi-archive-star:before{content:"\f176d"}.mdi-archive-star-outline:before{content:"\f176e"}.mdi-archive-sync:before{content:"\f176f"}.mdi-archive-sync-outline:before{content:"\f1770"}.mdi-arm-flex:before{content:"\f0fd7"}.mdi-arm-flex-outline:before{content:"\f0fd6"}.mdi-arrange-bring-forward:before{content:"\f003d"}.mdi-arrange-bring-to-front:before{content:"\f003e"}.mdi-arrange-send-backward:before{content:"\f003f"}.mdi-arrange-send-to-back:before{content:"\f0040"}.mdi-arrow-all:before{content:"\f0041"}.mdi-arrow-bottom-left:before{content:"\f0042"}.mdi-arrow-bottom-left-bold-box:before{content:"\f1964"}.mdi-arrow-bottom-left-bold-box-outline:before{content:"\f1965"}.mdi-arrow-bottom-left-bold-outline:before{content:"\f09b7"}.mdi-arrow-bottom-left-thick:before{content:"\f09b8"}.mdi-arrow-bottom-left-thin:before{content:"\f19b6"}.mdi-arrow-bottom-left-thin-circle-outline:before{content:"\f1596"}.mdi-arrow-bottom-right:before{content:"\f0043"}.mdi-arrow-bottom-right-bold-box:before{content:"\f1966"}.mdi-arrow-bottom-right-bold-box-outline:before{content:"\f1967"}.mdi-arrow-bottom-right-bold-outline:before{content:"\f09b9"}.mdi-arrow-bottom-right-thick:before{content:"\f09ba"}.mdi-arrow-bottom-right-thin:before{content:"\f19b7"}.mdi-arrow-bottom-right-thin-circle-outline:before{content:"\f1595"}.mdi-arrow-collapse:before{content:"\f0615"}.mdi-arrow-collapse-all:before{content:"\f0044"}.mdi-arrow-collapse-down:before{content:"\f0792"}.mdi-arrow-collapse-horizontal:before{content:"\f084c"}.mdi-arrow-collapse-left:before{content:"\f0793"}.mdi-arrow-collapse-right:before{content:"\f0794"}.mdi-arrow-collapse-up:before{content:"\f0795"}.mdi-arrow-collapse-vertical:before{content:"\f084d"}.mdi-arrow-decision:before{content:"\f09bb"}.mdi-arrow-decision-auto:before{content:"\f09bc"}.mdi-arrow-decision-auto-outline:before{content:"\f09bd"}.mdi-arrow-decision-outline:before{content:"\f09be"}.mdi-arrow-down:before{content:"\f0045"}.mdi-arrow-down-bold:before{content:"\f072e"}.mdi-arrow-down-bold-box:before{content:"\f072f"}.mdi-arrow-down-bold-box-outline:before{content:"\f0730"}.mdi-arrow-down-bold-circle:before{content:"\f0047"}.mdi-arrow-down-bold-circle-outline:before{content:"\f0048"}.mdi-arrow-down-bold-hexagon-outline:before{content:"\f0049"}.mdi-arrow-down-bold-outline:before{content:"\f09bf"}.mdi-arrow-down-box:before{content:"\f06c0"}.mdi-arrow-down-circle:before{content:"\f0cdb"}.mdi-arrow-down-circle-outline:before{content:"\f0cdc"}.mdi-arrow-down-drop-circle:before{content:"\f004a"}.mdi-arrow-down-drop-circle-outline:before{content:"\f004b"}.mdi-arrow-down-left:before{content:"\f17a1"}.mdi-arrow-down-left-bold:before{content:"\f17a2"}.mdi-arrow-down-right:before{content:"\f17a3"}.mdi-arrow-down-right-bold:before{content:"\f17a4"}.mdi-arrow-down-thick:before{content:"\f0046"}.mdi-arrow-down-thin:before{content:"\f19b3"}.mdi-arrow-down-thin-circle-outline:before{content:"\f1599"}.mdi-arrow-expand:before{content:"\f0616"}.mdi-arrow-expand-all:before{content:"\f004c"}.mdi-arrow-expand-down:before{content:"\f0796"}.mdi-arrow-expand-horizontal:before{content:"\f084e"}.mdi-arrow-expand-left:before{content:"\f0797"}.mdi-arrow-expand-right:before{content:"\f0798"}.mdi-arrow-expand-up:before{content:"\f0799"}.mdi-arrow-expand-vertical:before{content:"\f084f"}.mdi-arrow-horizontal-lock:before{content:"\f115b"}.mdi-arrow-left:before{content:"\f004d"}.mdi-arrow-left-bold:before{content:"\f0731"}.mdi-arrow-left-bold-box:before{content:"\f0732"}.mdi-arrow-left-bold-box-outline:before{content:"\f0733"}.mdi-arrow-left-bold-circle:before{content:"\f004f"}.mdi-arrow-left-bold-circle-outline:before{content:"\f0050"}.mdi-arrow-left-bold-hexagon-outline:before{content:"\f0051"}.mdi-arrow-left-bold-outline:before{content:"\f09c0"}.mdi-arrow-left-bottom:before{content:"\f17a5"}.mdi-arrow-left-bottom-bold:before{content:"\f17a6"}.mdi-arrow-left-box:before{content:"\f06c1"}.mdi-arrow-left-circle:before{content:"\f0cdd"}.mdi-arrow-left-circle-outline:before{content:"\f0cde"}.mdi-arrow-left-drop-circle:before{content:"\f0052"}.mdi-arrow-left-drop-circle-outline:before{content:"\f0053"}.mdi-arrow-left-right:before{content:"\f0e73"}.mdi-arrow-left-right-bold:before{content:"\f0e74"}.mdi-arrow-left-right-bold-outline:before{content:"\f09c1"}.mdi-arrow-left-thick:before{content:"\f004e"}.mdi-arrow-left-thin:before{content:"\f19b1"}.mdi-arrow-left-thin-circle-outline:before{content:"\f159a"}.mdi-arrow-left-top:before{content:"\f17a7"}.mdi-arrow-left-top-bold:before{content:"\f17a8"}.mdi-arrow-oscillating:before{content:"\f1c91"}.mdi-arrow-oscillating-off:before{content:"\f1c92"}.mdi-arrow-projectile:before{content:"\f1840"}.mdi-arrow-projectile-multiple:before{content:"\f183f"}.mdi-arrow-right:before{content:"\f0054"}.mdi-arrow-right-bold:before{content:"\f0734"}.mdi-arrow-right-bold-box:before{content:"\f0735"}.mdi-arrow-right-bold-box-outline:before{content:"\f0736"}.mdi-arrow-right-bold-circle:before{content:"\f0056"}.mdi-arrow-right-bold-circle-outline:before{content:"\f0057"}.mdi-arrow-right-bold-hexagon-outline:before{content:"\f0058"}.mdi-arrow-right-bold-outline:before{content:"\f09c2"}.mdi-arrow-right-bottom:before{content:"\f17a9"}.mdi-arrow-right-bottom-bold:before{content:"\f17aa"}.mdi-arrow-right-box:before{content:"\f06c2"}.mdi-arrow-right-circle:before{content:"\f0cdf"}.mdi-arrow-right-circle-outline:before{content:"\f0ce0"}.mdi-arrow-right-drop-circle:before{content:"\f0059"}.mdi-arrow-right-drop-circle-outline:before{content:"\f005a"}.mdi-arrow-right-thick:before{content:"\f0055"}.mdi-arrow-right-thin:before{content:"\f19b0"}.mdi-arrow-right-thin-circle-outline:before{content:"\f1598"}.mdi-arrow-right-top:before{content:"\f17ab"}.mdi-arrow-right-top-bold:before{content:"\f17ac"}.mdi-arrow-split-horizontal:before{content:"\f093b"}.mdi-arrow-split-vertical:before{content:"\f093c"}.mdi-arrow-top-left:before{content:"\f005b"}.mdi-arrow-top-left-bold-box:before{content:"\f1968"}.mdi-arrow-top-left-bold-box-outline:before{content:"\f1969"}.mdi-arrow-top-left-bold-outline:before{content:"\f09c3"}.mdi-arrow-top-left-bottom-right:before{content:"\f0e75"}.mdi-arrow-top-left-bottom-right-bold:before{content:"\f0e76"}.mdi-arrow-top-left-thick:before{content:"\f09c4"}.mdi-arrow-top-left-thin:before{content:"\f19b5"}.mdi-arrow-top-left-thin-circle-outline:before{content:"\f1593"}.mdi-arrow-top-right:before{content:"\f005c"}.mdi-arrow-top-right-bold-box:before{content:"\f196a"}.mdi-arrow-top-right-bold-box-outline:before{content:"\f196b"}.mdi-arrow-top-right-bold-outline:before{content:"\f09c5"}.mdi-arrow-top-right-bottom-left:before{content:"\f0e77"}.mdi-arrow-top-right-bottom-left-bold:before{content:"\f0e78"}.mdi-arrow-top-right-thick:before{content:"\f09c6"}.mdi-arrow-top-right-thin:before{content:"\f19b4"}.mdi-arrow-top-right-thin-circle-outline:before{content:"\f1594"}.mdi-arrow-u-down-left:before{content:"\f17ad"}.mdi-arrow-u-down-left-bold:before{content:"\f17ae"}.mdi-arrow-u-down-right:before{content:"\f17af"}.mdi-arrow-u-down-right-bold:before{content:"\f17b0"}.mdi-arrow-u-left-bottom:before{content:"\f17b1"}.mdi-arrow-u-left-bottom-bold:before{content:"\f17b2"}.mdi-arrow-u-left-top:before{content:"\f17b3"}.mdi-arrow-u-left-top-bold:before{content:"\f17b4"}.mdi-arrow-u-right-bottom:before{content:"\f17b5"}.mdi-arrow-u-right-bottom-bold:before{content:"\f17b6"}.mdi-arrow-u-right-top:before{content:"\f17b7"}.mdi-arrow-u-right-top-bold:before{content:"\f17b8"}.mdi-arrow-u-up-left:before{content:"\f17b9"}.mdi-arrow-u-up-left-bold:before{content:"\f17ba"}.mdi-arrow-u-up-right:before{content:"\f17bb"}.mdi-arrow-u-up-right-bold:before{content:"\f17bc"}.mdi-arrow-up:before{content:"\f005d"}.mdi-arrow-up-bold:before{content:"\f0737"}.mdi-arrow-up-bold-box:before{content:"\f0738"}.mdi-arrow-up-bold-box-outline:before{content:"\f0739"}.mdi-arrow-up-bold-circle:before{content:"\f005f"}.mdi-arrow-up-bold-circle-outline:before{content:"\f0060"}.mdi-arrow-up-bold-hexagon-outline:before{content:"\f0061"}.mdi-arrow-up-bold-outline:before{content:"\f09c7"}.mdi-arrow-up-box:before{content:"\f06c3"}.mdi-arrow-up-circle:before{content:"\f0ce1"}.mdi-arrow-up-circle-outline:before{content:"\f0ce2"}.mdi-arrow-up-down:before{content:"\f0e79"}.mdi-arrow-up-down-bold:before{content:"\f0e7a"}.mdi-arrow-up-down-bold-outline:before{content:"\f09c8"}.mdi-arrow-up-drop-circle:before{content:"\f0062"}.mdi-arrow-up-drop-circle-outline:before{content:"\f0063"}.mdi-arrow-up-left:before{content:"\f17bd"}.mdi-arrow-up-left-bold:before{content:"\f17be"}.mdi-arrow-up-right:before{content:"\f17bf"}.mdi-arrow-up-right-bold:before{content:"\f17c0"}.mdi-arrow-up-thick:before{content:"\f005e"}.mdi-arrow-up-thin:before{content:"\f19b2"}.mdi-arrow-up-thin-circle-outline:before{content:"\f1597"}.mdi-arrow-vertical-lock:before{content:"\f115c"}.mdi-artboard:before{content:"\f1b9a"}.mdi-artstation:before{content:"\f0b5b"}.mdi-aspect-ratio:before{content:"\f0a24"}.mdi-assistant:before{content:"\f0064"}.mdi-asterisk:before{content:"\f06c4"}.mdi-asterisk-circle-outline:before{content:"\f1a27"}.mdi-at:before{content:"\f0065"}.mdi-atlassian:before{content:"\f0804"}.mdi-atm:before{content:"\f0d47"}.mdi-atom:before{content:"\f0768"}.mdi-atom-variant:before{content:"\f0e7b"}.mdi-attachment:before{content:"\f0066"}.mdi-attachment-check:before{content:"\f1ac1"}.mdi-attachment-lock:before{content:"\f19c4"}.mdi-attachment-minus:before{content:"\f1ac2"}.mdi-attachment-off:before{content:"\f1ac3"}.mdi-attachment-plus:before{content:"\f1ac4"}.mdi-attachment-remove:before{content:"\f1ac5"}.mdi-atv:before{content:"\f1b70"}.mdi-audio-input-rca:before{content:"\f186b"}.mdi-audio-input-stereo-minijack:before{content:"\f186c"}.mdi-audio-input-xlr:before{content:"\f186d"}.mdi-audio-video:before{content:"\f093d"}.mdi-audio-video-off:before{content:"\f11b6"}.mdi-augmented-reality:before{content:"\f0850"}.mdi-aurora:before{content:"\f1bb9"}.mdi-auto-download:before{content:"\f137e"}.mdi-auto-fix:before{content:"\f0068"}.mdi-auto-mode:before{content:"\f1c20"}.mdi-auto-upload:before{content:"\f0069"}.mdi-autorenew:before{content:"\f006a"}.mdi-autorenew-off:before{content:"\f19e7"}.mdi-av-timer:before{content:"\f006b"}.mdi-awning:before{content:"\f1b87"}.mdi-awning-outline:before{content:"\f1b88"}.mdi-aws:before{content:"\f0e0f"}.mdi-axe:before{content:"\f08c8"}.mdi-axe-battle:before{content:"\f1842"}.mdi-axis:before{content:"\f0d48"}.mdi-axis-arrow:before{content:"\f0d49"}.mdi-axis-arrow-info:before{content:"\f140e"}.mdi-axis-arrow-lock:before{content:"\f0d4a"}.mdi-axis-lock:before{content:"\f0d4b"}.mdi-axis-x-arrow:before{content:"\f0d4c"}.mdi-axis-x-arrow-lock:before{content:"\f0d4d"}.mdi-axis-x-rotate-clockwise:before{content:"\f0d4e"}.mdi-axis-x-rotate-counterclockwise:before{content:"\f0d4f"}.mdi-axis-x-y-arrow-lock:before{content:"\f0d50"}.mdi-axis-y-arrow:before{content:"\f0d51"}.mdi-axis-y-arrow-lock:before{content:"\f0d52"}.mdi-axis-y-rotate-clockwise:before{content:"\f0d53"}.mdi-axis-y-rotate-counterclockwise:before{content:"\f0d54"}.mdi-axis-z-arrow:before{content:"\f0d55"}.mdi-axis-z-arrow-lock:before{content:"\f0d56"}.mdi-axis-z-rotate-clockwise:before{content:"\f0d57"}.mdi-axis-z-rotate-counterclockwise:before{content:"\f0d58"}.mdi-babel:before{content:"\f0a25"}.mdi-baby:before{content:"\f006c"}.mdi-baby-bottle:before{content:"\f0f39"}.mdi-baby-bottle-outline:before{content:"\f0f3a"}.mdi-baby-buggy:before{content:"\f13e0"}.mdi-baby-buggy-off:before{content:"\f1af3"}.mdi-baby-carriage:before{content:"\f068f"}.mdi-baby-carriage-off:before{content:"\f0fa0"}.mdi-baby-face:before{content:"\f0e7c"}.mdi-baby-face-outline:before{content:"\f0e7d"}.mdi-backburger:before{content:"\f006d"}.mdi-backspace:before{content:"\f006e"}.mdi-backspace-outline:before{content:"\f0b5c"}.mdi-backspace-reverse:before{content:"\f0e7e"}.mdi-backspace-reverse-outline:before{content:"\f0e7f"}.mdi-backup-restore:before{content:"\f006f"}.mdi-bacteria:before{content:"\f0ed5"}.mdi-bacteria-outline:before{content:"\f0ed6"}.mdi-badge-account:before{content:"\f0da7"}.mdi-badge-account-alert:before{content:"\f0da8"}.mdi-badge-account-alert-outline:before{content:"\f0da9"}.mdi-badge-account-horizontal:before{content:"\f0e0d"}.mdi-badge-account-horizontal-outline:before{content:"\f0e0e"}.mdi-badge-account-outline:before{content:"\f0daa"}.mdi-badminton:before{content:"\f0851"}.mdi-bag-carry-on:before{content:"\f0f3b"}.mdi-bag-carry-on-check:before{content:"\f0d65"}.mdi-bag-carry-on-off:before{content:"\f0f3c"}.mdi-bag-checked:before{content:"\f0f3d"}.mdi-bag-personal:before{content:"\f0e10"}.mdi-bag-personal-off:before{content:"\f0e11"}.mdi-bag-personal-off-outline:before{content:"\f0e12"}.mdi-bag-personal-outline:before{content:"\f0e13"}.mdi-bag-personal-plus:before{content:"\f1ca4"}.mdi-bag-personal-plus-outline:before{content:"\f1ca5"}.mdi-bag-personal-tag:before{content:"\f1b0c"}.mdi-bag-personal-tag-outline:before{content:"\f1b0d"}.mdi-bag-suitcase:before{content:"\f158b"}.mdi-bag-suitcase-off:before{content:"\f158d"}.mdi-bag-suitcase-off-outline:before{content:"\f158e"}.mdi-bag-suitcase-outline:before{content:"\f158c"}.mdi-baguette:before{content:"\f0f3e"}.mdi-balcony:before{content:"\f1817"}.mdi-balloon:before{content:"\f0a26"}.mdi-ballot:before{content:"\f09c9"}.mdi-ballot-outline:before{content:"\f09ca"}.mdi-ballot-recount:before{content:"\f0c39"}.mdi-ballot-recount-outline:before{content:"\f0c3a"}.mdi-bandage:before{content:"\f0daf"}.mdi-bank:before{content:"\f0070"}.mdi-bank-check:before{content:"\f1655"}.mdi-bank-circle:before{content:"\f1c03"}.mdi-bank-circle-outline:before{content:"\f1c04"}.mdi-bank-minus:before{content:"\f0db0"}.mdi-bank-off:before{content:"\f1656"}.mdi-bank-off-outline:before{content:"\f1657"}.mdi-bank-outline:before{content:"\f0e80"}.mdi-bank-plus:before{content:"\f0db1"}.mdi-bank-remove:before{content:"\f0db2"}.mdi-bank-transfer:before{content:"\f0a27"}.mdi-bank-transfer-in:before{content:"\f0a28"}.mdi-bank-transfer-out:before{content:"\f0a29"}.mdi-barcode:before{content:"\f0071"}.mdi-barcode-off:before{content:"\f1236"}.mdi-barcode-scan:before{content:"\f0072"}.mdi-barley:before{content:"\f0073"}.mdi-barley-off:before{content:"\f0b5d"}.mdi-barn:before{content:"\f0b5e"}.mdi-barrel:before{content:"\f0074"}.mdi-barrel-outline:before{content:"\f1a28"}.mdi-baseball:before{content:"\f0852"}.mdi-baseball-bat:before{content:"\f0853"}.mdi-baseball-diamond:before{content:"\f15ec"}.mdi-baseball-diamond-outline:before{content:"\f15ed"}.mdi-baseball-outline:before{content:"\f1c5a"}.mdi-bash:before{content:"\f1183"}.mdi-basket:before{content:"\f0076"}.mdi-basket-check:before{content:"\f18e5"}.mdi-basket-check-outline:before{content:"\f18e6"}.mdi-basket-fill:before{content:"\f0077"}.mdi-basket-minus:before{content:"\f1523"}.mdi-basket-minus-outline:before{content:"\f1524"}.mdi-basket-off:before{content:"\f1525"}.mdi-basket-off-outline:before{content:"\f1526"}.mdi-basket-outline:before{content:"\f1181"}.mdi-basket-plus:before{content:"\f1527"}.mdi-basket-plus-outline:before{content:"\f1528"}.mdi-basket-remove:before{content:"\f1529"}.mdi-basket-remove-outline:before{content:"\f152a"}.mdi-basket-unfill:before{content:"\f0078"}.mdi-basketball:before{content:"\f0806"}.mdi-basketball-hoop:before{content:"\f0c3b"}.mdi-basketball-hoop-outline:before{content:"\f0c3c"}.mdi-bat:before{content:"\f0b5f"}.mdi-bathtub:before{content:"\f1818"}.mdi-bathtub-outline:before{content:"\f1819"}.mdi-battery:before{content:"\f0079"}.mdi-battery-10:before{content:"\f007a"}.mdi-battery-10-bluetooth:before{content:"\f093e"}.mdi-battery-20:before{content:"\f007b"}.mdi-battery-20-bluetooth:before{content:"\f093f"}.mdi-battery-30:before{content:"\f007c"}.mdi-battery-30-bluetooth:before{content:"\f0940"}.mdi-battery-40:before{content:"\f007d"}.mdi-battery-40-bluetooth:before{content:"\f0941"}.mdi-battery-50:before{content:"\f007e"}.mdi-battery-50-bluetooth:before{content:"\f0942"}.mdi-battery-60:before{content:"\f007f"}.mdi-battery-60-bluetooth:before{content:"\f0943"}.mdi-battery-70:before{content:"\f0080"}.mdi-battery-70-bluetooth:before{content:"\f0944"}.mdi-battery-80:before{content:"\f0081"}.mdi-battery-80-bluetooth:before{content:"\f0945"}.mdi-battery-90:before{content:"\f0082"}.mdi-battery-90-bluetooth:before{content:"\f0946"}.mdi-battery-alert:before{content:"\f0083"}.mdi-battery-alert-bluetooth:before{content:"\f0947"}.mdi-battery-alert-variant:before{content:"\f10cc"}.mdi-battery-alert-variant-outline:before{content:"\f10cd"}.mdi-battery-arrow-down:before{content:"\f17de"}.mdi-battery-arrow-down-outline:before{content:"\f17df"}.mdi-battery-arrow-up:before{content:"\f17e0"}.mdi-battery-arrow-up-outline:before{content:"\f17e1"}.mdi-battery-bluetooth:before{content:"\f0948"}.mdi-battery-bluetooth-variant:before{content:"\f0949"}.mdi-battery-charging:before{content:"\f0084"}.mdi-battery-charging-10:before{content:"\f089c"}.mdi-battery-charging-100:before{content:"\f0085"}.mdi-battery-charging-20:before{content:"\f0086"}.mdi-battery-charging-30:before{content:"\f0087"}.mdi-battery-charging-40:before{content:"\f0088"}.mdi-battery-charging-50:before{content:"\f089d"}.mdi-battery-charging-60:before{content:"\f0089"}.mdi-battery-charging-70:before{content:"\f089e"}.mdi-battery-charging-80:before{content:"\f008a"}.mdi-battery-charging-90:before{content:"\f008b"}.mdi-battery-charging-high:before{content:"\f12a6"}.mdi-battery-charging-low:before{content:"\f12a4"}.mdi-battery-charging-medium:before{content:"\f12a5"}.mdi-battery-charging-outline:before{content:"\f089f"}.mdi-battery-charging-wireless:before{content:"\f0807"}.mdi-battery-charging-wireless-10:before{content:"\f0808"}.mdi-battery-charging-wireless-20:before{content:"\f0809"}.mdi-battery-charging-wireless-30:before{content:"\f080a"}.mdi-battery-charging-wireless-40:before{content:"\f080b"}.mdi-battery-charging-wireless-50:before{content:"\f080c"}.mdi-battery-charging-wireless-60:before{content:"\f080d"}.mdi-battery-charging-wireless-70:before{content:"\f080e"}.mdi-battery-charging-wireless-80:before{content:"\f080f"}.mdi-battery-charging-wireless-90:before{content:"\f0810"}.mdi-battery-charging-wireless-alert:before{content:"\f0811"}.mdi-battery-charging-wireless-outline:before{content:"\f0812"}.mdi-battery-check:before{content:"\f17e2"}.mdi-battery-check-outline:before{content:"\f17e3"}.mdi-battery-clock:before{content:"\f19e5"}.mdi-battery-clock-outline:before{content:"\f19e6"}.mdi-battery-heart:before{content:"\f120f"}.mdi-battery-heart-outline:before{content:"\f1210"}.mdi-battery-heart-variant:before{content:"\f1211"}.mdi-battery-high:before{content:"\f12a3"}.mdi-battery-lock:before{content:"\f179c"}.mdi-battery-lock-open:before{content:"\f179d"}.mdi-battery-low:before{content:"\f12a1"}.mdi-battery-medium:before{content:"\f12a2"}.mdi-battery-minus:before{content:"\f17e4"}.mdi-battery-minus-outline:before{content:"\f17e5"}.mdi-battery-minus-variant:before{content:"\f008c"}.mdi-battery-negative:before{content:"\f008d"}.mdi-battery-off:before{content:"\f125d"}.mdi-battery-off-outline:before{content:"\f125e"}.mdi-battery-outline:before{content:"\f008e"}.mdi-battery-plus:before{content:"\f17e6"}.mdi-battery-plus-outline:before{content:"\f17e7"}.mdi-battery-plus-variant:before{content:"\f008f"}.mdi-battery-positive:before{content:"\f0090"}.mdi-battery-remove:before{content:"\f17e8"}.mdi-battery-remove-outline:before{content:"\f17e9"}.mdi-battery-sync:before{content:"\f1834"}.mdi-battery-sync-outline:before{content:"\f1835"}.mdi-battery-unknown:before{content:"\f0091"}.mdi-battery-unknown-bluetooth:before{content:"\f094a"}.mdi-beach:before{content:"\f0092"}.mdi-beaker:before{content:"\f0cea"}.mdi-beaker-alert:before{content:"\f1229"}.mdi-beaker-alert-outline:before{content:"\f122a"}.mdi-beaker-check:before{content:"\f122b"}.mdi-beaker-check-outline:before{content:"\f122c"}.mdi-beaker-minus:before{content:"\f122d"}.mdi-beaker-minus-outline:before{content:"\f122e"}.mdi-beaker-outline:before{content:"\f0690"}.mdi-beaker-plus:before{content:"\f122f"}.mdi-beaker-plus-outline:before{content:"\f1230"}.mdi-beaker-question:before{content:"\f1231"}.mdi-beaker-question-outline:before{content:"\f1232"}.mdi-beaker-remove:before{content:"\f1233"}.mdi-beaker-remove-outline:before{content:"\f1234"}.mdi-bed:before{content:"\f02e3"}.mdi-bed-clock:before{content:"\f1b94"}.mdi-bed-double:before{content:"\f0fd4"}.mdi-bed-double-outline:before{content:"\f0fd3"}.mdi-bed-empty:before{content:"\f08a0"}.mdi-bed-king:before{content:"\f0fd2"}.mdi-bed-king-outline:before{content:"\f0fd1"}.mdi-bed-outline:before{content:"\f0099"}.mdi-bed-queen:before{content:"\f0fd0"}.mdi-bed-queen-outline:before{content:"\f0fdb"}.mdi-bed-single:before{content:"\f106d"}.mdi-bed-single-outline:before{content:"\f106e"}.mdi-bee:before{content:"\f0fa1"}.mdi-bee-flower:before{content:"\f0fa2"}.mdi-beehive-off-outline:before{content:"\f13ed"}.mdi-beehive-outline:before{content:"\f10ce"}.mdi-beekeeper:before{content:"\f14e2"}.mdi-beer:before{content:"\f0098"}.mdi-beer-outline:before{content:"\f130c"}.mdi-bell:before{content:"\f009a"}.mdi-bell-alert:before{content:"\f0d59"}.mdi-bell-alert-outline:before{content:"\f0e81"}.mdi-bell-badge:before{content:"\f116b"}.mdi-bell-badge-outline:before{content:"\f0178"}.mdi-bell-cancel:before{content:"\f13e7"}.mdi-bell-cancel-outline:before{content:"\f13e8"}.mdi-bell-check:before{content:"\f11e5"}.mdi-bell-check-outline:before{content:"\f11e6"}.mdi-bell-circle:before{content:"\f0d5a"}.mdi-bell-circle-outline:before{content:"\f0d5b"}.mdi-bell-cog:before{content:"\f1a29"}.mdi-bell-cog-outline:before{content:"\f1a2a"}.mdi-bell-minus:before{content:"\f13e9"}.mdi-bell-minus-outline:before{content:"\f13ea"}.mdi-bell-off:before{content:"\f009b"}.mdi-bell-off-outline:before{content:"\f0a91"}.mdi-bell-outline:before{content:"\f009c"}.mdi-bell-plus:before{content:"\f009d"}.mdi-bell-plus-outline:before{content:"\f0a92"}.mdi-bell-remove:before{content:"\f13eb"}.mdi-bell-remove-outline:before{content:"\f13ec"}.mdi-bell-ring:before{content:"\f009e"}.mdi-bell-ring-outline:before{content:"\f009f"}.mdi-bell-sleep:before{content:"\f00a0"}.mdi-bell-sleep-outline:before{content:"\f0a93"}.mdi-bench:before{content:"\f1c21"}.mdi-bench-back:before{content:"\f1c22"}.mdi-beta:before{content:"\f00a1"}.mdi-betamax:before{content:"\f09cb"}.mdi-biathlon:before{content:"\f0e14"}.mdi-bicycle:before{content:"\f109c"}.mdi-bicycle-basket:before{content:"\f1235"}.mdi-bicycle-cargo:before{content:"\f189c"}.mdi-bicycle-electric:before{content:"\f15b4"}.mdi-bicycle-penny-farthing:before{content:"\f15e9"}.mdi-bike:before{content:"\f00a3"}.mdi-bike-fast:before{content:"\f111f"}.mdi-bike-pedal:before{content:"\f1c23"}.mdi-bike-pedal-clipless:before{content:"\f1c24"}.mdi-bike-pedal-mountain:before{content:"\f1c25"}.mdi-billboard:before{content:"\f1010"}.mdi-billiards:before{content:"\f0b61"}.mdi-billiards-rack:before{content:"\f0b62"}.mdi-binoculars:before{content:"\f00a5"}.mdi-bio:before{content:"\f00a6"}.mdi-biohazard:before{content:"\f00a7"}.mdi-bird:before{content:"\f15c6"}.mdi-bitbucket:before{content:"\f00a8"}.mdi-bitcoin:before{content:"\f0813"}.mdi-black-mesa:before{content:"\f00a9"}.mdi-blender:before{content:"\f0ceb"}.mdi-blender-outline:before{content:"\f181a"}.mdi-blender-software:before{content:"\f00ab"}.mdi-blinds:before{content:"\f00ac"}.mdi-blinds-horizontal:before{content:"\f1a2b"}.mdi-blinds-horizontal-closed:before{content:"\f1a2c"}.mdi-blinds-open:before{content:"\f1011"}.mdi-blinds-vertical:before{content:"\f1a2d"}.mdi-blinds-vertical-closed:before{content:"\f1a2e"}.mdi-block-helper:before{content:"\f00ad"}.mdi-blood-bag:before{content:"\f0cec"}.mdi-bluetooth:before{content:"\f00af"}.mdi-bluetooth-audio:before{content:"\f00b0"}.mdi-bluetooth-connect:before{content:"\f00b1"}.mdi-bluetooth-off:before{content:"\f00b2"}.mdi-bluetooth-settings:before{content:"\f00b3"}.mdi-bluetooth-transfer:before{content:"\f00b4"}.mdi-blur:before{content:"\f00b5"}.mdi-blur-linear:before{content:"\f00b6"}.mdi-blur-off:before{content:"\f00b7"}.mdi-blur-radial:before{content:"\f00b8"}.mdi-bolt:before{content:"\f0db3"}.mdi-bomb:before{content:"\f0691"}.mdi-bomb-off:before{content:"\f06c5"}.mdi-bone:before{content:"\f00b9"}.mdi-bone-off:before{content:"\f19e0"}.mdi-book:before{content:"\f00ba"}.mdi-book-account:before{content:"\f13ad"}.mdi-book-account-outline:before{content:"\f13ae"}.mdi-book-alert:before{content:"\f167c"}.mdi-book-alert-outline:before{content:"\f167d"}.mdi-book-alphabet:before{content:"\f061d"}.mdi-book-arrow-down:before{content:"\f167e"}.mdi-book-arrow-down-outline:before{content:"\f167f"}.mdi-book-arrow-left:before{content:"\f1680"}.mdi-book-arrow-left-outline:before{content:"\f1681"}.mdi-book-arrow-right:before{content:"\f1682"}.mdi-book-arrow-right-outline:before{content:"\f1683"}.mdi-book-arrow-up:before{content:"\f1684"}.mdi-book-arrow-up-outline:before{content:"\f1685"}.mdi-book-cancel:before{content:"\f1686"}.mdi-book-cancel-outline:before{content:"\f1687"}.mdi-book-check:before{content:"\f14f3"}.mdi-book-check-outline:before{content:"\f14f4"}.mdi-book-clock:before{content:"\f1688"}.mdi-book-clock-outline:before{content:"\f1689"}.mdi-book-cog:before{content:"\f168a"}.mdi-book-cog-outline:before{content:"\f168b"}.mdi-book-cross:before{content:"\f00a2"}.mdi-book-edit:before{content:"\f168c"}.mdi-book-edit-outline:before{content:"\f168d"}.mdi-book-education:before{content:"\f16c9"}.mdi-book-education-outline:before{content:"\f16ca"}.mdi-book-heart:before{content:"\f1a1d"}.mdi-book-heart-outline:before{content:"\f1a1e"}.mdi-book-information-variant:before{content:"\f106f"}.mdi-book-lock:before{content:"\f079a"}.mdi-book-lock-open:before{content:"\f079b"}.mdi-book-lock-open-outline:before{content:"\f168e"}.mdi-book-lock-outline:before{content:"\f168f"}.mdi-book-marker:before{content:"\f1690"}.mdi-book-marker-outline:before{content:"\f1691"}.mdi-book-minus:before{content:"\f05d9"}.mdi-book-minus-multiple:before{content:"\f0a94"}.mdi-book-minus-multiple-outline:before{content:"\f090b"}.mdi-book-minus-outline:before{content:"\f1692"}.mdi-book-multiple:before{content:"\f00bb"}.mdi-book-multiple-outline:before{content:"\f0436"}.mdi-book-music:before{content:"\f0067"}.mdi-book-music-outline:before{content:"\f1693"}.mdi-book-off:before{content:"\f1694"}.mdi-book-off-outline:before{content:"\f1695"}.mdi-book-open:before{content:"\f00bd"}.mdi-book-open-blank-variant:before{content:"\f00be"}.mdi-book-open-blank-variant-outline:before{content:"\f1ccb"}.mdi-book-open-outline:before{content:"\f0b63"}.mdi-book-open-page-variant:before{content:"\f05da"}.mdi-book-open-page-variant-outline:before{content:"\f15d6"}.mdi-book-open-variant:before{content:"\f14f7"}.mdi-book-open-variant-outline:before{content:"\f1ccc"}.mdi-book-outline:before{content:"\f0b64"}.mdi-book-play:before{content:"\f0e82"}.mdi-book-play-outline:before{content:"\f0e83"}.mdi-book-plus:before{content:"\f05db"}.mdi-book-plus-multiple:before{content:"\f0a95"}.mdi-book-plus-multiple-outline:before{content:"\f0ade"}.mdi-book-plus-outline:before{content:"\f1696"}.mdi-book-refresh:before{content:"\f1697"}.mdi-book-refresh-outline:before{content:"\f1698"}.mdi-book-remove:before{content:"\f0a97"}.mdi-book-remove-multiple:before{content:"\f0a96"}.mdi-book-remove-multiple-outline:before{content:"\f04ca"}.mdi-book-remove-outline:before{content:"\f1699"}.mdi-book-search:before{content:"\f0e84"}.mdi-book-search-outline:before{content:"\f0e85"}.mdi-book-settings:before{content:"\f169a"}.mdi-book-settings-outline:before{content:"\f169b"}.mdi-book-sync:before{content:"\f169c"}.mdi-book-sync-outline:before{content:"\f16c8"}.mdi-book-variant:before{content:"\f00bf"}.mdi-bookmark:before{content:"\f00c0"}.mdi-bookmark-box:before{content:"\f1b75"}.mdi-bookmark-box-multiple:before{content:"\f196c"}.mdi-bookmark-box-multiple-outline:before{content:"\f196d"}.mdi-bookmark-box-outline:before{content:"\f1b76"}.mdi-bookmark-check:before{content:"\f00c1"}.mdi-bookmark-check-outline:before{content:"\f137b"}.mdi-bookmark-minus:before{content:"\f09cc"}.mdi-bookmark-minus-outline:before{content:"\f09cd"}.mdi-bookmark-multiple:before{content:"\f0e15"}.mdi-bookmark-multiple-outline:before{content:"\f0e16"}.mdi-bookmark-music:before{content:"\f00c2"}.mdi-bookmark-music-outline:before{content:"\f1379"}.mdi-bookmark-off:before{content:"\f09ce"}.mdi-bookmark-off-outline:before{content:"\f09cf"}.mdi-bookmark-outline:before{content:"\f00c3"}.mdi-bookmark-plus:before{content:"\f00c5"}.mdi-bookmark-plus-outline:before{content:"\f00c4"}.mdi-bookmark-remove:before{content:"\f00c6"}.mdi-bookmark-remove-outline:before{content:"\f137a"}.mdi-bookshelf:before{content:"\f125f"}.mdi-boom-gate:before{content:"\f0e86"}.mdi-boom-gate-alert:before{content:"\f0e87"}.mdi-boom-gate-alert-outline:before{content:"\f0e88"}.mdi-boom-gate-arrow-down:before{content:"\f0e89"}.mdi-boom-gate-arrow-down-outline:before{content:"\f0e8a"}.mdi-boom-gate-arrow-up:before{content:"\f0e8c"}.mdi-boom-gate-arrow-up-outline:before{content:"\f0e8d"}.mdi-boom-gate-outline:before{content:"\f0e8b"}.mdi-boom-gate-up:before{content:"\f17f9"}.mdi-boom-gate-up-outline:before{content:"\f17fa"}.mdi-boombox:before{content:"\f05dc"}.mdi-boomerang:before{content:"\f10cf"}.mdi-bootstrap:before{content:"\f06c6"}.mdi-border-all:before{content:"\f00c7"}.mdi-border-all-variant:before{content:"\f08a1"}.mdi-border-bottom:before{content:"\f00c8"}.mdi-border-bottom-variant:before{content:"\f08a2"}.mdi-border-color:before{content:"\f00c9"}.mdi-border-horizontal:before{content:"\f00ca"}.mdi-border-inside:before{content:"\f00cb"}.mdi-border-left:before{content:"\f00cc"}.mdi-border-left-variant:before{content:"\f08a3"}.mdi-border-none:before{content:"\f00cd"}.mdi-border-none-variant:before{content:"\f08a4"}.mdi-border-outside:before{content:"\f00ce"}.mdi-border-radius:before{content:"\f1af4"}.mdi-border-right:before{content:"\f00cf"}.mdi-border-right-variant:before{content:"\f08a5"}.mdi-border-style:before{content:"\f00d0"}.mdi-border-top:before{content:"\f00d1"}.mdi-border-top-variant:before{content:"\f08a6"}.mdi-border-vertical:before{content:"\f00d2"}.mdi-bottle-soda:before{content:"\f1070"}.mdi-bottle-soda-classic:before{content:"\f1071"}.mdi-bottle-soda-classic-outline:before{content:"\f1363"}.mdi-bottle-soda-outline:before{content:"\f1072"}.mdi-bottle-tonic:before{content:"\f112e"}.mdi-bottle-tonic-outline:before{content:"\f112f"}.mdi-bottle-tonic-plus:before{content:"\f1130"}.mdi-bottle-tonic-plus-outline:before{content:"\f1131"}.mdi-bottle-tonic-skull:before{content:"\f1132"}.mdi-bottle-tonic-skull-outline:before{content:"\f1133"}.mdi-bottle-wine:before{content:"\f0854"}.mdi-bottle-wine-outline:before{content:"\f1310"}.mdi-bow-arrow:before{content:"\f1841"}.mdi-bow-tie:before{content:"\f0678"}.mdi-bowl:before{content:"\f028e"}.mdi-bowl-mix:before{content:"\f0617"}.mdi-bowl-mix-outline:before{content:"\f02e4"}.mdi-bowl-outline:before{content:"\f02a9"}.mdi-bowling:before{content:"\f00d3"}.mdi-box:before{content:"\f00d4"}.mdi-box-cutter:before{content:"\f00d5"}.mdi-box-cutter-off:before{content:"\f0b4a"}.mdi-box-shadow:before{content:"\f0637"}.mdi-boxing-glove:before{content:"\f0b65"}.mdi-braille:before{content:"\f09d0"}.mdi-brain:before{content:"\f09d1"}.mdi-bread-slice:before{content:"\f0cee"}.mdi-bread-slice-outline:before{content:"\f0cef"}.mdi-bridge:before{content:"\f0618"}.mdi-briefcase:before{content:"\f00d6"}.mdi-briefcase-account:before{content:"\f0cf0"}.mdi-briefcase-account-outline:before{content:"\f0cf1"}.mdi-briefcase-arrow-left-right:before{content:"\f1a8d"}.mdi-briefcase-arrow-left-right-outline:before{content:"\f1a8e"}.mdi-briefcase-arrow-up-down:before{content:"\f1a8f"}.mdi-briefcase-arrow-up-down-outline:before{content:"\f1a90"}.mdi-briefcase-check:before{content:"\f00d7"}.mdi-briefcase-check-outline:before{content:"\f131e"}.mdi-briefcase-clock:before{content:"\f10d0"}.mdi-briefcase-clock-outline:before{content:"\f10d1"}.mdi-briefcase-download:before{content:"\f00d8"}.mdi-briefcase-download-outline:before{content:"\f0c3d"}.mdi-briefcase-edit:before{content:"\f0a98"}.mdi-briefcase-edit-outline:before{content:"\f0c3e"}.mdi-briefcase-eye:before{content:"\f17d9"}.mdi-briefcase-eye-outline:before{content:"\f17da"}.mdi-briefcase-minus:before{content:"\f0a2a"}.mdi-briefcase-minus-outline:before{content:"\f0c3f"}.mdi-briefcase-off:before{content:"\f1658"}.mdi-briefcase-off-outline:before{content:"\f1659"}.mdi-briefcase-outline:before{content:"\f0814"}.mdi-briefcase-plus:before{content:"\f0a2b"}.mdi-briefcase-plus-outline:before{content:"\f0c40"}.mdi-briefcase-remove:before{content:"\f0a2c"}.mdi-briefcase-remove-outline:before{content:"\f0c41"}.mdi-briefcase-search:before{content:"\f0a2d"}.mdi-briefcase-search-outline:before{content:"\f0c42"}.mdi-briefcase-upload:before{content:"\f00d9"}.mdi-briefcase-upload-outline:before{content:"\f0c43"}.mdi-briefcase-variant:before{content:"\f1494"}.mdi-briefcase-variant-off:before{content:"\f165a"}.mdi-briefcase-variant-off-outline:before{content:"\f165b"}.mdi-briefcase-variant-outline:before{content:"\f1495"}.mdi-brightness-1:before{content:"\f00da"}.mdi-brightness-2:before{content:"\f00db"}.mdi-brightness-3:before{content:"\f00dc"}.mdi-brightness-4:before{content:"\f00dd"}.mdi-brightness-5:before{content:"\f00de"}.mdi-brightness-6:before{content:"\f00df"}.mdi-brightness-7:before{content:"\f00e0"}.mdi-brightness-auto:before{content:"\f00e1"}.mdi-brightness-percent:before{content:"\f0cf2"}.mdi-broadcast:before{content:"\f1720"}.mdi-broadcast-off:before{content:"\f1721"}.mdi-broom:before{content:"\f00e2"}.mdi-brush:before{content:"\f00e3"}.mdi-brush-off:before{content:"\f1771"}.mdi-brush-outline:before{content:"\f1a0d"}.mdi-brush-variant:before{content:"\f1813"}.mdi-bucket:before{content:"\f1415"}.mdi-bucket-outline:before{content:"\f1416"}.mdi-buffet:before{content:"\f0578"}.mdi-bug:before{content:"\f00e4"}.mdi-bug-check:before{content:"\f0a2e"}.mdi-bug-check-outline:before{content:"\f0a2f"}.mdi-bug-outline:before{content:"\f0a30"}.mdi-bug-pause:before{content:"\f1af5"}.mdi-bug-pause-outline:before{content:"\f1af6"}.mdi-bug-play:before{content:"\f1af7"}.mdi-bug-play-outline:before{content:"\f1af8"}.mdi-bug-stop:before{content:"\f1af9"}.mdi-bug-stop-outline:before{content:"\f1afa"}.mdi-bugle:before{content:"\f0db4"}.mdi-bulkhead-light:before{content:"\f1a2f"}.mdi-bulldozer:before{content:"\f0b22"}.mdi-bullet:before{content:"\f0cf3"}.mdi-bulletin-board:before{content:"\f00e5"}.mdi-bullhorn:before{content:"\f00e6"}.mdi-bullhorn-outline:before{content:"\f0b23"}.mdi-bullhorn-variant:before{content:"\f196e"}.mdi-bullhorn-variant-outline:before{content:"\f196f"}.mdi-bullseye:before{content:"\f05dd"}.mdi-bullseye-arrow:before{content:"\f08c9"}.mdi-bulma:before{content:"\f12e7"}.mdi-bunk-bed:before{content:"\f1302"}.mdi-bunk-bed-outline:before{content:"\f0097"}.mdi-bus:before{content:"\f00e7"}.mdi-bus-alert:before{content:"\f0a99"}.mdi-bus-articulated-end:before{content:"\f079c"}.mdi-bus-articulated-front:before{content:"\f079d"}.mdi-bus-clock:before{content:"\f08ca"}.mdi-bus-double-decker:before{content:"\f079e"}.mdi-bus-electric:before{content:"\f191d"}.mdi-bus-marker:before{content:"\f1212"}.mdi-bus-multiple:before{content:"\f0f3f"}.mdi-bus-school:before{content:"\f079f"}.mdi-bus-side:before{content:"\f07a0"}.mdi-bus-sign:before{content:"\f1cc1"}.mdi-bus-stop:before{content:"\f1012"}.mdi-bus-stop-covered:before{content:"\f1013"}.mdi-bus-stop-uncovered:before{content:"\f1014"}.mdi-bus-wrench:before{content:"\f1cc2"}.mdi-butterfly:before{content:"\f1589"}.mdi-butterfly-outline:before{content:"\f158a"}.mdi-button-cursor:before{content:"\f1b4f"}.mdi-button-pointer:before{content:"\f1b50"}.mdi-cabin-a-frame:before{content:"\f188c"}.mdi-cable-data:before{content:"\f1394"}.mdi-cached:before{content:"\f00e8"}.mdi-cactus:before{content:"\f0db5"}.mdi-cake:before{content:"\f00e9"}.mdi-cake-layered:before{content:"\f00ea"}.mdi-cake-variant:before{content:"\f00eb"}.mdi-cake-variant-outline:before{content:"\f17f0"}.mdi-calculator:before{content:"\f00ec"}.mdi-calculator-variant:before{content:"\f0a9a"}.mdi-calculator-variant-outline:before{content:"\f15a6"}.mdi-calendar:before{content:"\f00ed"}.mdi-calendar-account:before{content:"\f0ed7"}.mdi-calendar-account-outline:before{content:"\f0ed8"}.mdi-calendar-alert:before{content:"\f0a31"}.mdi-calendar-alert-outline:before{content:"\f1b62"}.mdi-calendar-arrow-left:before{content:"\f1134"}.mdi-calendar-arrow-right:before{content:"\f1135"}.mdi-calendar-badge:before{content:"\f1b9d"}.mdi-calendar-badge-outline:before{content:"\f1b9e"}.mdi-calendar-blank:before{content:"\f00ee"}.mdi-calendar-blank-multiple:before{content:"\f1073"}.mdi-calendar-blank-outline:before{content:"\f0b66"}.mdi-calendar-check:before{content:"\f00ef"}.mdi-calendar-check-outline:before{content:"\f0c44"}.mdi-calendar-clock:before{content:"\f00f0"}.mdi-calendar-clock-outline:before{content:"\f16e1"}.mdi-calendar-collapse-horizontal:before{content:"\f189d"}.mdi-calendar-collapse-horizontal-outline:before{content:"\f1b63"}.mdi-calendar-cursor:before{content:"\f157b"}.mdi-calendar-cursor-outline:before{content:"\f1b64"}.mdi-calendar-edit:before{content:"\f08a7"}.mdi-calendar-edit-outline:before{content:"\f1b65"}.mdi-calendar-end:before{content:"\f166c"}.mdi-calendar-end-outline:before{content:"\f1b66"}.mdi-calendar-expand-horizontal:before{content:"\f189e"}.mdi-calendar-expand-horizontal-outline:before{content:"\f1b67"}.mdi-calendar-export:before{content:"\f0b24"}.mdi-calendar-export-outline:before{content:"\f1b68"}.mdi-calendar-filter:before{content:"\f1a32"}.mdi-calendar-filter-outline:before{content:"\f1a33"}.mdi-calendar-heart:before{content:"\f09d2"}.mdi-calendar-heart-outline:before{content:"\f1b69"}.mdi-calendar-import:before{content:"\f0b25"}.mdi-calendar-import-outline:before{content:"\f1b6a"}.mdi-calendar-lock:before{content:"\f1641"}.mdi-calendar-lock-open:before{content:"\f1b5b"}.mdi-calendar-lock-open-outline:before{content:"\f1b5c"}.mdi-calendar-lock-outline:before{content:"\f1642"}.mdi-calendar-minus:before{content:"\f0d5c"}.mdi-calendar-minus-outline:before{content:"\f1b6b"}.mdi-calendar-month:before{content:"\f0e17"}.mdi-calendar-month-outline:before{content:"\f0e18"}.mdi-calendar-multiple:before{content:"\f00f1"}.mdi-calendar-multiple-check:before{content:"\f00f2"}.mdi-calendar-multiselect:before{content:"\f0a32"}.mdi-calendar-multiselect-outline:before{content:"\f1b55"}.mdi-calendar-outline:before{content:"\f0b67"}.mdi-calendar-plus:before{content:"\f00f3"}.mdi-calendar-plus-outline:before{content:"\f1b6c"}.mdi-calendar-question:before{content:"\f0692"}.mdi-calendar-question-outline:before{content:"\f1b6d"}.mdi-calendar-range:before{content:"\f0679"}.mdi-calendar-range-outline:before{content:"\f0b68"}.mdi-calendar-refresh:before{content:"\f01e1"}.mdi-calendar-refresh-outline:before{content:"\f0203"}.mdi-calendar-remove:before{content:"\f00f4"}.mdi-calendar-remove-outline:before{content:"\f0c45"}.mdi-calendar-search:before{content:"\f094c"}.mdi-calendar-search-outline:before{content:"\f1b6e"}.mdi-calendar-star:before{content:"\f09d3"}.mdi-calendar-star-four-points:before{content:"\f1c1f"}.mdi-calendar-star-outline:before{content:"\f1b53"}.mdi-calendar-start:before{content:"\f166d"}.mdi-calendar-start-outline:before{content:"\f1b6f"}.mdi-calendar-sync:before{content:"\f0e8e"}.mdi-calendar-sync-outline:before{content:"\f0e8f"}.mdi-calendar-text:before{content:"\f00f5"}.mdi-calendar-text-outline:before{content:"\f0c46"}.mdi-calendar-today:before{content:"\f00f6"}.mdi-calendar-today-outline:before{content:"\f1a30"}.mdi-calendar-week:before{content:"\f0a33"}.mdi-calendar-week-begin:before{content:"\f0a34"}.mdi-calendar-week-begin-outline:before{content:"\f1a31"}.mdi-calendar-week-outline:before{content:"\f1a34"}.mdi-calendar-weekend:before{content:"\f0ed9"}.mdi-calendar-weekend-outline:before{content:"\f0eda"}.mdi-call-made:before{content:"\f00f7"}.mdi-call-merge:before{content:"\f00f8"}.mdi-call-missed:before{content:"\f00f9"}.mdi-call-received:before{content:"\f00fa"}.mdi-call-split:before{content:"\f00fb"}.mdi-camcorder:before{content:"\f00fc"}.mdi-camcorder-off:before{content:"\f00ff"}.mdi-camera:before{content:"\f0100"}.mdi-camera-account:before{content:"\f08cb"}.mdi-camera-burst:before{content:"\f0693"}.mdi-camera-control:before{content:"\f0b69"}.mdi-camera-document:before{content:"\f1871"}.mdi-camera-document-off:before{content:"\f1872"}.mdi-camera-enhance:before{content:"\f0101"}.mdi-camera-enhance-outline:before{content:"\f0b6a"}.mdi-camera-flip:before{content:"\f15d9"}.mdi-camera-flip-outline:before{content:"\f15da"}.mdi-camera-front:before{content:"\f0102"}.mdi-camera-front-variant:before{content:"\f0103"}.mdi-camera-gopro:before{content:"\f07a1"}.mdi-camera-image:before{content:"\f08cc"}.mdi-camera-iris:before{content:"\f0104"}.mdi-camera-lock:before{content:"\f1a14"}.mdi-camera-lock-open:before{content:"\f1c0d"}.mdi-camera-lock-open-outline:before{content:"\f1c0e"}.mdi-camera-lock-outline:before{content:"\f1a15"}.mdi-camera-marker:before{content:"\f19a7"}.mdi-camera-marker-outline:before{content:"\f19a8"}.mdi-camera-metering-center:before{content:"\f07a2"}.mdi-camera-metering-matrix:before{content:"\f07a3"}.mdi-camera-metering-partial:before{content:"\f07a4"}.mdi-camera-metering-spot:before{content:"\f07a5"}.mdi-camera-off:before{content:"\f05df"}.mdi-camera-off-outline:before{content:"\f19bf"}.mdi-camera-outline:before{content:"\f0d5d"}.mdi-camera-party-mode:before{content:"\f0105"}.mdi-camera-plus:before{content:"\f0edb"}.mdi-camera-plus-outline:before{content:"\f0edc"}.mdi-camera-rear:before{content:"\f0106"}.mdi-camera-rear-variant:before{content:"\f0107"}.mdi-camera-retake:before{content:"\f0e19"}.mdi-camera-retake-outline:before{content:"\f0e1a"}.mdi-camera-switch:before{content:"\f0108"}.mdi-camera-switch-outline:before{content:"\f084a"}.mdi-camera-timer:before{content:"\f0109"}.mdi-camera-wireless:before{content:"\f0db6"}.mdi-camera-wireless-outline:before{content:"\f0db7"}.mdi-campfire:before{content:"\f0edd"}.mdi-cancel:before{content:"\f073a"}.mdi-candelabra:before{content:"\f17d2"}.mdi-candelabra-fire:before{content:"\f17d3"}.mdi-candle:before{content:"\f05e2"}.mdi-candy:before{content:"\f1970"}.mdi-candy-off:before{content:"\f1971"}.mdi-candy-off-outline:before{content:"\f1972"}.mdi-candy-outline:before{content:"\f1973"}.mdi-candycane:before{content:"\f010a"}.mdi-cannabis:before{content:"\f07a6"}.mdi-cannabis-off:before{content:"\f166e"}.mdi-caps-lock:before{content:"\f0a9b"}.mdi-car:before{content:"\f010b"}.mdi-car-2-plus:before{content:"\f1015"}.mdi-car-3-plus:before{content:"\f1016"}.mdi-car-arrow-left:before{content:"\f13b2"}.mdi-car-arrow-right:before{content:"\f13b3"}.mdi-car-back:before{content:"\f0e1b"}.mdi-car-battery:before{content:"\f010c"}.mdi-car-brake-abs:before{content:"\f0c47"}.mdi-car-brake-alert:before{content:"\f0c48"}.mdi-car-brake-fluid-level:before{content:"\f1909"}.mdi-car-brake-hold:before{content:"\f0d5e"}.mdi-car-brake-low-pressure:before{content:"\f190a"}.mdi-car-brake-parking:before{content:"\f0d5f"}.mdi-car-brake-retarder:before{content:"\f1017"}.mdi-car-brake-temperature:before{content:"\f190b"}.mdi-car-brake-worn-linings:before{content:"\f190c"}.mdi-car-child-seat:before{content:"\f0fa3"}.mdi-car-clock:before{content:"\f1974"}.mdi-car-clutch:before{content:"\f1018"}.mdi-car-cog:before{content:"\f13cc"}.mdi-car-connected:before{content:"\f010d"}.mdi-car-convertible:before{content:"\f07a7"}.mdi-car-coolant-level:before{content:"\f1019"}.mdi-car-cruise-control:before{content:"\f0d60"}.mdi-car-defrost-front:before{content:"\f0d61"}.mdi-car-defrost-rear:before{content:"\f0d62"}.mdi-car-door:before{content:"\f0b6b"}.mdi-car-door-lock:before{content:"\f109d"}.mdi-car-door-lock-open:before{content:"\f1c81"}.mdi-car-electric:before{content:"\f0b6c"}.mdi-car-electric-outline:before{content:"\f15b5"}.mdi-car-emergency:before{content:"\f160f"}.mdi-car-esp:before{content:"\f0c49"}.mdi-car-estate:before{content:"\f07a8"}.mdi-car-hatchback:before{content:"\f07a9"}.mdi-car-info:before{content:"\f11be"}.mdi-car-key:before{content:"\f0b6d"}.mdi-car-lifted-pickup:before{content:"\f152d"}.mdi-car-light-alert:before{content:"\f190d"}.mdi-car-light-dimmed:before{content:"\f0c4a"}.mdi-car-light-fog:before{content:"\f0c4b"}.mdi-car-light-high:before{content:"\f0c4c"}.mdi-car-limousine:before{content:"\f08cd"}.mdi-car-multiple:before{content:"\f0b6e"}.mdi-car-off:before{content:"\f0e1c"}.mdi-car-outline:before{content:"\f14ed"}.mdi-car-parking-lights:before{content:"\f0d63"}.mdi-car-pickup:before{content:"\f07aa"}.mdi-car-search:before{content:"\f1b8d"}.mdi-car-search-outline:before{content:"\f1b8e"}.mdi-car-seat:before{content:"\f0fa4"}.mdi-car-seat-cooler:before{content:"\f0fa5"}.mdi-car-seat-heater:before{content:"\f0fa6"}.mdi-car-select:before{content:"\f1879"}.mdi-car-settings:before{content:"\f13cd"}.mdi-car-shift-pattern:before{content:"\f0f40"}.mdi-car-side:before{content:"\f07ab"}.mdi-car-speed-limiter:before{content:"\f190e"}.mdi-car-sports:before{content:"\f07ac"}.mdi-car-tire-alert:before{content:"\f0c4d"}.mdi-car-traction-control:before{content:"\f0d64"}.mdi-car-turbocharger:before{content:"\f101a"}.mdi-car-wash:before{content:"\f010e"}.mdi-car-windshield:before{content:"\f101b"}.mdi-car-windshield-outline:before{content:"\f101c"}.mdi-car-wireless:before{content:"\f1878"}.mdi-car-wrench:before{content:"\f1814"}.mdi-carabiner:before{content:"\f14c0"}.mdi-caravan:before{content:"\f07ad"}.mdi-card:before{content:"\f0b6f"}.mdi-card-account-details:before{content:"\f05d2"}.mdi-card-account-details-outline:before{content:"\f0dab"}.mdi-card-account-details-star:before{content:"\f02a3"}.mdi-card-account-details-star-outline:before{content:"\f06db"}.mdi-card-account-mail:before{content:"\f018e"}.mdi-card-account-mail-outline:before{content:"\f0e98"}.mdi-card-account-phone:before{content:"\f0e99"}.mdi-card-account-phone-outline:before{content:"\f0e9a"}.mdi-card-bulleted:before{content:"\f0b70"}.mdi-card-bulleted-off:before{content:"\f0b71"}.mdi-card-bulleted-off-outline:before{content:"\f0b72"}.mdi-card-bulleted-outline:before{content:"\f0b73"}.mdi-card-bulleted-settings:before{content:"\f0b74"}.mdi-card-bulleted-settings-outline:before{content:"\f0b75"}.mdi-card-minus:before{content:"\f1600"}.mdi-card-minus-outline:before{content:"\f1601"}.mdi-card-multiple:before{content:"\f17f1"}.mdi-card-multiple-outline:before{content:"\f17f2"}.mdi-card-off:before{content:"\f1602"}.mdi-card-off-outline:before{content:"\f1603"}.mdi-card-outline:before{content:"\f0b76"}.mdi-card-plus:before{content:"\f11ff"}.mdi-card-plus-outline:before{content:"\f1200"}.mdi-card-remove:before{content:"\f1604"}.mdi-card-remove-outline:before{content:"\f1605"}.mdi-card-search:before{content:"\f1074"}.mdi-card-search-outline:before{content:"\f1075"}.mdi-card-text:before{content:"\f0b77"}.mdi-card-text-outline:before{content:"\f0b78"}.mdi-cards:before{content:"\f0638"}.mdi-cards-club:before{content:"\f08ce"}.mdi-cards-club-outline:before{content:"\f189f"}.mdi-cards-diamond:before{content:"\f08cf"}.mdi-cards-diamond-outline:before{content:"\f101d"}.mdi-cards-heart:before{content:"\f08d0"}.mdi-cards-heart-outline:before{content:"\f18a0"}.mdi-cards-outline:before{content:"\f0639"}.mdi-cards-playing:before{content:"\f18a1"}.mdi-cards-playing-club:before{content:"\f18a2"}.mdi-cards-playing-club-multiple:before{content:"\f18a3"}.mdi-cards-playing-club-multiple-outline:before{content:"\f18a4"}.mdi-cards-playing-club-outline:before{content:"\f18a5"}.mdi-cards-playing-diamond:before{content:"\f18a6"}.mdi-cards-playing-diamond-multiple:before{content:"\f18a7"}.mdi-cards-playing-diamond-multiple-outline:before{content:"\f18a8"}.mdi-cards-playing-diamond-outline:before{content:"\f18a9"}.mdi-cards-playing-heart:before{content:"\f18aa"}.mdi-cards-playing-heart-multiple:before{content:"\f18ab"}.mdi-cards-playing-heart-multiple-outline:before{content:"\f18ac"}.mdi-cards-playing-heart-outline:before{content:"\f18ad"}.mdi-cards-playing-outline:before{content:"\f063a"}.mdi-cards-playing-spade:before{content:"\f18ae"}.mdi-cards-playing-spade-multiple:before{content:"\f18af"}.mdi-cards-playing-spade-multiple-outline:before{content:"\f18b0"}.mdi-cards-playing-spade-outline:before{content:"\f18b1"}.mdi-cards-spade:before{content:"\f08d1"}.mdi-cards-spade-outline:before{content:"\f18b2"}.mdi-cards-variant:before{content:"\f06c7"}.mdi-carrot:before{content:"\f010f"}.mdi-cart:before{content:"\f0110"}.mdi-cart-arrow-down:before{content:"\f0d66"}.mdi-cart-arrow-right:before{content:"\f0c4e"}.mdi-cart-arrow-up:before{content:"\f0d67"}.mdi-cart-check:before{content:"\f15ea"}.mdi-cart-heart:before{content:"\f18e0"}.mdi-cart-minus:before{content:"\f0d68"}.mdi-cart-off:before{content:"\f066b"}.mdi-cart-outline:before{content:"\f0111"}.mdi-cart-percent:before{content:"\f1bae"}.mdi-cart-plus:before{content:"\f0112"}.mdi-cart-remove:before{content:"\f0d69"}.mdi-cart-variant:before{content:"\f15eb"}.mdi-case-sensitive-alt:before{content:"\f0113"}.mdi-cash:before{content:"\f0114"}.mdi-cash-100:before{content:"\f0115"}.mdi-cash-check:before{content:"\f14ee"}.mdi-cash-clock:before{content:"\f1a91"}.mdi-cash-edit:before{content:"\f1cab"}.mdi-cash-fast:before{content:"\f185c"}.mdi-cash-lock:before{content:"\f14ea"}.mdi-cash-lock-open:before{content:"\f14eb"}.mdi-cash-marker:before{content:"\f0db8"}.mdi-cash-minus:before{content:"\f1260"}.mdi-cash-multiple:before{content:"\f0116"}.mdi-cash-off:before{content:"\f1c79"}.mdi-cash-plus:before{content:"\f1261"}.mdi-cash-refund:before{content:"\f0a9c"}.mdi-cash-register:before{content:"\f0cf4"}.mdi-cash-remove:before{content:"\f1262"}.mdi-cash-sync:before{content:"\f1a92"}.mdi-cassette:before{content:"\f09d4"}.mdi-cast:before{content:"\f0118"}.mdi-cast-audio:before{content:"\f101e"}.mdi-cast-audio-variant:before{content:"\f1749"}.mdi-cast-connected:before{content:"\f0119"}.mdi-cast-education:before{content:"\f0e1d"}.mdi-cast-off:before{content:"\f078a"}.mdi-cast-variant:before{content:"\f001f"}.mdi-castle:before{content:"\f011a"}.mdi-cat:before{content:"\f011b"}.mdi-cctv:before{content:"\f07ae"}.mdi-cctv-off:before{content:"\f185f"}.mdi-ceiling-fan:before{content:"\f1797"}.mdi-ceiling-fan-light:before{content:"\f1798"}.mdi-ceiling-light:before{content:"\f0769"}.mdi-ceiling-light-multiple:before{content:"\f18dd"}.mdi-ceiling-light-multiple-outline:before{content:"\f18de"}.mdi-ceiling-light-outline:before{content:"\f17c7"}.mdi-cellphone:before{content:"\f011c"}.mdi-cellphone-arrow-down:before{content:"\f09d5"}.mdi-cellphone-arrow-down-variant:before{content:"\f19c5"}.mdi-cellphone-basic:before{content:"\f011e"}.mdi-cellphone-charging:before{content:"\f1397"}.mdi-cellphone-check:before{content:"\f17fd"}.mdi-cellphone-cog:before{content:"\f0951"}.mdi-cellphone-dock:before{content:"\f011f"}.mdi-cellphone-information:before{content:"\f0f41"}.mdi-cellphone-key:before{content:"\f094e"}.mdi-cellphone-link:before{content:"\f0121"}.mdi-cellphone-link-off:before{content:"\f0122"}.mdi-cellphone-lock:before{content:"\f094f"}.mdi-cellphone-marker:before{content:"\f183a"}.mdi-cellphone-message:before{content:"\f08d3"}.mdi-cellphone-message-off:before{content:"\f10d2"}.mdi-cellphone-nfc:before{content:"\f0e90"}.mdi-cellphone-nfc-off:before{content:"\f12d8"}.mdi-cellphone-off:before{content:"\f0950"}.mdi-cellphone-play:before{content:"\f101f"}.mdi-cellphone-remove:before{content:"\f094d"}.mdi-cellphone-screenshot:before{content:"\f0a35"}.mdi-cellphone-settings:before{content:"\f0123"}.mdi-cellphone-sound:before{content:"\f0952"}.mdi-cellphone-text:before{content:"\f08d2"}.mdi-cellphone-wireless:before{content:"\f0815"}.mdi-centos:before{content:"\f111a"}.mdi-certificate:before{content:"\f0124"}.mdi-certificate-outline:before{content:"\f1188"}.mdi-chair-rolling:before{content:"\f0f48"}.mdi-chair-school:before{content:"\f0125"}.mdi-chandelier:before{content:"\f1793"}.mdi-charity:before{content:"\f0c4f"}.mdi-charity-search:before{content:"\f1c82"}.mdi-chart-arc:before{content:"\f0126"}.mdi-chart-areaspline:before{content:"\f0127"}.mdi-chart-areaspline-variant:before{content:"\f0e91"}.mdi-chart-bar:before{content:"\f0128"}.mdi-chart-bar-stacked:before{content:"\f076a"}.mdi-chart-bell-curve:before{content:"\f0c50"}.mdi-chart-bell-curve-cumulative:before{content:"\f0fa7"}.mdi-chart-box:before{content:"\f154d"}.mdi-chart-box-multiple:before{content:"\f1ccd"}.mdi-chart-box-multiple-outline:before{content:"\f1cce"}.mdi-chart-box-outline:before{content:"\f154e"}.mdi-chart-box-plus-outline:before{content:"\f154f"}.mdi-chart-bubble:before{content:"\f05e3"}.mdi-chart-donut:before{content:"\f07af"}.mdi-chart-donut-variant:before{content:"\f07b0"}.mdi-chart-gantt:before{content:"\f066c"}.mdi-chart-histogram:before{content:"\f0129"}.mdi-chart-line:before{content:"\f012a"}.mdi-chart-line-stacked:before{content:"\f076b"}.mdi-chart-line-variant:before{content:"\f07b1"}.mdi-chart-multiline:before{content:"\f08d4"}.mdi-chart-multiple:before{content:"\f1213"}.mdi-chart-pie:before{content:"\f012b"}.mdi-chart-pie-outline:before{content:"\f1bdf"}.mdi-chart-ppf:before{content:"\f1380"}.mdi-chart-sankey:before{content:"\f11df"}.mdi-chart-sankey-variant:before{content:"\f11e0"}.mdi-chart-scatter-plot:before{content:"\f0e92"}.mdi-chart-scatter-plot-hexbin:before{content:"\f066d"}.mdi-chart-timeline:before{content:"\f066e"}.mdi-chart-timeline-variant:before{content:"\f0e93"}.mdi-chart-timeline-variant-shimmer:before{content:"\f15b6"}.mdi-chart-tree:before{content:"\f0e94"}.mdi-chart-waterfall:before{content:"\f1918"}.mdi-chat:before{content:"\f0b79"}.mdi-chat-alert:before{content:"\f0b7a"}.mdi-chat-alert-outline:before{content:"\f12c9"}.mdi-chat-minus:before{content:"\f1410"}.mdi-chat-minus-outline:before{content:"\f1413"}.mdi-chat-outline:before{content:"\f0ede"}.mdi-chat-plus:before{content:"\f140f"}.mdi-chat-plus-outline:before{content:"\f1412"}.mdi-chat-processing:before{content:"\f0b7b"}.mdi-chat-processing-outline:before{content:"\f12ca"}.mdi-chat-question:before{content:"\f1738"}.mdi-chat-question-outline:before{content:"\f1739"}.mdi-chat-remove:before{content:"\f1411"}.mdi-chat-remove-outline:before{content:"\f1414"}.mdi-chat-sleep:before{content:"\f12d1"}.mdi-chat-sleep-outline:before{content:"\f12d2"}.mdi-check:before{content:"\f012c"}.mdi-check-all:before{content:"\f012d"}.mdi-check-bold:before{content:"\f0e1e"}.mdi-check-circle:before{content:"\f05e0"}.mdi-check-circle-outline:before{content:"\f05e1"}.mdi-check-decagram:before{content:"\f0791"}.mdi-check-decagram-outline:before{content:"\f1740"}.mdi-check-network:before{content:"\f0c53"}.mdi-check-network-outline:before{content:"\f0c54"}.mdi-check-outline:before{content:"\f0855"}.mdi-check-underline:before{content:"\f0e1f"}.mdi-check-underline-circle:before{content:"\f0e20"}.mdi-check-underline-circle-outline:before{content:"\f0e21"}.mdi-checkbook:before{content:"\f0a9d"}.mdi-checkbook-arrow-left:before{content:"\f1c1d"}.mdi-checkbook-arrow-right:before{content:"\f1c1e"}.mdi-checkbox-blank:before{content:"\f012e"}.mdi-checkbox-blank-badge:before{content:"\f1176"}.mdi-checkbox-blank-badge-outline:before{content:"\f0117"}.mdi-checkbox-blank-circle:before{content:"\f012f"}.mdi-checkbox-blank-circle-outline:before{content:"\f0130"}.mdi-checkbox-blank-off:before{content:"\f12ec"}.mdi-checkbox-blank-off-outline:before{content:"\f12ed"}.mdi-checkbox-blank-outline:before{content:"\f0131"}.mdi-checkbox-intermediate:before{content:"\f0856"}.mdi-checkbox-intermediate-variant:before{content:"\f1b54"}.mdi-checkbox-marked:before{content:"\f0132"}.mdi-checkbox-marked-circle:before{content:"\f0133"}.mdi-checkbox-marked-circle-auto-outline:before{content:"\f1c26"}.mdi-checkbox-marked-circle-minus-outline:before{content:"\f1c27"}.mdi-checkbox-marked-circle-outline:before{content:"\f0134"}.mdi-checkbox-marked-circle-plus-outline:before{content:"\f1927"}.mdi-checkbox-marked-outline:before{content:"\f0135"}.mdi-checkbox-multiple-blank:before{content:"\f0136"}.mdi-checkbox-multiple-blank-circle:before{content:"\f063b"}.mdi-checkbox-multiple-blank-circle-outline:before{content:"\f063c"}.mdi-checkbox-multiple-blank-outline:before{content:"\f0137"}.mdi-checkbox-multiple-marked:before{content:"\f0138"}.mdi-checkbox-multiple-marked-circle:before{content:"\f063d"}.mdi-checkbox-multiple-marked-circle-outline:before{content:"\f063e"}.mdi-checkbox-multiple-marked-outline:before{content:"\f0139"}.mdi-checkbox-multiple-outline:before{content:"\f0c51"}.mdi-checkbox-outline:before{content:"\f0c52"}.mdi-checkerboard:before{content:"\f013a"}.mdi-checkerboard-minus:before{content:"\f1202"}.mdi-checkerboard-plus:before{content:"\f1201"}.mdi-checkerboard-remove:before{content:"\f1203"}.mdi-cheese:before{content:"\f12b9"}.mdi-cheese-off:before{content:"\f13ee"}.mdi-chef-hat:before{content:"\f0b7c"}.mdi-chemical-weapon:before{content:"\f013b"}.mdi-chess-bishop:before{content:"\f085c"}.mdi-chess-king:before{content:"\f0857"}.mdi-chess-knight:before{content:"\f0858"}.mdi-chess-pawn:before{content:"\f0859"}.mdi-chess-queen:before{content:"\f085a"}.mdi-chess-rook:before{content:"\f085b"}.mdi-chevron-double-down:before{content:"\f013c"}.mdi-chevron-double-left:before{content:"\f013d"}.mdi-chevron-double-right:before{content:"\f013e"}.mdi-chevron-double-up:before{content:"\f013f"}.mdi-chevron-down:before{content:"\f0140"}.mdi-chevron-down-box:before{content:"\f09d6"}.mdi-chevron-down-box-outline:before{content:"\f09d7"}.mdi-chevron-down-circle:before{content:"\f0b26"}.mdi-chevron-down-circle-outline:before{content:"\f0b27"}.mdi-chevron-left:before{content:"\f0141"}.mdi-chevron-left-box:before{content:"\f09d8"}.mdi-chevron-left-box-outline:before{content:"\f09d9"}.mdi-chevron-left-circle:before{content:"\f0b28"}.mdi-chevron-left-circle-outline:before{content:"\f0b29"}.mdi-chevron-right:before{content:"\f0142"}.mdi-chevron-right-box:before{content:"\f09da"}.mdi-chevron-right-box-outline:before{content:"\f09db"}.mdi-chevron-right-circle:before{content:"\f0b2a"}.mdi-chevron-right-circle-outline:before{content:"\f0b2b"}.mdi-chevron-triple-down:before{content:"\f0db9"}.mdi-chevron-triple-left:before{content:"\f0dba"}.mdi-chevron-triple-right:before{content:"\f0dbb"}.mdi-chevron-triple-up:before{content:"\f0dbc"}.mdi-chevron-up:before{content:"\f0143"}.mdi-chevron-up-box:before{content:"\f09dc"}.mdi-chevron-up-box-outline:before{content:"\f09dd"}.mdi-chevron-up-circle:before{content:"\f0b2c"}.mdi-chevron-up-circle-outline:before{content:"\f0b2d"}.mdi-chili-alert:before{content:"\f17ea"}.mdi-chili-alert-outline:before{content:"\f17eb"}.mdi-chili-hot:before{content:"\f07b2"}.mdi-chili-hot-outline:before{content:"\f17ec"}.mdi-chili-medium:before{content:"\f07b3"}.mdi-chili-medium-outline:before{content:"\f17ed"}.mdi-chili-mild:before{content:"\f07b4"}.mdi-chili-mild-outline:before{content:"\f17ee"}.mdi-chili-off:before{content:"\f1467"}.mdi-chili-off-outline:before{content:"\f17ef"}.mdi-chip:before{content:"\f061a"}.mdi-church:before{content:"\f0144"}.mdi-church-outline:before{content:"\f1b02"}.mdi-cigar:before{content:"\f1189"}.mdi-cigar-off:before{content:"\f141b"}.mdi-circle:before{content:"\f0765"}.mdi-circle-box:before{content:"\f15dc"}.mdi-circle-box-outline:before{content:"\f15dd"}.mdi-circle-double:before{content:"\f0e95"}.mdi-circle-edit-outline:before{content:"\f08d5"}.mdi-circle-expand:before{content:"\f0e96"}.mdi-circle-half:before{content:"\f1395"}.mdi-circle-half-full:before{content:"\f1396"}.mdi-circle-medium:before{content:"\f09de"}.mdi-circle-multiple:before{content:"\f0b38"}.mdi-circle-multiple-outline:before{content:"\f0695"}.mdi-circle-off-outline:before{content:"\f10d3"}.mdi-circle-opacity:before{content:"\f1853"}.mdi-circle-outline:before{content:"\f0766"}.mdi-circle-slice-1:before{content:"\f0a9e"}.mdi-circle-slice-2:before{content:"\f0a9f"}.mdi-circle-slice-3:before{content:"\f0aa0"}.mdi-circle-slice-4:before{content:"\f0aa1"}.mdi-circle-slice-5:before{content:"\f0aa2"}.mdi-circle-slice-6:before{content:"\f0aa3"}.mdi-circle-slice-7:before{content:"\f0aa4"}.mdi-circle-slice-8:before{content:"\f0aa5"}.mdi-circle-small:before{content:"\f09df"}.mdi-circular-saw:before{content:"\f0e22"}.mdi-city:before{content:"\f0146"}.mdi-city-switch:before{content:"\f1c28"}.mdi-city-variant:before{content:"\f0a36"}.mdi-city-variant-outline:before{content:"\f0a37"}.mdi-clipboard:before{content:"\f0147"}.mdi-clipboard-account:before{content:"\f0148"}.mdi-clipboard-account-outline:before{content:"\f0c55"}.mdi-clipboard-alert:before{content:"\f0149"}.mdi-clipboard-alert-outline:before{content:"\f0cf7"}.mdi-clipboard-arrow-down:before{content:"\f014a"}.mdi-clipboard-arrow-down-outline:before{content:"\f0c56"}.mdi-clipboard-arrow-left:before{content:"\f014b"}.mdi-clipboard-arrow-left-outline:before{content:"\f0cf8"}.mdi-clipboard-arrow-right:before{content:"\f0cf9"}.mdi-clipboard-arrow-right-outline:before{content:"\f0cfa"}.mdi-clipboard-arrow-up:before{content:"\f0c57"}.mdi-clipboard-arrow-up-outline:before{content:"\f0c58"}.mdi-clipboard-check:before{content:"\f014e"}.mdi-clipboard-check-multiple:before{content:"\f1263"}.mdi-clipboard-check-multiple-outline:before{content:"\f1264"}.mdi-clipboard-check-outline:before{content:"\f08a8"}.mdi-clipboard-clock:before{content:"\f16e2"}.mdi-clipboard-clock-outline:before{content:"\f16e3"}.mdi-clipboard-edit:before{content:"\f14e5"}.mdi-clipboard-edit-outline:before{content:"\f14e6"}.mdi-clipboard-file:before{content:"\f1265"}.mdi-clipboard-file-outline:before{content:"\f1266"}.mdi-clipboard-flow:before{content:"\f06c8"}.mdi-clipboard-flow-outline:before{content:"\f1117"}.mdi-clipboard-list:before{content:"\f10d4"}.mdi-clipboard-list-outline:before{content:"\f10d5"}.mdi-clipboard-minus:before{content:"\f1618"}.mdi-clipboard-minus-outline:before{content:"\f1619"}.mdi-clipboard-multiple:before{content:"\f1267"}.mdi-clipboard-multiple-outline:before{content:"\f1268"}.mdi-clipboard-off:before{content:"\f161a"}.mdi-clipboard-off-outline:before{content:"\f161b"}.mdi-clipboard-outline:before{content:"\f014c"}.mdi-clipboard-play:before{content:"\f0c59"}.mdi-clipboard-play-multiple:before{content:"\f1269"}.mdi-clipboard-play-multiple-outline:before{content:"\f126a"}.mdi-clipboard-play-outline:before{content:"\f0c5a"}.mdi-clipboard-plus:before{content:"\f0751"}.mdi-clipboard-plus-outline:before{content:"\f131f"}.mdi-clipboard-pulse:before{content:"\f085d"}.mdi-clipboard-pulse-outline:before{content:"\f085e"}.mdi-clipboard-remove:before{content:"\f161c"}.mdi-clipboard-remove-outline:before{content:"\f161d"}.mdi-clipboard-search:before{content:"\f161e"}.mdi-clipboard-search-outline:before{content:"\f161f"}.mdi-clipboard-text:before{content:"\f014d"}.mdi-clipboard-text-clock:before{content:"\f18f9"}.mdi-clipboard-text-clock-outline:before{content:"\f18fa"}.mdi-clipboard-text-multiple:before{content:"\f126b"}.mdi-clipboard-text-multiple-outline:before{content:"\f126c"}.mdi-clipboard-text-off:before{content:"\f1620"}.mdi-clipboard-text-off-outline:before{content:"\f1621"}.mdi-clipboard-text-outline:before{content:"\f0a38"}.mdi-clipboard-text-play:before{content:"\f0c5b"}.mdi-clipboard-text-play-outline:before{content:"\f0c5c"}.mdi-clipboard-text-search:before{content:"\f1622"}.mdi-clipboard-text-search-outline:before{content:"\f1623"}.mdi-clippy:before{content:"\f014f"}.mdi-clock:before{content:"\f0954"}.mdi-clock-alert:before{content:"\f0955"}.mdi-clock-alert-outline:before{content:"\f05ce"}.mdi-clock-check:before{content:"\f0fa8"}.mdi-clock-check-outline:before{content:"\f0fa9"}.mdi-clock-digital:before{content:"\f0e97"}.mdi-clock-edit:before{content:"\f19ba"}.mdi-clock-edit-outline:before{content:"\f19bb"}.mdi-clock-end:before{content:"\f0151"}.mdi-clock-fast:before{content:"\f0152"}.mdi-clock-in:before{content:"\f0153"}.mdi-clock-minus:before{content:"\f1863"}.mdi-clock-minus-outline:before{content:"\f1864"}.mdi-clock-out:before{content:"\f0154"}.mdi-clock-outline:before{content:"\f0150"}.mdi-clock-plus:before{content:"\f1861"}.mdi-clock-plus-outline:before{content:"\f1862"}.mdi-clock-remove:before{content:"\f1865"}.mdi-clock-remove-outline:before{content:"\f1866"}.mdi-clock-star-four-points:before{content:"\f1c29"}.mdi-clock-star-four-points-outline:before{content:"\f1c2a"}.mdi-clock-start:before{content:"\f0155"}.mdi-clock-time-eight:before{content:"\f1446"}.mdi-clock-time-eight-outline:before{content:"\f1452"}.mdi-clock-time-eleven:before{content:"\f1449"}.mdi-clock-time-eleven-outline:before{content:"\f1455"}.mdi-clock-time-five:before{content:"\f1443"}.mdi-clock-time-five-outline:before{content:"\f144f"}.mdi-clock-time-four:before{content:"\f1442"}.mdi-clock-time-four-outline:before{content:"\f144e"}.mdi-clock-time-nine:before{content:"\f1447"}.mdi-clock-time-nine-outline:before{content:"\f1453"}.mdi-clock-time-one:before{content:"\f143f"}.mdi-clock-time-one-outline:before{content:"\f144b"}.mdi-clock-time-seven:before{content:"\f1445"}.mdi-clock-time-seven-outline:before{content:"\f1451"}.mdi-clock-time-six:before{content:"\f1444"}.mdi-clock-time-six-outline:before{content:"\f1450"}.mdi-clock-time-ten:before{content:"\f1448"}.mdi-clock-time-ten-outline:before{content:"\f1454"}.mdi-clock-time-three:before{content:"\f1441"}.mdi-clock-time-three-outline:before{content:"\f144d"}.mdi-clock-time-twelve:before{content:"\f144a"}.mdi-clock-time-twelve-outline:before{content:"\f1456"}.mdi-clock-time-two:before{content:"\f1440"}.mdi-clock-time-two-outline:before{content:"\f144c"}.mdi-close:before{content:"\f0156"}.mdi-close-box:before{content:"\f0157"}.mdi-close-box-multiple:before{content:"\f0c5d"}.mdi-close-box-multiple-outline:before{content:"\f0c5e"}.mdi-close-box-outline:before{content:"\f0158"}.mdi-close-circle:before{content:"\f0159"}.mdi-close-circle-multiple:before{content:"\f062a"}.mdi-close-circle-multiple-outline:before{content:"\f0883"}.mdi-close-circle-outline:before{content:"\f015a"}.mdi-close-network:before{content:"\f015b"}.mdi-close-network-outline:before{content:"\f0c5f"}.mdi-close-octagon:before{content:"\f015c"}.mdi-close-octagon-outline:before{content:"\f015d"}.mdi-close-outline:before{content:"\f06c9"}.mdi-close-thick:before{content:"\f1398"}.mdi-closed-caption:before{content:"\f015e"}.mdi-closed-caption-outline:before{content:"\f0dbd"}.mdi-cloud:before{content:"\f015f"}.mdi-cloud-alert:before{content:"\f09e0"}.mdi-cloud-alert-outline:before{content:"\f1be0"}.mdi-cloud-arrow-down:before{content:"\f1be1"}.mdi-cloud-arrow-down-outline:before{content:"\f1be2"}.mdi-cloud-arrow-left:before{content:"\f1be3"}.mdi-cloud-arrow-left-outline:before{content:"\f1be4"}.mdi-cloud-arrow-right:before{content:"\f1be5"}.mdi-cloud-arrow-right-outline:before{content:"\f1be6"}.mdi-cloud-arrow-up:before{content:"\f1be7"}.mdi-cloud-arrow-up-outline:before{content:"\f1be8"}.mdi-cloud-braces:before{content:"\f07b5"}.mdi-cloud-cancel:before{content:"\f1be9"}.mdi-cloud-cancel-outline:before{content:"\f1bea"}.mdi-cloud-check:before{content:"\f1beb"}.mdi-cloud-check-outline:before{content:"\f1bec"}.mdi-cloud-check-variant:before{content:"\f0160"}.mdi-cloud-check-variant-outline:before{content:"\f12cc"}.mdi-cloud-circle:before{content:"\f0161"}.mdi-cloud-circle-outline:before{content:"\f1bed"}.mdi-cloud-clock:before{content:"\f1bee"}.mdi-cloud-clock-outline:before{content:"\f1bef"}.mdi-cloud-cog:before{content:"\f1bf0"}.mdi-cloud-cog-outline:before{content:"\f1bf1"}.mdi-cloud-download:before{content:"\f0162"}.mdi-cloud-download-outline:before{content:"\f0b7d"}.mdi-cloud-key:before{content:"\f1ca1"}.mdi-cloud-key-outline:before{content:"\f1ca2"}.mdi-cloud-lock:before{content:"\f11f1"}.mdi-cloud-lock-open:before{content:"\f1bf2"}.mdi-cloud-lock-open-outline:before{content:"\f1bf3"}.mdi-cloud-lock-outline:before{content:"\f11f2"}.mdi-cloud-minus:before{content:"\f1bf4"}.mdi-cloud-minus-outline:before{content:"\f1bf5"}.mdi-cloud-off:before{content:"\f1bf6"}.mdi-cloud-off-outline:before{content:"\f0164"}.mdi-cloud-outline:before{content:"\f0163"}.mdi-cloud-percent:before{content:"\f1a35"}.mdi-cloud-percent-outline:before{content:"\f1a36"}.mdi-cloud-plus:before{content:"\f1bf7"}.mdi-cloud-plus-outline:before{content:"\f1bf8"}.mdi-cloud-print:before{content:"\f0165"}.mdi-cloud-print-outline:before{content:"\f0166"}.mdi-cloud-question:before{content:"\f0a39"}.mdi-cloud-question-outline:before{content:"\f1bf9"}.mdi-cloud-refresh:before{content:"\f1bfa"}.mdi-cloud-refresh-outline:before{content:"\f1bfb"}.mdi-cloud-refresh-variant:before{content:"\f052a"}.mdi-cloud-refresh-variant-outline:before{content:"\f1bfc"}.mdi-cloud-remove:before{content:"\f1bfd"}.mdi-cloud-remove-outline:before{content:"\f1bfe"}.mdi-cloud-search:before{content:"\f0956"}.mdi-cloud-search-outline:before{content:"\f0957"}.mdi-cloud-sync:before{content:"\f063f"}.mdi-cloud-sync-outline:before{content:"\f12d6"}.mdi-cloud-tags:before{content:"\f07b6"}.mdi-cloud-upload:before{content:"\f0167"}.mdi-cloud-upload-outline:before{content:"\f0b7e"}.mdi-clouds:before{content:"\f1b95"}.mdi-clover:before{content:"\f0816"}.mdi-clover-outline:before{content:"\f1c62"}.mdi-coach-lamp:before{content:"\f1020"}.mdi-coach-lamp-variant:before{content:"\f1a37"}.mdi-coat-rack:before{content:"\f109e"}.mdi-code-array:before{content:"\f0168"}.mdi-code-block-braces:before{content:"\f1c83"}.mdi-code-block-brackets:before{content:"\f1c84"}.mdi-code-block-parentheses:before{content:"\f1c85"}.mdi-code-block-tags:before{content:"\f1c86"}.mdi-code-braces:before{content:"\f0169"}.mdi-code-braces-box:before{content:"\f10d6"}.mdi-code-brackets:before{content:"\f016a"}.mdi-code-equal:before{content:"\f016b"}.mdi-code-greater-than:before{content:"\f016c"}.mdi-code-greater-than-or-equal:before{content:"\f016d"}.mdi-code-json:before{content:"\f0626"}.mdi-code-less-than:before{content:"\f016e"}.mdi-code-less-than-or-equal:before{content:"\f016f"}.mdi-code-not-equal:before{content:"\f0170"}.mdi-code-not-equal-variant:before{content:"\f0171"}.mdi-code-parentheses:before{content:"\f0172"}.mdi-code-parentheses-box:before{content:"\f10d7"}.mdi-code-string:before{content:"\f0173"}.mdi-code-tags:before{content:"\f0174"}.mdi-code-tags-check:before{content:"\f0694"}.mdi-codepen:before{content:"\f0175"}.mdi-coffee:before{content:"\f0176"}.mdi-coffee-maker:before{content:"\f109f"}.mdi-coffee-maker-check:before{content:"\f1931"}.mdi-coffee-maker-check-outline:before{content:"\f1932"}.mdi-coffee-maker-outline:before{content:"\f181b"}.mdi-coffee-off:before{content:"\f0faa"}.mdi-coffee-off-outline:before{content:"\f0fab"}.mdi-coffee-outline:before{content:"\f06ca"}.mdi-coffee-to-go:before{content:"\f0177"}.mdi-coffee-to-go-outline:before{content:"\f130e"}.mdi-coffin:before{content:"\f0b7f"}.mdi-cog:before{content:"\f0493"}.mdi-cog-box:before{content:"\f0494"}.mdi-cog-clockwise:before{content:"\f11dd"}.mdi-cog-counterclockwise:before{content:"\f11de"}.mdi-cog-off:before{content:"\f13ce"}.mdi-cog-off-outline:before{content:"\f13cf"}.mdi-cog-outline:before{content:"\f08bb"}.mdi-cog-pause:before{content:"\f1933"}.mdi-cog-pause-outline:before{content:"\f1934"}.mdi-cog-play:before{content:"\f1935"}.mdi-cog-play-outline:before{content:"\f1936"}.mdi-cog-refresh:before{content:"\f145e"}.mdi-cog-refresh-outline:before{content:"\f145f"}.mdi-cog-stop:before{content:"\f1937"}.mdi-cog-stop-outline:before{content:"\f1938"}.mdi-cog-sync:before{content:"\f1460"}.mdi-cog-sync-outline:before{content:"\f1461"}.mdi-cog-transfer:before{content:"\f105b"}.mdi-cog-transfer-outline:before{content:"\f105c"}.mdi-cogs:before{content:"\f08d6"}.mdi-collage:before{content:"\f0640"}.mdi-collapse-all:before{content:"\f0aa6"}.mdi-collapse-all-outline:before{content:"\f0aa7"}.mdi-color-helper:before{content:"\f0179"}.mdi-comma:before{content:"\f0e23"}.mdi-comma-box:before{content:"\f0e2b"}.mdi-comma-box-outline:before{content:"\f0e24"}.mdi-comma-circle:before{content:"\f0e25"}.mdi-comma-circle-outline:before{content:"\f0e26"}.mdi-comment:before{content:"\f017a"}.mdi-comment-account:before{content:"\f017b"}.mdi-comment-account-outline:before{content:"\f017c"}.mdi-comment-alert:before{content:"\f017d"}.mdi-comment-alert-outline:before{content:"\f017e"}.mdi-comment-arrow-left:before{content:"\f09e1"}.mdi-comment-arrow-left-outline:before{content:"\f09e2"}.mdi-comment-arrow-right:before{content:"\f09e3"}.mdi-comment-arrow-right-outline:before{content:"\f09e4"}.mdi-comment-bookmark:before{content:"\f15ae"}.mdi-comment-bookmark-outline:before{content:"\f15af"}.mdi-comment-check:before{content:"\f017f"}.mdi-comment-check-outline:before{content:"\f0180"}.mdi-comment-edit:before{content:"\f11bf"}.mdi-comment-edit-outline:before{content:"\f12c4"}.mdi-comment-eye:before{content:"\f0a3a"}.mdi-comment-eye-outline:before{content:"\f0a3b"}.mdi-comment-flash:before{content:"\f15b0"}.mdi-comment-flash-outline:before{content:"\f15b1"}.mdi-comment-minus:before{content:"\f15df"}.mdi-comment-minus-outline:before{content:"\f15e0"}.mdi-comment-multiple:before{content:"\f085f"}.mdi-comment-multiple-outline:before{content:"\f0181"}.mdi-comment-off:before{content:"\f15e1"}.mdi-comment-off-outline:before{content:"\f15e2"}.mdi-comment-outline:before{content:"\f0182"}.mdi-comment-plus:before{content:"\f09e5"}.mdi-comment-plus-outline:before{content:"\f0183"}.mdi-comment-processing:before{content:"\f0184"}.mdi-comment-processing-outline:before{content:"\f0185"}.mdi-comment-question:before{content:"\f0817"}.mdi-comment-question-outline:before{content:"\f0186"}.mdi-comment-quote:before{content:"\f1021"}.mdi-comment-quote-outline:before{content:"\f1022"}.mdi-comment-remove:before{content:"\f05de"}.mdi-comment-remove-outline:before{content:"\f0187"}.mdi-comment-search:before{content:"\f0a3c"}.mdi-comment-search-outline:before{content:"\f0a3d"}.mdi-comment-text:before{content:"\f0188"}.mdi-comment-text-multiple:before{content:"\f0860"}.mdi-comment-text-multiple-outline:before{content:"\f0861"}.mdi-comment-text-outline:before{content:"\f0189"}.mdi-compare:before{content:"\f018a"}.mdi-compare-horizontal:before{content:"\f1492"}.mdi-compare-remove:before{content:"\f18b3"}.mdi-compare-vertical:before{content:"\f1493"}.mdi-compass:before{content:"\f018b"}.mdi-compass-off:before{content:"\f0b80"}.mdi-compass-off-outline:before{content:"\f0b81"}.mdi-compass-outline:before{content:"\f018c"}.mdi-compass-rose:before{content:"\f1382"}.mdi-compost:before{content:"\f1a38"}.mdi-cone:before{content:"\f194c"}.mdi-cone-off:before{content:"\f194d"}.mdi-connection:before{content:"\f1616"}.mdi-console:before{content:"\f018d"}.mdi-console-line:before{content:"\f07b7"}.mdi-console-network:before{content:"\f08a9"}.mdi-console-network-outline:before{content:"\f0c60"}.mdi-consolidate:before{content:"\f10d8"}.mdi-contactless-payment:before{content:"\f0d6a"}.mdi-contactless-payment-circle:before{content:"\f0321"}.mdi-contactless-payment-circle-outline:before{content:"\f0408"}.mdi-contacts:before{content:"\f06cb"}.mdi-contacts-outline:before{content:"\f05b8"}.mdi-contain:before{content:"\f0a3e"}.mdi-contain-end:before{content:"\f0a3f"}.mdi-contain-start:before{content:"\f0a40"}.mdi-content-copy:before{content:"\f018f"}.mdi-content-cut:before{content:"\f0190"}.mdi-content-duplicate:before{content:"\f0191"}.mdi-content-paste:before{content:"\f0192"}.mdi-content-save:before{content:"\f0193"}.mdi-content-save-alert:before{content:"\f0f42"}.mdi-content-save-alert-outline:before{content:"\f0f43"}.mdi-content-save-all:before{content:"\f0194"}.mdi-content-save-all-outline:before{content:"\f0f44"}.mdi-content-save-check:before{content:"\f18ea"}.mdi-content-save-check-outline:before{content:"\f18eb"}.mdi-content-save-cog:before{content:"\f145b"}.mdi-content-save-cog-outline:before{content:"\f145c"}.mdi-content-save-edit:before{content:"\f0cfb"}.mdi-content-save-edit-outline:before{content:"\f0cfc"}.mdi-content-save-minus:before{content:"\f1b43"}.mdi-content-save-minus-outline:before{content:"\f1b44"}.mdi-content-save-move:before{content:"\f0e27"}.mdi-content-save-move-outline:before{content:"\f0e28"}.mdi-content-save-off:before{content:"\f1643"}.mdi-content-save-off-outline:before{content:"\f1644"}.mdi-content-save-outline:before{content:"\f0818"}.mdi-content-save-plus:before{content:"\f1b41"}.mdi-content-save-plus-outline:before{content:"\f1b42"}.mdi-content-save-settings:before{content:"\f061b"}.mdi-content-save-settings-outline:before{content:"\f0b2e"}.mdi-contrast:before{content:"\f0195"}.mdi-contrast-box:before{content:"\f0196"}.mdi-contrast-circle:before{content:"\f0197"}.mdi-controller:before{content:"\f02b4"}.mdi-controller-classic:before{content:"\f0b82"}.mdi-controller-classic-outline:before{content:"\f0b83"}.mdi-controller-off:before{content:"\f02b5"}.mdi-cookie:before{content:"\f0198"}.mdi-cookie-alert:before{content:"\f16d0"}.mdi-cookie-alert-outline:before{content:"\f16d1"}.mdi-cookie-check:before{content:"\f16d2"}.mdi-cookie-check-outline:before{content:"\f16d3"}.mdi-cookie-clock:before{content:"\f16e4"}.mdi-cookie-clock-outline:before{content:"\f16e5"}.mdi-cookie-cog:before{content:"\f16d4"}.mdi-cookie-cog-outline:before{content:"\f16d5"}.mdi-cookie-edit:before{content:"\f16e6"}.mdi-cookie-edit-outline:before{content:"\f16e7"}.mdi-cookie-lock:before{content:"\f16e8"}.mdi-cookie-lock-outline:before{content:"\f16e9"}.mdi-cookie-minus:before{content:"\f16da"}.mdi-cookie-minus-outline:before{content:"\f16db"}.mdi-cookie-off:before{content:"\f16ea"}.mdi-cookie-off-outline:before{content:"\f16eb"}.mdi-cookie-outline:before{content:"\f16de"}.mdi-cookie-plus:before{content:"\f16d6"}.mdi-cookie-plus-outline:before{content:"\f16d7"}.mdi-cookie-refresh:before{content:"\f16ec"}.mdi-cookie-refresh-outline:before{content:"\f16ed"}.mdi-cookie-remove:before{content:"\f16d8"}.mdi-cookie-remove-outline:before{content:"\f16d9"}.mdi-cookie-settings:before{content:"\f16dc"}.mdi-cookie-settings-outline:before{content:"\f16dd"}.mdi-coolant-temperature:before{content:"\f03c8"}.mdi-copyleft:before{content:"\f1939"}.mdi-copyright:before{content:"\f05e6"}.mdi-cordova:before{content:"\f0958"}.mdi-corn:before{content:"\f07b8"}.mdi-corn-off:before{content:"\f13ef"}.mdi-cosine-wave:before{content:"\f1479"}.mdi-counter:before{content:"\f0199"}.mdi-countertop:before{content:"\f181c"}.mdi-countertop-outline:before{content:"\f181d"}.mdi-cow:before{content:"\f019a"}.mdi-cow-off:before{content:"\f18fc"}.mdi-cpu-32-bit:before{content:"\f0edf"}.mdi-cpu-64-bit:before{content:"\f0ee0"}.mdi-cradle:before{content:"\f198b"}.mdi-cradle-outline:before{content:"\f1991"}.mdi-crane:before{content:"\f0862"}.mdi-creation:before{content:"\f0674"}.mdi-creation-outline:before{content:"\f1c2b"}.mdi-creative-commons:before{content:"\f0d6b"}.mdi-credit-card:before{content:"\f0fef"}.mdi-credit-card-check:before{content:"\f13d0"}.mdi-credit-card-check-outline:before{content:"\f13d1"}.mdi-credit-card-chip:before{content:"\f190f"}.mdi-credit-card-chip-outline:before{content:"\f1910"}.mdi-credit-card-clock:before{content:"\f0ee1"}.mdi-credit-card-clock-outline:before{content:"\f0ee2"}.mdi-credit-card-edit:before{content:"\f17d7"}.mdi-credit-card-edit-outline:before{content:"\f17d8"}.mdi-credit-card-fast:before{content:"\f1911"}.mdi-credit-card-fast-outline:before{content:"\f1912"}.mdi-credit-card-lock:before{content:"\f18e7"}.mdi-credit-card-lock-outline:before{content:"\f18e8"}.mdi-credit-card-marker:before{content:"\f06a8"}.mdi-credit-card-marker-outline:before{content:"\f0dbe"}.mdi-credit-card-minus:before{content:"\f0fac"}.mdi-credit-card-minus-outline:before{content:"\f0fad"}.mdi-credit-card-multiple:before{content:"\f0ff0"}.mdi-credit-card-multiple-outline:before{content:"\f019c"}.mdi-credit-card-off:before{content:"\f0ff1"}.mdi-credit-card-off-outline:before{content:"\f05e4"}.mdi-credit-card-outline:before{content:"\f019b"}.mdi-credit-card-plus:before{content:"\f0ff2"}.mdi-credit-card-plus-outline:before{content:"\f0676"}.mdi-credit-card-refresh:before{content:"\f1645"}.mdi-credit-card-refresh-outline:before{content:"\f1646"}.mdi-credit-card-refund:before{content:"\f0ff3"}.mdi-credit-card-refund-outline:before{content:"\f0aa8"}.mdi-credit-card-remove:before{content:"\f0fae"}.mdi-credit-card-remove-outline:before{content:"\f0faf"}.mdi-credit-card-scan:before{content:"\f0ff4"}.mdi-credit-card-scan-outline:before{content:"\f019d"}.mdi-credit-card-search:before{content:"\f1647"}.mdi-credit-card-search-outline:before{content:"\f1648"}.mdi-credit-card-settings:before{content:"\f0ff5"}.mdi-credit-card-settings-outline:before{content:"\f08d7"}.mdi-credit-card-sync:before{content:"\f1649"}.mdi-credit-card-sync-outline:before{content:"\f164a"}.mdi-credit-card-wireless:before{content:"\f0802"}.mdi-credit-card-wireless-off:before{content:"\f057a"}.mdi-credit-card-wireless-off-outline:before{content:"\f057b"}.mdi-credit-card-wireless-outline:before{content:"\f0d6c"}.mdi-cricket:before{content:"\f0d6d"}.mdi-crop:before{content:"\f019e"}.mdi-crop-free:before{content:"\f019f"}.mdi-crop-landscape:before{content:"\f01a0"}.mdi-crop-portrait:before{content:"\f01a1"}.mdi-crop-rotate:before{content:"\f0696"}.mdi-crop-square:before{content:"\f01a2"}.mdi-cross:before{content:"\f0953"}.mdi-cross-bolnisi:before{content:"\f0ced"}.mdi-cross-celtic:before{content:"\f0cf5"}.mdi-cross-outline:before{content:"\f0cf6"}.mdi-crosshairs:before{content:"\f01a3"}.mdi-crosshairs-gps:before{content:"\f01a4"}.mdi-crosshairs-off:before{content:"\f0f45"}.mdi-crosshairs-question:before{content:"\f1136"}.mdi-crowd:before{content:"\f1975"}.mdi-crown:before{content:"\f01a5"}.mdi-crown-circle:before{content:"\f17dc"}.mdi-crown-circle-outline:before{content:"\f17dd"}.mdi-crown-outline:before{content:"\f11d0"}.mdi-cryengine:before{content:"\f0959"}.mdi-crystal-ball:before{content:"\f0b2f"}.mdi-cube:before{content:"\f01a6"}.mdi-cube-off:before{content:"\f141c"}.mdi-cube-off-outline:before{content:"\f141d"}.mdi-cube-outline:before{content:"\f01a7"}.mdi-cube-scan:before{content:"\f0b84"}.mdi-cube-send:before{content:"\f01a8"}.mdi-cube-unfolded:before{content:"\f01a9"}.mdi-cup:before{content:"\f01aa"}.mdi-cup-off:before{content:"\f05e5"}.mdi-cup-off-outline:before{content:"\f137d"}.mdi-cup-outline:before{content:"\f130f"}.mdi-cup-water:before{content:"\f01ab"}.mdi-cupboard:before{content:"\f0f46"}.mdi-cupboard-outline:before{content:"\f0f47"}.mdi-cupcake:before{content:"\f095a"}.mdi-curling:before{content:"\f0863"}.mdi-currency-bdt:before{content:"\f0864"}.mdi-currency-brl:before{content:"\f0b85"}.mdi-currency-btc:before{content:"\f01ac"}.mdi-currency-cny:before{content:"\f07ba"}.mdi-currency-eth:before{content:"\f07bb"}.mdi-currency-eur:before{content:"\f01ad"}.mdi-currency-eur-off:before{content:"\f1315"}.mdi-currency-fra:before{content:"\f1a39"}.mdi-currency-gbp:before{content:"\f01ae"}.mdi-currency-ils:before{content:"\f0c61"}.mdi-currency-inr:before{content:"\f01af"}.mdi-currency-jpy:before{content:"\f07bc"}.mdi-currency-krw:before{content:"\f07bd"}.mdi-currency-kzt:before{content:"\f0865"}.mdi-currency-mnt:before{content:"\f1512"}.mdi-currency-ngn:before{content:"\f01b0"}.mdi-currency-php:before{content:"\f09e6"}.mdi-currency-rial:before{content:"\f0e9c"}.mdi-currency-rub:before{content:"\f01b1"}.mdi-currency-rupee:before{content:"\f1976"}.mdi-currency-sign:before{content:"\f07be"}.mdi-currency-thb:before{content:"\f1c05"}.mdi-currency-try:before{content:"\f01b2"}.mdi-currency-twd:before{content:"\f07bf"}.mdi-currency-uah:before{content:"\f1b9b"}.mdi-currency-usd:before{content:"\f01c1"}.mdi-currency-usd-off:before{content:"\f067a"}.mdi-current-ac:before{content:"\f1480"}.mdi-current-dc:before{content:"\f095c"}.mdi-cursor-default:before{content:"\f01c0"}.mdi-cursor-default-click:before{content:"\f0cfd"}.mdi-cursor-default-click-outline:before{content:"\f0cfe"}.mdi-cursor-default-gesture:before{content:"\f1127"}.mdi-cursor-default-gesture-outline:before{content:"\f1128"}.mdi-cursor-default-outline:before{content:"\f01bf"}.mdi-cursor-move:before{content:"\f01be"}.mdi-cursor-pointer:before{content:"\f01bd"}.mdi-cursor-text:before{content:"\f05e7"}.mdi-curtains:before{content:"\f1846"}.mdi-curtains-closed:before{content:"\f1847"}.mdi-cylinder:before{content:"\f194e"}.mdi-cylinder-off:before{content:"\f194f"}.mdi-dance-ballroom:before{content:"\f15fb"}.mdi-dance-pole:before{content:"\f1578"}.mdi-data-matrix:before{content:"\f153c"}.mdi-data-matrix-edit:before{content:"\f153d"}.mdi-data-matrix-minus:before{content:"\f153e"}.mdi-data-matrix-plus:before{content:"\f153f"}.mdi-data-matrix-remove:before{content:"\f1540"}.mdi-data-matrix-scan:before{content:"\f1541"}.mdi-database:before{content:"\f01bc"}.mdi-database-alert:before{content:"\f163a"}.mdi-database-alert-outline:before{content:"\f1624"}.mdi-database-arrow-down:before{content:"\f163b"}.mdi-database-arrow-down-outline:before{content:"\f1625"}.mdi-database-arrow-left:before{content:"\f163c"}.mdi-database-arrow-left-outline:before{content:"\f1626"}.mdi-database-arrow-right:before{content:"\f163d"}.mdi-database-arrow-right-outline:before{content:"\f1627"}.mdi-database-arrow-up:before{content:"\f163e"}.mdi-database-arrow-up-outline:before{content:"\f1628"}.mdi-database-check:before{content:"\f0aa9"}.mdi-database-check-outline:before{content:"\f1629"}.mdi-database-clock:before{content:"\f163f"}.mdi-database-clock-outline:before{content:"\f162a"}.mdi-database-cog:before{content:"\f164b"}.mdi-database-cog-outline:before{content:"\f164c"}.mdi-database-edit:before{content:"\f0b86"}.mdi-database-edit-outline:before{content:"\f162b"}.mdi-database-export:before{content:"\f095e"}.mdi-database-export-outline:before{content:"\f162c"}.mdi-database-eye:before{content:"\f191f"}.mdi-database-eye-off:before{content:"\f1920"}.mdi-database-eye-off-outline:before{content:"\f1921"}.mdi-database-eye-outline:before{content:"\f1922"}.mdi-database-import:before{content:"\f095d"}.mdi-database-import-outline:before{content:"\f162d"}.mdi-database-lock:before{content:"\f0aaa"}.mdi-database-lock-outline:before{content:"\f162e"}.mdi-database-marker:before{content:"\f12f6"}.mdi-database-marker-outline:before{content:"\f162f"}.mdi-database-minus:before{content:"\f01bb"}.mdi-database-minus-outline:before{content:"\f1630"}.mdi-database-off:before{content:"\f1640"}.mdi-database-off-outline:before{content:"\f1631"}.mdi-database-outline:before{content:"\f1632"}.mdi-database-plus:before{content:"\f01ba"}.mdi-database-plus-outline:before{content:"\f1633"}.mdi-database-refresh:before{content:"\f05c2"}.mdi-database-refresh-outline:before{content:"\f1634"}.mdi-database-remove:before{content:"\f0d00"}.mdi-database-remove-outline:before{content:"\f1635"}.mdi-database-search:before{content:"\f0866"}.mdi-database-search-outline:before{content:"\f1636"}.mdi-database-settings:before{content:"\f0d01"}.mdi-database-settings-outline:before{content:"\f1637"}.mdi-database-sync:before{content:"\f0cff"}.mdi-database-sync-outline:before{content:"\f1638"}.mdi-death-star:before{content:"\f08d8"}.mdi-death-star-variant:before{content:"\f08d9"}.mdi-deathly-hallows:before{content:"\f0b87"}.mdi-debian:before{content:"\f08da"}.mdi-debug-step-into:before{content:"\f01b9"}.mdi-debug-step-out:before{content:"\f01b8"}.mdi-debug-step-over:before{content:"\f01b7"}.mdi-decagram:before{content:"\f076c"}.mdi-decagram-outline:before{content:"\f076d"}.mdi-decimal:before{content:"\f10a1"}.mdi-decimal-comma:before{content:"\f10a2"}.mdi-decimal-comma-decrease:before{content:"\f10a3"}.mdi-decimal-comma-increase:before{content:"\f10a4"}.mdi-decimal-decrease:before{content:"\f01b6"}.mdi-decimal-increase:before{content:"\f01b5"}.mdi-delete:before{content:"\f01b4"}.mdi-delete-alert:before{content:"\f10a5"}.mdi-delete-alert-outline:before{content:"\f10a6"}.mdi-delete-circle:before{content:"\f0683"}.mdi-delete-circle-outline:before{content:"\f0b88"}.mdi-delete-clock:before{content:"\f1556"}.mdi-delete-clock-outline:before{content:"\f1557"}.mdi-delete-empty:before{content:"\f06cc"}.mdi-delete-empty-outline:before{content:"\f0e9d"}.mdi-delete-forever:before{content:"\f05e8"}.mdi-delete-forever-outline:before{content:"\f0b89"}.mdi-delete-off:before{content:"\f10a7"}.mdi-delete-off-outline:before{content:"\f10a8"}.mdi-delete-outline:before{content:"\f09e7"}.mdi-delete-restore:before{content:"\f0819"}.mdi-delete-sweep:before{content:"\f05e9"}.mdi-delete-sweep-outline:before{content:"\f0c62"}.mdi-delete-variant:before{content:"\f01b3"}.mdi-delta:before{content:"\f01c2"}.mdi-desk:before{content:"\f1239"}.mdi-desk-lamp:before{content:"\f095f"}.mdi-desk-lamp-off:before{content:"\f1b1f"}.mdi-desk-lamp-on:before{content:"\f1b20"}.mdi-deskphone:before{content:"\f01c3"}.mdi-desktop-classic:before{content:"\f07c0"}.mdi-desktop-tower:before{content:"\f01c5"}.mdi-desktop-tower-monitor:before{content:"\f0aab"}.mdi-details:before{content:"\f01c6"}.mdi-dev-to:before{content:"\f0d6e"}.mdi-developer-board:before{content:"\f0697"}.mdi-deviantart:before{content:"\f01c7"}.mdi-devices:before{content:"\f0fb0"}.mdi-dharmachakra:before{content:"\f094b"}.mdi-diabetes:before{content:"\f1126"}.mdi-dialpad:before{content:"\f061c"}.mdi-diameter:before{content:"\f0c63"}.mdi-diameter-outline:before{content:"\f0c64"}.mdi-diameter-variant:before{content:"\f0c65"}.mdi-diamond:before{content:"\f0b8a"}.mdi-diamond-outline:before{content:"\f0b8b"}.mdi-diamond-stone:before{content:"\f01c8"}.mdi-diaper-outline:before{content:"\f1ccf"}.mdi-dice-1:before{content:"\f01ca"}.mdi-dice-1-outline:before{content:"\f114a"}.mdi-dice-2:before{content:"\f01cb"}.mdi-dice-2-outline:before{content:"\f114b"}.mdi-dice-3:before{content:"\f01cc"}.mdi-dice-3-outline:before{content:"\f114c"}.mdi-dice-4:before{content:"\f01cd"}.mdi-dice-4-outline:before{content:"\f114d"}.mdi-dice-5:before{content:"\f01ce"}.mdi-dice-5-outline:before{content:"\f114e"}.mdi-dice-6:before{content:"\f01cf"}.mdi-dice-6-outline:before{content:"\f114f"}.mdi-dice-d10:before{content:"\f1153"}.mdi-dice-d10-outline:before{content:"\f076f"}.mdi-dice-d12:before{content:"\f1154"}.mdi-dice-d12-outline:before{content:"\f0867"}.mdi-dice-d20:before{content:"\f1155"}.mdi-dice-d20-outline:before{content:"\f05ea"}.mdi-dice-d4:before{content:"\f1150"}.mdi-dice-d4-outline:before{content:"\f05eb"}.mdi-dice-d6:before{content:"\f1151"}.mdi-dice-d6-outline:before{content:"\f05ed"}.mdi-dice-d8:before{content:"\f1152"}.mdi-dice-d8-outline:before{content:"\f05ec"}.mdi-dice-multiple:before{content:"\f076e"}.mdi-dice-multiple-outline:before{content:"\f1156"}.mdi-digital-ocean:before{content:"\f1237"}.mdi-dip-switch:before{content:"\f07c1"}.mdi-directions:before{content:"\f01d0"}.mdi-directions-fork:before{content:"\f0641"}.mdi-disc:before{content:"\f05ee"}.mdi-disc-alert:before{content:"\f01d1"}.mdi-disc-player:before{content:"\f0960"}.mdi-dishwasher:before{content:"\f0aac"}.mdi-dishwasher-alert:before{content:"\f11b8"}.mdi-dishwasher-off:before{content:"\f11b9"}.mdi-disqus:before{content:"\f01d2"}.mdi-distribute-horizontal-center:before{content:"\f11c9"}.mdi-distribute-horizontal-left:before{content:"\f11c8"}.mdi-distribute-horizontal-right:before{content:"\f11ca"}.mdi-distribute-vertical-bottom:before{content:"\f11cb"}.mdi-distribute-vertical-center:before{content:"\f11cc"}.mdi-distribute-vertical-top:before{content:"\f11cd"}.mdi-diversify:before{content:"\f1877"}.mdi-diving:before{content:"\f1977"}.mdi-diving-flippers:before{content:"\f0dbf"}.mdi-diving-helmet:before{content:"\f0dc0"}.mdi-diving-scuba:before{content:"\f1b77"}.mdi-diving-scuba-flag:before{content:"\f0dc2"}.mdi-diving-scuba-mask:before{content:"\f0dc1"}.mdi-diving-scuba-tank:before{content:"\f0dc3"}.mdi-diving-scuba-tank-multiple:before{content:"\f0dc4"}.mdi-diving-snorkel:before{content:"\f0dc5"}.mdi-division:before{content:"\f01d4"}.mdi-division-box:before{content:"\f01d5"}.mdi-dlna:before{content:"\f0a41"}.mdi-dna:before{content:"\f0684"}.mdi-dns:before{content:"\f01d6"}.mdi-dns-outline:before{content:"\f0b8c"}.mdi-dock-bottom:before{content:"\f10a9"}.mdi-dock-left:before{content:"\f10aa"}.mdi-dock-right:before{content:"\f10ab"}.mdi-dock-top:before{content:"\f1513"}.mdi-dock-window:before{content:"\f10ac"}.mdi-docker:before{content:"\f0868"}.mdi-doctor:before{content:"\f0a42"}.mdi-dog:before{content:"\f0a43"}.mdi-dog-service:before{content:"\f0aad"}.mdi-dog-side:before{content:"\f0a44"}.mdi-dog-side-off:before{content:"\f16ee"}.mdi-dolby:before{content:"\f06b3"}.mdi-dolly:before{content:"\f0e9e"}.mdi-dolphin:before{content:"\f18b4"}.mdi-domain:before{content:"\f01d7"}.mdi-domain-off:before{content:"\f0d6f"}.mdi-domain-plus:before{content:"\f10ad"}.mdi-domain-remove:before{content:"\f10ae"}.mdi-domain-switch:before{content:"\f1c2c"}.mdi-dome-light:before{content:"\f141e"}.mdi-domino-mask:before{content:"\f1023"}.mdi-donkey:before{content:"\f07c2"}.mdi-door:before{content:"\f081a"}.mdi-door-closed:before{content:"\f081b"}.mdi-door-closed-cancel:before{content:"\f1c93"}.mdi-door-closed-lock:before{content:"\f10af"}.mdi-door-open:before{content:"\f081c"}.mdi-door-sliding:before{content:"\f181e"}.mdi-door-sliding-lock:before{content:"\f181f"}.mdi-door-sliding-open:before{content:"\f1820"}.mdi-doorbell:before{content:"\f12e6"}.mdi-doorbell-video:before{content:"\f0869"}.mdi-dot-net:before{content:"\f0aae"}.mdi-dots-circle:before{content:"\f1978"}.mdi-dots-grid:before{content:"\f15fc"}.mdi-dots-hexagon:before{content:"\f15ff"}.mdi-dots-horizontal:before{content:"\f01d8"}.mdi-dots-horizontal-circle:before{content:"\f07c3"}.mdi-dots-horizontal-circle-outline:before{content:"\f0b8d"}.mdi-dots-square:before{content:"\f15fd"}.mdi-dots-triangle:before{content:"\f15fe"}.mdi-dots-vertical:before{content:"\f01d9"}.mdi-dots-vertical-circle:before{content:"\f07c4"}.mdi-dots-vertical-circle-outline:before{content:"\f0b8e"}.mdi-download:before{content:"\f01da"}.mdi-download-box:before{content:"\f1462"}.mdi-download-box-outline:before{content:"\f1463"}.mdi-download-circle:before{content:"\f1464"}.mdi-download-circle-outline:before{content:"\f1465"}.mdi-download-lock:before{content:"\f1320"}.mdi-download-lock-outline:before{content:"\f1321"}.mdi-download-multiple:before{content:"\f09e9"}.mdi-download-multiple-outline:before{content:"\f1cd0"}.mdi-download-network:before{content:"\f06f4"}.mdi-download-network-outline:before{content:"\f0c66"}.mdi-download-off:before{content:"\f10b0"}.mdi-download-off-outline:before{content:"\f10b1"}.mdi-download-outline:before{content:"\f0b8f"}.mdi-drag:before{content:"\f01db"}.mdi-drag-horizontal:before{content:"\f01dc"}.mdi-drag-horizontal-variant:before{content:"\f12f0"}.mdi-drag-variant:before{content:"\f0b90"}.mdi-drag-vertical:before{content:"\f01dd"}.mdi-drag-vertical-variant:before{content:"\f12f1"}.mdi-drama-masks:before{content:"\f0d02"}.mdi-draw:before{content:"\f0f49"}.mdi-draw-pen:before{content:"\f19b9"}.mdi-drawing:before{content:"\f01de"}.mdi-drawing-box:before{content:"\f01df"}.mdi-dresser:before{content:"\f0f4a"}.mdi-dresser-outline:before{content:"\f0f4b"}.mdi-drone:before{content:"\f01e2"}.mdi-dropbox:before{content:"\f01e3"}.mdi-drupal:before{content:"\f01e4"}.mdi-duck:before{content:"\f01e5"}.mdi-dumbbell:before{content:"\f01e6"}.mdi-dump-truck:before{content:"\f0c67"}.mdi-ear-hearing:before{content:"\f07c5"}.mdi-ear-hearing-loop:before{content:"\f1aee"}.mdi-ear-hearing-off:before{content:"\f0a45"}.mdi-earbuds:before{content:"\f184f"}.mdi-earbuds-off:before{content:"\f1850"}.mdi-earbuds-off-outline:before{content:"\f1851"}.mdi-earbuds-outline:before{content:"\f1852"}.mdi-earth:before{content:"\f01e7"}.mdi-earth-arrow-down:before{content:"\f1c87"}.mdi-earth-arrow-left:before{content:"\f1c88"}.mdi-earth-arrow-right:before{content:"\f1311"}.mdi-earth-arrow-up:before{content:"\f1c89"}.mdi-earth-box:before{content:"\f06cd"}.mdi-earth-box-minus:before{content:"\f1407"}.mdi-earth-box-off:before{content:"\f06ce"}.mdi-earth-box-plus:before{content:"\f1406"}.mdi-earth-box-remove:before{content:"\f1408"}.mdi-earth-minus:before{content:"\f1404"}.mdi-earth-off:before{content:"\f01e8"}.mdi-earth-plus:before{content:"\f1403"}.mdi-earth-remove:before{content:"\f1405"}.mdi-egg:before{content:"\f0aaf"}.mdi-egg-easter:before{content:"\f0ab0"}.mdi-egg-fried:before{content:"\f184a"}.mdi-egg-off:before{content:"\f13f0"}.mdi-egg-off-outline:before{content:"\f13f1"}.mdi-egg-outline:before{content:"\f13f2"}.mdi-eiffel-tower:before{content:"\f156b"}.mdi-eight-track:before{content:"\f09ea"}.mdi-eject:before{content:"\f01ea"}.mdi-eject-circle:before{content:"\f1b23"}.mdi-eject-circle-outline:before{content:"\f1b24"}.mdi-eject-outline:before{content:"\f0b91"}.mdi-electric-switch:before{content:"\f0e9f"}.mdi-electric-switch-closed:before{content:"\f10d9"}.mdi-electron-framework:before{content:"\f1024"}.mdi-elephant:before{content:"\f07c6"}.mdi-elevation-decline:before{content:"\f01eb"}.mdi-elevation-rise:before{content:"\f01ec"}.mdi-elevator:before{content:"\f01ed"}.mdi-elevator-down:before{content:"\f12c2"}.mdi-elevator-passenger:before{content:"\f1381"}.mdi-elevator-passenger-off:before{content:"\f1979"}.mdi-elevator-passenger-off-outline:before{content:"\f197a"}.mdi-elevator-passenger-outline:before{content:"\f197b"}.mdi-elevator-up:before{content:"\f12c1"}.mdi-ellipse:before{content:"\f0ea0"}.mdi-ellipse-outline:before{content:"\f0ea1"}.mdi-email:before{content:"\f01ee"}.mdi-email-alert:before{content:"\f06cf"}.mdi-email-alert-outline:before{content:"\f0d42"}.mdi-email-arrow-left:before{content:"\f10da"}.mdi-email-arrow-left-outline:before{content:"\f10db"}.mdi-email-arrow-right:before{content:"\f10dc"}.mdi-email-arrow-right-outline:before{content:"\f10dd"}.mdi-email-box:before{content:"\f0d03"}.mdi-email-check:before{content:"\f0ab1"}.mdi-email-check-outline:before{content:"\f0ab2"}.mdi-email-edit:before{content:"\f0ee3"}.mdi-email-edit-outline:before{content:"\f0ee4"}.mdi-email-fast:before{content:"\f186f"}.mdi-email-fast-outline:before{content:"\f1870"}.mdi-email-heart-outline:before{content:"\f1c5b"}.mdi-email-lock:before{content:"\f01f1"}.mdi-email-lock-outline:before{content:"\f1b61"}.mdi-email-mark-as-unread:before{content:"\f0b92"}.mdi-email-minus:before{content:"\f0ee5"}.mdi-email-minus-outline:before{content:"\f0ee6"}.mdi-email-multiple:before{content:"\f0ee7"}.mdi-email-multiple-outline:before{content:"\f0ee8"}.mdi-email-newsletter:before{content:"\f0fb1"}.mdi-email-off:before{content:"\f13e3"}.mdi-email-off-outline:before{content:"\f13e4"}.mdi-email-open:before{content:"\f01ef"}.mdi-email-open-heart-outline:before{content:"\f1c5c"}.mdi-email-open-multiple:before{content:"\f0ee9"}.mdi-email-open-multiple-outline:before{content:"\f0eea"}.mdi-email-open-outline:before{content:"\f05ef"}.mdi-email-outline:before{content:"\f01f0"}.mdi-email-plus:before{content:"\f09eb"}.mdi-email-plus-outline:before{content:"\f09ec"}.mdi-email-remove:before{content:"\f1661"}.mdi-email-remove-outline:before{content:"\f1662"}.mdi-email-seal:before{content:"\f195b"}.mdi-email-seal-outline:before{content:"\f195c"}.mdi-email-search:before{content:"\f0961"}.mdi-email-search-outline:before{content:"\f0962"}.mdi-email-sync:before{content:"\f12c7"}.mdi-email-sync-outline:before{content:"\f12c8"}.mdi-email-variant:before{content:"\f05f0"}.mdi-ember:before{content:"\f0b30"}.mdi-emby:before{content:"\f06b4"}.mdi-emoticon:before{content:"\f0c68"}.mdi-emoticon-angry:before{content:"\f0c69"}.mdi-emoticon-angry-outline:before{content:"\f0c6a"}.mdi-emoticon-confused:before{content:"\f10de"}.mdi-emoticon-confused-outline:before{content:"\f10df"}.mdi-emoticon-cool:before{content:"\f0c6b"}.mdi-emoticon-cool-outline:before{content:"\f01f3"}.mdi-emoticon-cry:before{content:"\f0c6c"}.mdi-emoticon-cry-outline:before{content:"\f0c6d"}.mdi-emoticon-dead:before{content:"\f0c6e"}.mdi-emoticon-dead-outline:before{content:"\f069b"}.mdi-emoticon-devil:before{content:"\f0c6f"}.mdi-emoticon-devil-outline:before{content:"\f01f4"}.mdi-emoticon-excited:before{content:"\f0c70"}.mdi-emoticon-excited-outline:before{content:"\f069c"}.mdi-emoticon-frown:before{content:"\f0f4c"}.mdi-emoticon-frown-outline:before{content:"\f0f4d"}.mdi-emoticon-happy:before{content:"\f0c71"}.mdi-emoticon-happy-outline:before{content:"\f01f5"}.mdi-emoticon-kiss:before{content:"\f0c72"}.mdi-emoticon-kiss-outline:before{content:"\f0c73"}.mdi-emoticon-lol:before{content:"\f1214"}.mdi-emoticon-lol-outline:before{content:"\f1215"}.mdi-emoticon-minus:before{content:"\f1cb2"}.mdi-emoticon-minus-outline:before{content:"\f1cb3"}.mdi-emoticon-neutral:before{content:"\f0c74"}.mdi-emoticon-neutral-outline:before{content:"\f01f6"}.mdi-emoticon-outline:before{content:"\f01f2"}.mdi-emoticon-plus:before{content:"\f1cb4"}.mdi-emoticon-plus-outline:before{content:"\f1cb5"}.mdi-emoticon-poop:before{content:"\f01f7"}.mdi-emoticon-poop-outline:before{content:"\f0c75"}.mdi-emoticon-remove:before{content:"\f1cb6"}.mdi-emoticon-remove-outline:before{content:"\f1cb7"}.mdi-emoticon-sad:before{content:"\f0c76"}.mdi-emoticon-sad-outline:before{content:"\f01f8"}.mdi-emoticon-sick:before{content:"\f157c"}.mdi-emoticon-sick-outline:before{content:"\f157d"}.mdi-emoticon-tongue:before{content:"\f01f9"}.mdi-emoticon-tongue-outline:before{content:"\f0c77"}.mdi-emoticon-wink:before{content:"\f0c78"}.mdi-emoticon-wink-outline:before{content:"\f0c79"}.mdi-engine:before{content:"\f01fa"}.mdi-engine-off:before{content:"\f0a46"}.mdi-engine-off-outline:before{content:"\f0a47"}.mdi-engine-outline:before{content:"\f01fb"}.mdi-epsilon:before{content:"\f10e0"}.mdi-equal:before{content:"\f01fc"}.mdi-equal-box:before{content:"\f01fd"}.mdi-equalizer:before{content:"\f0ea2"}.mdi-equalizer-outline:before{content:"\f0ea3"}.mdi-eraser:before{content:"\f01fe"}.mdi-eraser-variant:before{content:"\f0642"}.mdi-escalator:before{content:"\f01ff"}.mdi-escalator-box:before{content:"\f1399"}.mdi-escalator-down:before{content:"\f12c0"}.mdi-escalator-up:before{content:"\f12bf"}.mdi-eslint:before{content:"\f0c7a"}.mdi-et:before{content:"\f0ab3"}.mdi-ethereum:before{content:"\f086a"}.mdi-ethernet:before{content:"\f0200"}.mdi-ethernet-cable:before{content:"\f0201"}.mdi-ethernet-cable-off:before{content:"\f0202"}.mdi-ethernet-off:before{content:"\f1cd1"}.mdi-ev-plug-ccs1:before{content:"\f1519"}.mdi-ev-plug-ccs2:before{content:"\f151a"}.mdi-ev-plug-chademo:before{content:"\f151b"}.mdi-ev-plug-tesla:before{content:"\f151c"}.mdi-ev-plug-type1:before{content:"\f151d"}.mdi-ev-plug-type2:before{content:"\f151e"}.mdi-ev-station:before{content:"\f05f1"}.mdi-evernote:before{content:"\f0204"}.mdi-excavator:before{content:"\f1025"}.mdi-exclamation:before{content:"\f0205"}.mdi-exclamation-thick:before{content:"\f1238"}.mdi-exit-run:before{content:"\f0a48"}.mdi-exit-to-app:before{content:"\f0206"}.mdi-expand-all:before{content:"\f0ab4"}.mdi-expand-all-outline:before{content:"\f0ab5"}.mdi-expansion-card:before{content:"\f08ae"}.mdi-expansion-card-variant:before{content:"\f0fb2"}.mdi-exponent:before{content:"\f0963"}.mdi-exponent-box:before{content:"\f0964"}.mdi-export:before{content:"\f0207"}.mdi-export-variant:before{content:"\f0b93"}.mdi-eye:before{content:"\f0208"}.mdi-eye-arrow-left:before{content:"\f18fd"}.mdi-eye-arrow-left-outline:before{content:"\f18fe"}.mdi-eye-arrow-right:before{content:"\f18ff"}.mdi-eye-arrow-right-outline:before{content:"\f1900"}.mdi-eye-check:before{content:"\f0d04"}.mdi-eye-check-outline:before{content:"\f0d05"}.mdi-eye-circle:before{content:"\f0b94"}.mdi-eye-circle-outline:before{content:"\f0b95"}.mdi-eye-closed:before{content:"\f1ca3"}.mdi-eye-lock:before{content:"\f1c06"}.mdi-eye-lock-open:before{content:"\f1c07"}.mdi-eye-lock-open-outline:before{content:"\f1c08"}.mdi-eye-lock-outline:before{content:"\f1c09"}.mdi-eye-minus:before{content:"\f1026"}.mdi-eye-minus-outline:before{content:"\f1027"}.mdi-eye-off:before{content:"\f0209"}.mdi-eye-off-outline:before{content:"\f06d1"}.mdi-eye-outline:before{content:"\f06d0"}.mdi-eye-plus:before{content:"\f086b"}.mdi-eye-plus-outline:before{content:"\f086c"}.mdi-eye-refresh:before{content:"\f197c"}.mdi-eye-refresh-outline:before{content:"\f197d"}.mdi-eye-remove:before{content:"\f15e3"}.mdi-eye-remove-outline:before{content:"\f15e4"}.mdi-eye-settings:before{content:"\f086d"}.mdi-eye-settings-outline:before{content:"\f086e"}.mdi-eyedropper:before{content:"\f020a"}.mdi-eyedropper-minus:before{content:"\f13dd"}.mdi-eyedropper-off:before{content:"\f13df"}.mdi-eyedropper-plus:before{content:"\f13dc"}.mdi-eyedropper-remove:before{content:"\f13de"}.mdi-eyedropper-variant:before{content:"\f020b"}.mdi-face-agent:before{content:"\f0d70"}.mdi-face-man:before{content:"\f0643"}.mdi-face-man-outline:before{content:"\f0b96"}.mdi-face-man-profile:before{content:"\f0644"}.mdi-face-man-shimmer:before{content:"\f15cc"}.mdi-face-man-shimmer-outline:before{content:"\f15cd"}.mdi-face-mask:before{content:"\f1586"}.mdi-face-mask-outline:before{content:"\f1587"}.mdi-face-recognition:before{content:"\f0c7b"}.mdi-face-woman:before{content:"\f1077"}.mdi-face-woman-outline:before{content:"\f1078"}.mdi-face-woman-profile:before{content:"\f1076"}.mdi-face-woman-shimmer:before{content:"\f15ce"}.mdi-face-woman-shimmer-outline:before{content:"\f15cf"}.mdi-facebook:before{content:"\f020c"}.mdi-facebook-gaming:before{content:"\f07dd"}.mdi-facebook-messenger:before{content:"\f020e"}.mdi-facebook-workplace:before{content:"\f0b31"}.mdi-factory:before{content:"\f020f"}.mdi-family-tree:before{content:"\f160e"}.mdi-fan:before{content:"\f0210"}.mdi-fan-alert:before{content:"\f146c"}.mdi-fan-auto:before{content:"\f171d"}.mdi-fan-chevron-down:before{content:"\f146d"}.mdi-fan-chevron-up:before{content:"\f146e"}.mdi-fan-clock:before{content:"\f1a3a"}.mdi-fan-minus:before{content:"\f1470"}.mdi-fan-off:before{content:"\f081d"}.mdi-fan-plus:before{content:"\f146f"}.mdi-fan-remove:before{content:"\f1471"}.mdi-fan-speed-1:before{content:"\f1472"}.mdi-fan-speed-2:before{content:"\f1473"}.mdi-fan-speed-3:before{content:"\f1474"}.mdi-fast-forward:before{content:"\f0211"}.mdi-fast-forward-10:before{content:"\f0d71"}.mdi-fast-forward-15:before{content:"\f193a"}.mdi-fast-forward-30:before{content:"\f0d06"}.mdi-fast-forward-45:before{content:"\f1b12"}.mdi-fast-forward-5:before{content:"\f11f8"}.mdi-fast-forward-60:before{content:"\f160b"}.mdi-fast-forward-outline:before{content:"\f06d2"}.mdi-faucet:before{content:"\f1b29"}.mdi-faucet-variant:before{content:"\f1b2a"}.mdi-fax:before{content:"\f0212"}.mdi-feather:before{content:"\f06d3"}.mdi-feature-search:before{content:"\f0a49"}.mdi-feature-search-outline:before{content:"\f0a4a"}.mdi-fedora:before{content:"\f08db"}.mdi-fence:before{content:"\f179a"}.mdi-fence-electric:before{content:"\f17f6"}.mdi-fencing:before{content:"\f14c1"}.mdi-ferris-wheel:before{content:"\f0ea4"}.mdi-ferry:before{content:"\f0213"}.mdi-file:before{content:"\f0214"}.mdi-file-account:before{content:"\f073b"}.mdi-file-account-outline:before{content:"\f1028"}.mdi-file-alert:before{content:"\f0a4b"}.mdi-file-alert-outline:before{content:"\f0a4c"}.mdi-file-arrow-left-right:before{content:"\f1a93"}.mdi-file-arrow-left-right-outline:before{content:"\f1a94"}.mdi-file-arrow-up-down:before{content:"\f1a95"}.mdi-file-arrow-up-down-outline:before{content:"\f1a96"}.mdi-file-cabinet:before{content:"\f0ab6"}.mdi-file-cad:before{content:"\f0eeb"}.mdi-file-cad-box:before{content:"\f0eec"}.mdi-file-cancel:before{content:"\f0dc6"}.mdi-file-cancel-outline:before{content:"\f0dc7"}.mdi-file-certificate:before{content:"\f1186"}.mdi-file-certificate-outline:before{content:"\f1187"}.mdi-file-chart:before{content:"\f0215"}.mdi-file-chart-check:before{content:"\f19c6"}.mdi-file-chart-check-outline:before{content:"\f19c7"}.mdi-file-chart-outline:before{content:"\f1029"}.mdi-file-check:before{content:"\f0216"}.mdi-file-check-outline:before{content:"\f0e29"}.mdi-file-clock:before{content:"\f12e1"}.mdi-file-clock-outline:before{content:"\f12e2"}.mdi-file-cloud:before{content:"\f0217"}.mdi-file-cloud-outline:before{content:"\f102a"}.mdi-file-code:before{content:"\f022e"}.mdi-file-code-outline:before{content:"\f102b"}.mdi-file-cog:before{content:"\f107b"}.mdi-file-cog-outline:before{content:"\f107c"}.mdi-file-compare:before{content:"\f08aa"}.mdi-file-delimited:before{content:"\f0218"}.mdi-file-delimited-outline:before{content:"\f0ea5"}.mdi-file-document:before{content:"\f0219"}.mdi-file-document-alert:before{content:"\f1a97"}.mdi-file-document-alert-outline:before{content:"\f1a98"}.mdi-file-document-arrow-right:before{content:"\f1c0f"}.mdi-file-document-arrow-right-outline:before{content:"\f1c10"}.mdi-file-document-check:before{content:"\f1a99"}.mdi-file-document-check-outline:before{content:"\f1a9a"}.mdi-file-document-edit:before{content:"\f0dc8"}.mdi-file-document-edit-outline:before{content:"\f0dc9"}.mdi-file-document-minus:before{content:"\f1a9b"}.mdi-file-document-minus-outline:before{content:"\f1a9c"}.mdi-file-document-multiple:before{content:"\f1517"}.mdi-file-document-multiple-outline:before{content:"\f1518"}.mdi-file-document-outline:before{content:"\f09ee"}.mdi-file-document-plus:before{content:"\f1a9d"}.mdi-file-document-plus-outline:before{content:"\f1a9e"}.mdi-file-document-refresh:before{content:"\f1c7a"}.mdi-file-document-refresh-outline:before{content:"\f1c7b"}.mdi-file-document-remove:before{content:"\f1a9f"}.mdi-file-document-remove-outline:before{content:"\f1aa0"}.mdi-file-download:before{content:"\f0965"}.mdi-file-download-outline:before{content:"\f0966"}.mdi-file-edit:before{content:"\f11e7"}.mdi-file-edit-outline:before{content:"\f11e8"}.mdi-file-excel:before{content:"\f021b"}.mdi-file-excel-box:before{content:"\f021c"}.mdi-file-excel-box-outline:before{content:"\f102c"}.mdi-file-excel-outline:before{content:"\f102d"}.mdi-file-export:before{content:"\f021d"}.mdi-file-export-outline:before{content:"\f102e"}.mdi-file-eye:before{content:"\f0dca"}.mdi-file-eye-outline:before{content:"\f0dcb"}.mdi-file-find:before{content:"\f021e"}.mdi-file-find-outline:before{content:"\f0b97"}.mdi-file-gif-box:before{content:"\f0d78"}.mdi-file-hidden:before{content:"\f0613"}.mdi-file-image:before{content:"\f021f"}.mdi-file-image-marker:before{content:"\f1772"}.mdi-file-image-marker-outline:before{content:"\f1773"}.mdi-file-image-minus:before{content:"\f193b"}.mdi-file-image-minus-outline:before{content:"\f193c"}.mdi-file-image-outline:before{content:"\f0eb0"}.mdi-file-image-plus:before{content:"\f193d"}.mdi-file-image-plus-outline:before{content:"\f193e"}.mdi-file-image-remove:before{content:"\f193f"}.mdi-file-image-remove-outline:before{content:"\f1940"}.mdi-file-import:before{content:"\f0220"}.mdi-file-import-outline:before{content:"\f102f"}.mdi-file-jpg-box:before{content:"\f0225"}.mdi-file-key:before{content:"\f1184"}.mdi-file-key-outline:before{content:"\f1185"}.mdi-file-link:before{content:"\f1177"}.mdi-file-link-outline:before{content:"\f1178"}.mdi-file-lock:before{content:"\f0221"}.mdi-file-lock-open:before{content:"\f19c8"}.mdi-file-lock-open-outline:before{content:"\f19c9"}.mdi-file-lock-outline:before{content:"\f1030"}.mdi-file-marker:before{content:"\f1774"}.mdi-file-marker-outline:before{content:"\f1775"}.mdi-file-minus:before{content:"\f1aa1"}.mdi-file-minus-outline:before{content:"\f1aa2"}.mdi-file-move:before{content:"\f0ab9"}.mdi-file-move-outline:before{content:"\f1031"}.mdi-file-multiple:before{content:"\f0222"}.mdi-file-multiple-outline:before{content:"\f1032"}.mdi-file-music:before{content:"\f0223"}.mdi-file-music-outline:before{content:"\f0e2a"}.mdi-file-outline:before{content:"\f0224"}.mdi-file-pdf-box:before{content:"\f0226"}.mdi-file-percent:before{content:"\f081e"}.mdi-file-percent-outline:before{content:"\f1033"}.mdi-file-phone:before{content:"\f1179"}.mdi-file-phone-outline:before{content:"\f117a"}.mdi-file-plus:before{content:"\f0752"}.mdi-file-plus-outline:before{content:"\f0eed"}.mdi-file-png-box:before{content:"\f0e2d"}.mdi-file-powerpoint:before{content:"\f0227"}.mdi-file-powerpoint-box:before{content:"\f0228"}.mdi-file-powerpoint-box-outline:before{content:"\f1034"}.mdi-file-powerpoint-outline:before{content:"\f1035"}.mdi-file-presentation-box:before{content:"\f0229"}.mdi-file-question:before{content:"\f086f"}.mdi-file-question-outline:before{content:"\f1036"}.mdi-file-refresh:before{content:"\f0918"}.mdi-file-refresh-outline:before{content:"\f0541"}.mdi-file-remove:before{content:"\f0b98"}.mdi-file-remove-outline:before{content:"\f1037"}.mdi-file-replace:before{content:"\f0b32"}.mdi-file-replace-outline:before{content:"\f0b33"}.mdi-file-restore:before{content:"\f0670"}.mdi-file-restore-outline:before{content:"\f1038"}.mdi-file-rotate-left:before{content:"\f1a3b"}.mdi-file-rotate-left-outline:before{content:"\f1a3c"}.mdi-file-rotate-right:before{content:"\f1a3d"}.mdi-file-rotate-right-outline:before{content:"\f1a3e"}.mdi-file-search:before{content:"\f0c7c"}.mdi-file-search-outline:before{content:"\f0c7d"}.mdi-file-send:before{content:"\f022a"}.mdi-file-send-outline:before{content:"\f1039"}.mdi-file-settings:before{content:"\f1079"}.mdi-file-settings-outline:before{content:"\f107a"}.mdi-file-sign:before{content:"\f19c3"}.mdi-file-star:before{content:"\f103a"}.mdi-file-star-four-points:before{content:"\f1c2d"}.mdi-file-star-four-points-outline:before{content:"\f1c2e"}.mdi-file-star-outline:before{content:"\f103b"}.mdi-file-swap:before{content:"\f0fb4"}.mdi-file-swap-outline:before{content:"\f0fb5"}.mdi-file-sync:before{content:"\f1216"}.mdi-file-sync-outline:before{content:"\f1217"}.mdi-file-table:before{content:"\f0c7e"}.mdi-file-table-box:before{content:"\f10e1"}.mdi-file-table-box-multiple:before{content:"\f10e2"}.mdi-file-table-box-multiple-outline:before{content:"\f10e3"}.mdi-file-table-box-outline:before{content:"\f10e4"}.mdi-file-table-outline:before{content:"\f0c7f"}.mdi-file-tree:before{content:"\f0645"}.mdi-file-tree-outline:before{content:"\f13d2"}.mdi-file-undo:before{content:"\f08dc"}.mdi-file-undo-outline:before{content:"\f103c"}.mdi-file-upload:before{content:"\f0a4d"}.mdi-file-upload-outline:before{content:"\f0a4e"}.mdi-file-video:before{content:"\f022b"}.mdi-file-video-outline:before{content:"\f0e2c"}.mdi-file-word:before{content:"\f022c"}.mdi-file-word-box:before{content:"\f022d"}.mdi-file-word-box-outline:before{content:"\f103d"}.mdi-file-word-outline:before{content:"\f103e"}.mdi-file-xml-box:before{content:"\f1b4b"}.mdi-film:before{content:"\f022f"}.mdi-filmstrip:before{content:"\f0230"}.mdi-filmstrip-box:before{content:"\f0332"}.mdi-filmstrip-box-multiple:before{content:"\f0d18"}.mdi-filmstrip-off:before{content:"\f0231"}.mdi-filter:before{content:"\f0232"}.mdi-filter-check:before{content:"\f18ec"}.mdi-filter-check-outline:before{content:"\f18ed"}.mdi-filter-cog:before{content:"\f1aa3"}.mdi-filter-cog-outline:before{content:"\f1aa4"}.mdi-filter-menu:before{content:"\f10e5"}.mdi-filter-menu-outline:before{content:"\f10e6"}.mdi-filter-minus:before{content:"\f0eee"}.mdi-filter-minus-outline:before{content:"\f0eef"}.mdi-filter-multiple:before{content:"\f1a3f"}.mdi-filter-multiple-outline:before{content:"\f1a40"}.mdi-filter-off:before{content:"\f14ef"}.mdi-filter-off-outline:before{content:"\f14f0"}.mdi-filter-outline:before{content:"\f0233"}.mdi-filter-plus:before{content:"\f0ef0"}.mdi-filter-plus-outline:before{content:"\f0ef1"}.mdi-filter-remove:before{content:"\f0234"}.mdi-filter-remove-outline:before{content:"\f0235"}.mdi-filter-settings:before{content:"\f1aa5"}.mdi-filter-settings-outline:before{content:"\f1aa6"}.mdi-filter-variant:before{content:"\f0236"}.mdi-filter-variant-minus:before{content:"\f1112"}.mdi-filter-variant-plus:before{content:"\f1113"}.mdi-filter-variant-remove:before{content:"\f103f"}.mdi-finance:before{content:"\f081f"}.mdi-find-replace:before{content:"\f06d4"}.mdi-fingerprint:before{content:"\f0237"}.mdi-fingerprint-off:before{content:"\f0eb1"}.mdi-fire:before{content:"\f0238"}.mdi-fire-alert:before{content:"\f15d7"}.mdi-fire-circle:before{content:"\f1807"}.mdi-fire-extinguisher:before{content:"\f0ef2"}.mdi-fire-hydrant:before{content:"\f1137"}.mdi-fire-hydrant-alert:before{content:"\f1138"}.mdi-fire-hydrant-off:before{content:"\f1139"}.mdi-fire-off:before{content:"\f1722"}.mdi-fire-station:before{content:"\f1cc3"}.mdi-fire-truck:before{content:"\f08ab"}.mdi-firebase:before{content:"\f0967"}.mdi-firefox:before{content:"\f0239"}.mdi-fireplace:before{content:"\f0e2e"}.mdi-fireplace-off:before{content:"\f0e2f"}.mdi-firewire:before{content:"\f05be"}.mdi-firework:before{content:"\f0e30"}.mdi-firework-off:before{content:"\f1723"}.mdi-fish:before{content:"\f023a"}.mdi-fish-off:before{content:"\f13f3"}.mdi-fishbowl:before{content:"\f0ef3"}.mdi-fishbowl-outline:before{content:"\f0ef4"}.mdi-fit-to-page:before{content:"\f0ef5"}.mdi-fit-to-page-outline:before{content:"\f0ef6"}.mdi-fit-to-screen:before{content:"\f18f4"}.mdi-fit-to-screen-outline:before{content:"\f18f5"}.mdi-flag:before{content:"\f023b"}.mdi-flag-checkered:before{content:"\f023c"}.mdi-flag-minus:before{content:"\f0b99"}.mdi-flag-minus-outline:before{content:"\f10b2"}.mdi-flag-off:before{content:"\f18ee"}.mdi-flag-off-outline:before{content:"\f18ef"}.mdi-flag-outline:before{content:"\f023d"}.mdi-flag-plus:before{content:"\f0b9a"}.mdi-flag-plus-outline:before{content:"\f10b3"}.mdi-flag-remove:before{content:"\f0b9b"}.mdi-flag-remove-outline:before{content:"\f10b4"}.mdi-flag-triangle:before{content:"\f023f"}.mdi-flag-variant:before{content:"\f0240"}.mdi-flag-variant-minus:before{content:"\f1bb4"}.mdi-flag-variant-minus-outline:before{content:"\f1bb5"}.mdi-flag-variant-off:before{content:"\f1bb0"}.mdi-flag-variant-off-outline:before{content:"\f1bb1"}.mdi-flag-variant-outline:before{content:"\f023e"}.mdi-flag-variant-plus:before{content:"\f1bb2"}.mdi-flag-variant-plus-outline:before{content:"\f1bb3"}.mdi-flag-variant-remove:before{content:"\f1bb6"}.mdi-flag-variant-remove-outline:before{content:"\f1bb7"}.mdi-flare:before{content:"\f0d72"}.mdi-flash:before{content:"\f0241"}.mdi-flash-alert:before{content:"\f0ef7"}.mdi-flash-alert-outline:before{content:"\f0ef8"}.mdi-flash-auto:before{content:"\f0242"}.mdi-flash-off:before{content:"\f0243"}.mdi-flash-off-outline:before{content:"\f1b45"}.mdi-flash-outline:before{content:"\f06d5"}.mdi-flash-red-eye:before{content:"\f067b"}.mdi-flash-triangle:before{content:"\f1b1d"}.mdi-flash-triangle-outline:before{content:"\f1b1e"}.mdi-flashlight:before{content:"\f0244"}.mdi-flashlight-off:before{content:"\f0245"}.mdi-flask:before{content:"\f0093"}.mdi-flask-empty:before{content:"\f0094"}.mdi-flask-empty-minus:before{content:"\f123a"}.mdi-flask-empty-minus-outline:before{content:"\f123b"}.mdi-flask-empty-off:before{content:"\f13f4"}.mdi-flask-empty-off-outline:before{content:"\f13f5"}.mdi-flask-empty-outline:before{content:"\f0095"}.mdi-flask-empty-plus:before{content:"\f123c"}.mdi-flask-empty-plus-outline:before{content:"\f123d"}.mdi-flask-empty-remove:before{content:"\f123e"}.mdi-flask-empty-remove-outline:before{content:"\f123f"}.mdi-flask-minus:before{content:"\f1240"}.mdi-flask-minus-outline:before{content:"\f1241"}.mdi-flask-off:before{content:"\f13f6"}.mdi-flask-off-outline:before{content:"\f13f7"}.mdi-flask-outline:before{content:"\f0096"}.mdi-flask-plus:before{content:"\f1242"}.mdi-flask-plus-outline:before{content:"\f1243"}.mdi-flask-remove:before{content:"\f1244"}.mdi-flask-remove-outline:before{content:"\f1245"}.mdi-flask-round-bottom:before{content:"\f124b"}.mdi-flask-round-bottom-empty:before{content:"\f124c"}.mdi-flask-round-bottom-empty-outline:before{content:"\f124d"}.mdi-flask-round-bottom-outline:before{content:"\f124e"}.mdi-fleur-de-lis:before{content:"\f1303"}.mdi-flip-horizontal:before{content:"\f10e7"}.mdi-flip-to-back:before{content:"\f0247"}.mdi-flip-to-front:before{content:"\f0248"}.mdi-flip-vertical:before{content:"\f10e8"}.mdi-floor-lamp:before{content:"\f08dd"}.mdi-floor-lamp-dual:before{content:"\f1040"}.mdi-floor-lamp-dual-outline:before{content:"\f17ce"}.mdi-floor-lamp-outline:before{content:"\f17c8"}.mdi-floor-lamp-torchiere:before{content:"\f1747"}.mdi-floor-lamp-torchiere-outline:before{content:"\f17d6"}.mdi-floor-lamp-torchiere-variant:before{content:"\f1041"}.mdi-floor-lamp-torchiere-variant-outline:before{content:"\f17cf"}.mdi-floor-plan:before{content:"\f0821"}.mdi-floppy:before{content:"\f0249"}.mdi-floppy-variant:before{content:"\f09ef"}.mdi-flower:before{content:"\f024a"}.mdi-flower-outline:before{content:"\f09f0"}.mdi-flower-pollen:before{content:"\f1885"}.mdi-flower-pollen-outline:before{content:"\f1886"}.mdi-flower-poppy:before{content:"\f0d08"}.mdi-flower-tulip:before{content:"\f09f1"}.mdi-flower-tulip-outline:before{content:"\f09f2"}.mdi-focus-auto:before{content:"\f0f4e"}.mdi-focus-field:before{content:"\f0f4f"}.mdi-focus-field-horizontal:before{content:"\f0f50"}.mdi-focus-field-vertical:before{content:"\f0f51"}.mdi-folder:before{content:"\f024b"}.mdi-folder-account:before{content:"\f024c"}.mdi-folder-account-outline:before{content:"\f0b9c"}.mdi-folder-alert:before{content:"\f0dcc"}.mdi-folder-alert-outline:before{content:"\f0dcd"}.mdi-folder-arrow-down:before{content:"\f19e8"}.mdi-folder-arrow-down-outline:before{content:"\f19e9"}.mdi-folder-arrow-left:before{content:"\f19ea"}.mdi-folder-arrow-left-outline:before{content:"\f19eb"}.mdi-folder-arrow-left-right:before{content:"\f19ec"}.mdi-folder-arrow-left-right-outline:before{content:"\f19ed"}.mdi-folder-arrow-right:before{content:"\f19ee"}.mdi-folder-arrow-right-outline:before{content:"\f19ef"}.mdi-folder-arrow-up:before{content:"\f19f0"}.mdi-folder-arrow-up-down:before{content:"\f19f1"}.mdi-folder-arrow-up-down-outline:before{content:"\f19f2"}.mdi-folder-arrow-up-outline:before{content:"\f19f3"}.mdi-folder-cancel:before{content:"\f19f4"}.mdi-folder-cancel-outline:before{content:"\f19f5"}.mdi-folder-check:before{content:"\f197e"}.mdi-folder-check-outline:before{content:"\f197f"}.mdi-folder-clock:before{content:"\f0aba"}.mdi-folder-clock-outline:before{content:"\f0abb"}.mdi-folder-cog:before{content:"\f107f"}.mdi-folder-cog-outline:before{content:"\f1080"}.mdi-folder-download:before{content:"\f024d"}.mdi-folder-download-outline:before{content:"\f10e9"}.mdi-folder-edit:before{content:"\f08de"}.mdi-folder-edit-outline:before{content:"\f0dce"}.mdi-folder-eye:before{content:"\f178a"}.mdi-folder-eye-outline:before{content:"\f178b"}.mdi-folder-file:before{content:"\f19f6"}.mdi-folder-file-outline:before{content:"\f19f7"}.mdi-folder-google-drive:before{content:"\f024e"}.mdi-folder-heart:before{content:"\f10ea"}.mdi-folder-heart-outline:before{content:"\f10eb"}.mdi-folder-hidden:before{content:"\f179e"}.mdi-folder-home:before{content:"\f10b5"}.mdi-folder-home-outline:before{content:"\f10b6"}.mdi-folder-image:before{content:"\f024f"}.mdi-folder-information:before{content:"\f10b7"}.mdi-folder-information-outline:before{content:"\f10b8"}.mdi-folder-key:before{content:"\f08ac"}.mdi-folder-key-network:before{content:"\f08ad"}.mdi-folder-key-network-outline:before{content:"\f0c80"}.mdi-folder-key-outline:before{content:"\f10ec"}.mdi-folder-lock:before{content:"\f0250"}.mdi-folder-lock-open:before{content:"\f0251"}.mdi-folder-lock-open-outline:before{content:"\f1aa7"}.mdi-folder-lock-outline:before{content:"\f1aa8"}.mdi-folder-marker:before{content:"\f126d"}.mdi-folder-marker-outline:before{content:"\f126e"}.mdi-folder-minus:before{content:"\f1b49"}.mdi-folder-minus-outline:before{content:"\f1b4a"}.mdi-folder-move:before{content:"\f0252"}.mdi-folder-move-outline:before{content:"\f1246"}.mdi-folder-multiple:before{content:"\f0253"}.mdi-folder-multiple-image:before{content:"\f0254"}.mdi-folder-multiple-outline:before{content:"\f0255"}.mdi-folder-multiple-plus:before{content:"\f147e"}.mdi-folder-multiple-plus-outline:before{content:"\f147f"}.mdi-folder-music:before{content:"\f1359"}.mdi-folder-music-outline:before{content:"\f135a"}.mdi-folder-network:before{content:"\f0870"}.mdi-folder-network-outline:before{content:"\f0c81"}.mdi-folder-off:before{content:"\f19f8"}.mdi-folder-off-outline:before{content:"\f19f9"}.mdi-folder-open:before{content:"\f0770"}.mdi-folder-open-outline:before{content:"\f0dcf"}.mdi-folder-outline:before{content:"\f0256"}.mdi-folder-play:before{content:"\f19fa"}.mdi-folder-play-outline:before{content:"\f19fb"}.mdi-folder-plus:before{content:"\f0257"}.mdi-folder-plus-outline:before{content:"\f0b9d"}.mdi-folder-pound:before{content:"\f0d09"}.mdi-folder-pound-outline:before{content:"\f0d0a"}.mdi-folder-question:before{content:"\f19ca"}.mdi-folder-question-outline:before{content:"\f19cb"}.mdi-folder-refresh:before{content:"\f0749"}.mdi-folder-refresh-outline:before{content:"\f0542"}.mdi-folder-remove:before{content:"\f0258"}.mdi-folder-remove-outline:before{content:"\f0b9e"}.mdi-folder-search:before{content:"\f0968"}.mdi-folder-search-outline:before{content:"\f0969"}.mdi-folder-settings:before{content:"\f107d"}.mdi-folder-settings-outline:before{content:"\f107e"}.mdi-folder-star:before{content:"\f069d"}.mdi-folder-star-multiple:before{content:"\f13d3"}.mdi-folder-star-multiple-outline:before{content:"\f13d4"}.mdi-folder-star-outline:before{content:"\f0b9f"}.mdi-folder-swap:before{content:"\f0fb6"}.mdi-folder-swap-outline:before{content:"\f0fb7"}.mdi-folder-sync:before{content:"\f0d0b"}.mdi-folder-sync-outline:before{content:"\f0d0c"}.mdi-folder-table:before{content:"\f12e3"}.mdi-folder-table-outline:before{content:"\f12e4"}.mdi-folder-text:before{content:"\f0c82"}.mdi-folder-text-outline:before{content:"\f0c83"}.mdi-folder-upload:before{content:"\f0259"}.mdi-folder-upload-outline:before{content:"\f10ed"}.mdi-folder-wrench:before{content:"\f19fc"}.mdi-folder-wrench-outline:before{content:"\f19fd"}.mdi-folder-zip:before{content:"\f06eb"}.mdi-folder-zip-outline:before{content:"\f07b9"}.mdi-font-awesome:before{content:"\f003a"}.mdi-food:before{content:"\f025a"}.mdi-food-apple:before{content:"\f025b"}.mdi-food-apple-outline:before{content:"\f0c84"}.mdi-food-croissant:before{content:"\f07c8"}.mdi-food-drumstick:before{content:"\f141f"}.mdi-food-drumstick-off:before{content:"\f1468"}.mdi-food-drumstick-off-outline:before{content:"\f1469"}.mdi-food-drumstick-outline:before{content:"\f1420"}.mdi-food-fork-drink:before{content:"\f05f2"}.mdi-food-halal:before{content:"\f1572"}.mdi-food-hot-dog:before{content:"\f184b"}.mdi-food-kosher:before{content:"\f1573"}.mdi-food-off:before{content:"\f05f3"}.mdi-food-off-outline:before{content:"\f1915"}.mdi-food-outline:before{content:"\f1916"}.mdi-food-steak:before{content:"\f146a"}.mdi-food-steak-off:before{content:"\f146b"}.mdi-food-takeout-box:before{content:"\f1836"}.mdi-food-takeout-box-outline:before{content:"\f1837"}.mdi-food-turkey:before{content:"\f171c"}.mdi-food-variant:before{content:"\f025c"}.mdi-food-variant-off:before{content:"\f13e5"}.mdi-foot-print:before{content:"\f0f52"}.mdi-football:before{content:"\f025d"}.mdi-football-australian:before{content:"\f025e"}.mdi-football-helmet:before{content:"\f025f"}.mdi-forest:before{content:"\f1897"}.mdi-forest-outline:before{content:"\f1c63"}.mdi-forklift:before{content:"\f07c9"}.mdi-form-dropdown:before{content:"\f1400"}.mdi-form-select:before{content:"\f1401"}.mdi-form-textarea:before{content:"\f1095"}.mdi-form-textbox:before{content:"\f060e"}.mdi-form-textbox-lock:before{content:"\f135d"}.mdi-form-textbox-password:before{content:"\f07f5"}.mdi-format-align-bottom:before{content:"\f0753"}.mdi-format-align-center:before{content:"\f0260"}.mdi-format-align-justify:before{content:"\f0261"}.mdi-format-align-left:before{content:"\f0262"}.mdi-format-align-middle:before{content:"\f0754"}.mdi-format-align-right:before{content:"\f0263"}.mdi-format-align-top:before{content:"\f0755"}.mdi-format-annotation-minus:before{content:"\f0abc"}.mdi-format-annotation-plus:before{content:"\f0646"}.mdi-format-bold:before{content:"\f0264"}.mdi-format-clear:before{content:"\f0265"}.mdi-format-color-fill:before{content:"\f0266"}.mdi-format-color-highlight:before{content:"\f0e31"}.mdi-format-color-marker-cancel:before{content:"\f1313"}.mdi-format-color-text:before{content:"\f069e"}.mdi-format-columns:before{content:"\f08df"}.mdi-format-float-center:before{content:"\f0267"}.mdi-format-float-left:before{content:"\f0268"}.mdi-format-float-none:before{content:"\f0269"}.mdi-format-float-right:before{content:"\f026a"}.mdi-format-font:before{content:"\f06d6"}.mdi-format-font-size-decrease:before{content:"\f09f3"}.mdi-format-font-size-increase:before{content:"\f09f4"}.mdi-format-header-1:before{content:"\f026b"}.mdi-format-header-2:before{content:"\f026c"}.mdi-format-header-3:before{content:"\f026d"}.mdi-format-header-4:before{content:"\f026e"}.mdi-format-header-5:before{content:"\f026f"}.mdi-format-header-6:before{content:"\f0270"}.mdi-format-header-decrease:before{content:"\f0271"}.mdi-format-header-equal:before{content:"\f0272"}.mdi-format-header-increase:before{content:"\f0273"}.mdi-format-header-pound:before{content:"\f0274"}.mdi-format-horizontal-align-center:before{content:"\f061e"}.mdi-format-horizontal-align-left:before{content:"\f061f"}.mdi-format-horizontal-align-right:before{content:"\f0620"}.mdi-format-indent-decrease:before{content:"\f0275"}.mdi-format-indent-increase:before{content:"\f0276"}.mdi-format-italic:before{content:"\f0277"}.mdi-format-letter-case:before{content:"\f0b34"}.mdi-format-letter-case-lower:before{content:"\f0b35"}.mdi-format-letter-case-upper:before{content:"\f0b36"}.mdi-format-letter-ends-with:before{content:"\f0fb8"}.mdi-format-letter-matches:before{content:"\f0fb9"}.mdi-format-letter-spacing:before{content:"\f1956"}.mdi-format-letter-spacing-variant:before{content:"\f1afb"}.mdi-format-letter-starts-with:before{content:"\f0fba"}.mdi-format-line-height:before{content:"\f1afc"}.mdi-format-line-spacing:before{content:"\f0278"}.mdi-format-line-style:before{content:"\f05c8"}.mdi-format-line-weight:before{content:"\f05c9"}.mdi-format-list-bulleted:before{content:"\f0279"}.mdi-format-list-bulleted-square:before{content:"\f0dd0"}.mdi-format-list-bulleted-triangle:before{content:"\f0eb2"}.mdi-format-list-bulleted-type:before{content:"\f027a"}.mdi-format-list-checkbox:before{content:"\f096a"}.mdi-format-list-checks:before{content:"\f0756"}.mdi-format-list-group:before{content:"\f1860"}.mdi-format-list-group-plus:before{content:"\f1b56"}.mdi-format-list-numbered:before{content:"\f027b"}.mdi-format-list-numbered-rtl:before{content:"\f0d0d"}.mdi-format-list-text:before{content:"\f126f"}.mdi-format-overline:before{content:"\f0eb3"}.mdi-format-page-break:before{content:"\f06d7"}.mdi-format-page-split:before{content:"\f1917"}.mdi-format-paint:before{content:"\f027c"}.mdi-format-paragraph:before{content:"\f027d"}.mdi-format-paragraph-spacing:before{content:"\f1afd"}.mdi-format-pilcrow:before{content:"\f06d8"}.mdi-format-pilcrow-arrow-left:before{content:"\f0286"}.mdi-format-pilcrow-arrow-right:before{content:"\f0285"}.mdi-format-quote-close:before{content:"\f027e"}.mdi-format-quote-close-outline:before{content:"\f11a8"}.mdi-format-quote-open:before{content:"\f0757"}.mdi-format-quote-open-outline:before{content:"\f11a7"}.mdi-format-rotate-90:before{content:"\f06aa"}.mdi-format-section:before{content:"\f069f"}.mdi-format-size:before{content:"\f027f"}.mdi-format-strikethrough:before{content:"\f0280"}.mdi-format-strikethrough-variant:before{content:"\f0281"}.mdi-format-subscript:before{content:"\f0282"}.mdi-format-superscript:before{content:"\f0283"}.mdi-format-text:before{content:"\f0284"}.mdi-format-text-rotation-angle-down:before{content:"\f0fbb"}.mdi-format-text-rotation-angle-up:before{content:"\f0fbc"}.mdi-format-text-rotation-down:before{content:"\f0d73"}.mdi-format-text-rotation-down-vertical:before{content:"\f0fbd"}.mdi-format-text-rotation-none:before{content:"\f0d74"}.mdi-format-text-rotation-up:before{content:"\f0fbe"}.mdi-format-text-rotation-vertical:before{content:"\f0fbf"}.mdi-format-text-variant:before{content:"\f0e32"}.mdi-format-text-variant-outline:before{content:"\f150f"}.mdi-format-text-wrapping-clip:before{content:"\f0d0e"}.mdi-format-text-wrapping-overflow:before{content:"\f0d0f"}.mdi-format-text-wrapping-wrap:before{content:"\f0d10"}.mdi-format-textbox:before{content:"\f0d11"}.mdi-format-title:before{content:"\f05f4"}.mdi-format-underline:before{content:"\f0287"}.mdi-format-underline-wavy:before{content:"\f18e9"}.mdi-format-vertical-align-bottom:before{content:"\f0621"}.mdi-format-vertical-align-center:before{content:"\f0622"}.mdi-format-vertical-align-top:before{content:"\f0623"}.mdi-format-wrap-inline:before{content:"\f0288"}.mdi-format-wrap-square:before{content:"\f0289"}.mdi-format-wrap-tight:before{content:"\f028a"}.mdi-format-wrap-top-bottom:before{content:"\f028b"}.mdi-forum:before{content:"\f028c"}.mdi-forum-minus:before{content:"\f1aa9"}.mdi-forum-minus-outline:before{content:"\f1aaa"}.mdi-forum-outline:before{content:"\f0822"}.mdi-forum-plus:before{content:"\f1aab"}.mdi-forum-plus-outline:before{content:"\f1aac"}.mdi-forum-remove:before{content:"\f1aad"}.mdi-forum-remove-outline:before{content:"\f1aae"}.mdi-forward:before{content:"\f028d"}.mdi-forwardburger:before{content:"\f0d75"}.mdi-fountain:before{content:"\f096b"}.mdi-fountain-pen:before{content:"\f0d12"}.mdi-fountain-pen-tip:before{content:"\f0d13"}.mdi-fraction-one-half:before{content:"\f1992"}.mdi-freebsd:before{content:"\f08e0"}.mdi-french-fries:before{content:"\f1957"}.mdi-frequently-asked-questions:before{content:"\f0eb4"}.mdi-fridge:before{content:"\f0290"}.mdi-fridge-alert:before{content:"\f11b1"}.mdi-fridge-alert-outline:before{content:"\f11b2"}.mdi-fridge-bottom:before{content:"\f0292"}.mdi-fridge-industrial:before{content:"\f15ee"}.mdi-fridge-industrial-alert:before{content:"\f15ef"}.mdi-fridge-industrial-alert-outline:before{content:"\f15f0"}.mdi-fridge-industrial-off:before{content:"\f15f1"}.mdi-fridge-industrial-off-outline:before{content:"\f15f2"}.mdi-fridge-industrial-outline:before{content:"\f15f3"}.mdi-fridge-off:before{content:"\f11af"}.mdi-fridge-off-outline:before{content:"\f11b0"}.mdi-fridge-outline:before{content:"\f028f"}.mdi-fridge-top:before{content:"\f0291"}.mdi-fridge-variant:before{content:"\f15f4"}.mdi-fridge-variant-alert:before{content:"\f15f5"}.mdi-fridge-variant-alert-outline:before{content:"\f15f6"}.mdi-fridge-variant-off:before{content:"\f15f7"}.mdi-fridge-variant-off-outline:before{content:"\f15f8"}.mdi-fridge-variant-outline:before{content:"\f15f9"}.mdi-fruit-cherries:before{content:"\f1042"}.mdi-fruit-cherries-off:before{content:"\f13f8"}.mdi-fruit-citrus:before{content:"\f1043"}.mdi-fruit-citrus-off:before{content:"\f13f9"}.mdi-fruit-grapes:before{content:"\f1044"}.mdi-fruit-grapes-outline:before{content:"\f1045"}.mdi-fruit-pear:before{content:"\f1a0e"}.mdi-fruit-pineapple:before{content:"\f1046"}.mdi-fruit-watermelon:before{content:"\f1047"}.mdi-fuel:before{content:"\f07ca"}.mdi-fuel-cell:before{content:"\f18b5"}.mdi-fullscreen:before{content:"\f0293"}.mdi-fullscreen-exit:before{content:"\f0294"}.mdi-function:before{content:"\f0295"}.mdi-function-variant:before{content:"\f0871"}.mdi-furigana-horizontal:before{content:"\f1081"}.mdi-furigana-vertical:before{content:"\f1082"}.mdi-fuse:before{content:"\f0c85"}.mdi-fuse-alert:before{content:"\f142d"}.mdi-fuse-blade:before{content:"\f0c86"}.mdi-fuse-off:before{content:"\f142c"}.mdi-gamepad:before{content:"\f0296"}.mdi-gamepad-circle:before{content:"\f0e33"}.mdi-gamepad-circle-down:before{content:"\f0e34"}.mdi-gamepad-circle-left:before{content:"\f0e35"}.mdi-gamepad-circle-outline:before{content:"\f0e36"}.mdi-gamepad-circle-right:before{content:"\f0e37"}.mdi-gamepad-circle-up:before{content:"\f0e38"}.mdi-gamepad-down:before{content:"\f0e39"}.mdi-gamepad-left:before{content:"\f0e3a"}.mdi-gamepad-outline:before{content:"\f1919"}.mdi-gamepad-right:before{content:"\f0e3b"}.mdi-gamepad-round:before{content:"\f0e3c"}.mdi-gamepad-round-down:before{content:"\f0e3d"}.mdi-gamepad-round-left:before{content:"\f0e3e"}.mdi-gamepad-round-outline:before{content:"\f0e3f"}.mdi-gamepad-round-right:before{content:"\f0e40"}.mdi-gamepad-round-up:before{content:"\f0e41"}.mdi-gamepad-square:before{content:"\f0eb5"}.mdi-gamepad-square-outline:before{content:"\f0eb6"}.mdi-gamepad-up:before{content:"\f0e42"}.mdi-gamepad-variant:before{content:"\f0297"}.mdi-gamepad-variant-outline:before{content:"\f0eb7"}.mdi-gamma:before{content:"\f10ee"}.mdi-gantry-crane:before{content:"\f0dd1"}.mdi-garage:before{content:"\f06d9"}.mdi-garage-alert:before{content:"\f0872"}.mdi-garage-alert-variant:before{content:"\f12d5"}.mdi-garage-lock:before{content:"\f17fb"}.mdi-garage-open:before{content:"\f06da"}.mdi-garage-open-variant:before{content:"\f12d4"}.mdi-garage-variant:before{content:"\f12d3"}.mdi-garage-variant-lock:before{content:"\f17fc"}.mdi-gas-burner:before{content:"\f1a1b"}.mdi-gas-cylinder:before{content:"\f0647"}.mdi-gas-station:before{content:"\f0298"}.mdi-gas-station-in-use:before{content:"\f1cc4"}.mdi-gas-station-in-use-outline:before{content:"\f1cc5"}.mdi-gas-station-off:before{content:"\f1409"}.mdi-gas-station-off-outline:before{content:"\f140a"}.mdi-gas-station-outline:before{content:"\f0eb8"}.mdi-gate:before{content:"\f0299"}.mdi-gate-alert:before{content:"\f17f8"}.mdi-gate-and:before{content:"\f08e1"}.mdi-gate-arrow-left:before{content:"\f17f7"}.mdi-gate-arrow-right:before{content:"\f1169"}.mdi-gate-buffer:before{content:"\f1afe"}.mdi-gate-nand:before{content:"\f08e2"}.mdi-gate-nor:before{content:"\f08e3"}.mdi-gate-not:before{content:"\f08e4"}.mdi-gate-open:before{content:"\f116a"}.mdi-gate-or:before{content:"\f08e5"}.mdi-gate-xnor:before{content:"\f08e6"}.mdi-gate-xor:before{content:"\f08e7"}.mdi-gatsby:before{content:"\f0e43"}.mdi-gauge:before{content:"\f029a"}.mdi-gauge-empty:before{content:"\f0873"}.mdi-gauge-full:before{content:"\f0874"}.mdi-gauge-low:before{content:"\f0875"}.mdi-gavel:before{content:"\f029b"}.mdi-gender-female:before{content:"\f029c"}.mdi-gender-male:before{content:"\f029d"}.mdi-gender-male-female:before{content:"\f029e"}.mdi-gender-male-female-variant:before{content:"\f113f"}.mdi-gender-non-binary:before{content:"\f1140"}.mdi-gender-transgender:before{content:"\f029f"}.mdi-generator-mobile:before{content:"\f1c8a"}.mdi-generator-portable:before{content:"\f1c8b"}.mdi-generator-stationary:before{content:"\f1c8c"}.mdi-gentoo:before{content:"\f08e8"}.mdi-gesture:before{content:"\f07cb"}.mdi-gesture-double-tap:before{content:"\f073c"}.mdi-gesture-pinch:before{content:"\f0abd"}.mdi-gesture-spread:before{content:"\f0abe"}.mdi-gesture-swipe:before{content:"\f0d76"}.mdi-gesture-swipe-down:before{content:"\f073d"}.mdi-gesture-swipe-horizontal:before{content:"\f0abf"}.mdi-gesture-swipe-left:before{content:"\f073e"}.mdi-gesture-swipe-right:before{content:"\f073f"}.mdi-gesture-swipe-up:before{content:"\f0740"}.mdi-gesture-swipe-vertical:before{content:"\f0ac0"}.mdi-gesture-tap:before{content:"\f0741"}.mdi-gesture-tap-box:before{content:"\f12a9"}.mdi-gesture-tap-button:before{content:"\f12a8"}.mdi-gesture-tap-hold:before{content:"\f0d77"}.mdi-gesture-two-double-tap:before{content:"\f0742"}.mdi-gesture-two-tap:before{content:"\f0743"}.mdi-ghost:before{content:"\f02a0"}.mdi-ghost-off:before{content:"\f09f5"}.mdi-ghost-off-outline:before{content:"\f165c"}.mdi-ghost-outline:before{content:"\f165d"}.mdi-gift:before{content:"\f0e44"}.mdi-gift-off:before{content:"\f16ef"}.mdi-gift-off-outline:before{content:"\f16f0"}.mdi-gift-open:before{content:"\f16f1"}.mdi-gift-open-outline:before{content:"\f16f2"}.mdi-gift-outline:before{content:"\f02a1"}.mdi-git:before{content:"\f02a2"}.mdi-github:before{content:"\f02a4"}.mdi-gitlab:before{content:"\f0ba0"}.mdi-glass-cocktail:before{content:"\f0356"}.mdi-glass-cocktail-off:before{content:"\f15e6"}.mdi-glass-flute:before{content:"\f02a5"}.mdi-glass-fragile:before{content:"\f1873"}.mdi-glass-mug:before{content:"\f02a6"}.mdi-glass-mug-off:before{content:"\f15e7"}.mdi-glass-mug-variant:before{content:"\f1116"}.mdi-glass-mug-variant-off:before{content:"\f15e8"}.mdi-glass-pint-outline:before{content:"\f130d"}.mdi-glass-stange:before{content:"\f02a7"}.mdi-glass-tulip:before{content:"\f02a8"}.mdi-glass-wine:before{content:"\f0876"}.mdi-glasses:before{content:"\f02aa"}.mdi-globe-light:before{content:"\f066f"}.mdi-globe-light-outline:before{content:"\f12d7"}.mdi-globe-model:before{content:"\f08e9"}.mdi-gmail:before{content:"\f02ab"}.mdi-gnome:before{content:"\f02ac"}.mdi-go-kart:before{content:"\f0d79"}.mdi-go-kart-track:before{content:"\f0d7a"}.mdi-gog:before{content:"\f0ba1"}.mdi-gold:before{content:"\f124f"}.mdi-golf:before{content:"\f0823"}.mdi-golf-cart:before{content:"\f11a4"}.mdi-golf-tee:before{content:"\f1083"}.mdi-gondola:before{content:"\f0686"}.mdi-goodreads:before{content:"\f0d7b"}.mdi-google:before{content:"\f02ad"}.mdi-google-ads:before{content:"\f0c87"}.mdi-google-analytics:before{content:"\f07cc"}.mdi-google-assistant:before{content:"\f07cd"}.mdi-google-cardboard:before{content:"\f02ae"}.mdi-google-chrome:before{content:"\f02af"}.mdi-google-circles:before{content:"\f02b0"}.mdi-google-circles-communities:before{content:"\f02b1"}.mdi-google-circles-extended:before{content:"\f02b2"}.mdi-google-circles-group:before{content:"\f02b3"}.mdi-google-classroom:before{content:"\f02c0"}.mdi-google-cloud:before{content:"\f11f6"}.mdi-google-downasaur:before{content:"\f1362"}.mdi-google-drive:before{content:"\f02b6"}.mdi-google-earth:before{content:"\f02b7"}.mdi-google-fit:before{content:"\f096c"}.mdi-google-glass:before{content:"\f02b8"}.mdi-google-hangouts:before{content:"\f02c9"}.mdi-google-keep:before{content:"\f06dc"}.mdi-google-lens:before{content:"\f09f6"}.mdi-google-maps:before{content:"\f05f5"}.mdi-google-my-business:before{content:"\f1048"}.mdi-google-nearby:before{content:"\f02b9"}.mdi-google-play:before{content:"\f02bc"}.mdi-google-plus:before{content:"\f02bd"}.mdi-google-podcast:before{content:"\f0eb9"}.mdi-google-spreadsheet:before{content:"\f09f7"}.mdi-google-street-view:before{content:"\f0c88"}.mdi-google-translate:before{content:"\f02bf"}.mdi-gradient-horizontal:before{content:"\f174a"}.mdi-gradient-vertical:before{content:"\f06a0"}.mdi-grain:before{content:"\f0d7c"}.mdi-graph:before{content:"\f1049"}.mdi-graph-outline:before{content:"\f104a"}.mdi-graphql:before{content:"\f0877"}.mdi-grass:before{content:"\f1510"}.mdi-grave-stone:before{content:"\f0ba2"}.mdi-grease-pencil:before{content:"\f0648"}.mdi-greater-than:before{content:"\f096d"}.mdi-greater-than-or-equal:before{content:"\f096e"}.mdi-greenhouse:before{content:"\f002d"}.mdi-grid:before{content:"\f02c1"}.mdi-grid-large:before{content:"\f0758"}.mdi-grid-off:before{content:"\f02c2"}.mdi-grill:before{content:"\f0e45"}.mdi-grill-outline:before{content:"\f118a"}.mdi-group:before{content:"\f02c3"}.mdi-guitar-acoustic:before{content:"\f0771"}.mdi-guitar-electric:before{content:"\f02c4"}.mdi-guitar-pick:before{content:"\f02c5"}.mdi-guitar-pick-outline:before{content:"\f02c6"}.mdi-guy-fawkes-mask:before{content:"\f0825"}.mdi-gymnastics:before{content:"\f1a41"}.mdi-hail:before{content:"\f0ac1"}.mdi-hair-dryer:before{content:"\f10ef"}.mdi-hair-dryer-outline:before{content:"\f10f0"}.mdi-halloween:before{content:"\f0ba3"}.mdi-hamburger:before{content:"\f0685"}.mdi-hamburger-check:before{content:"\f1776"}.mdi-hamburger-minus:before{content:"\f1777"}.mdi-hamburger-off:before{content:"\f1778"}.mdi-hamburger-plus:before{content:"\f1779"}.mdi-hamburger-remove:before{content:"\f177a"}.mdi-hammer:before{content:"\f08ea"}.mdi-hammer-screwdriver:before{content:"\f1322"}.mdi-hammer-sickle:before{content:"\f1887"}.mdi-hammer-wrench:before{content:"\f1323"}.mdi-hand-back-left:before{content:"\f0e46"}.mdi-hand-back-left-off:before{content:"\f1830"}.mdi-hand-back-left-off-outline:before{content:"\f1832"}.mdi-hand-back-left-outline:before{content:"\f182c"}.mdi-hand-back-right:before{content:"\f0e47"}.mdi-hand-back-right-off:before{content:"\f1831"}.mdi-hand-back-right-off-outline:before{content:"\f1833"}.mdi-hand-back-right-outline:before{content:"\f182d"}.mdi-hand-clap:before{content:"\f194b"}.mdi-hand-clap-off:before{content:"\f1a42"}.mdi-hand-coin:before{content:"\f188f"}.mdi-hand-coin-outline:before{content:"\f1890"}.mdi-hand-cycle:before{content:"\f1b9c"}.mdi-hand-extended:before{content:"\f18b6"}.mdi-hand-extended-outline:before{content:"\f18b7"}.mdi-hand-front-left:before{content:"\f182b"}.mdi-hand-front-left-outline:before{content:"\f182e"}.mdi-hand-front-right:before{content:"\f0a4f"}.mdi-hand-front-right-outline:before{content:"\f182f"}.mdi-hand-heart:before{content:"\f10f1"}.mdi-hand-heart-outline:before{content:"\f157e"}.mdi-hand-okay:before{content:"\f0a50"}.mdi-hand-peace:before{content:"\f0a51"}.mdi-hand-peace-variant:before{content:"\f0a52"}.mdi-hand-pointing-down:before{content:"\f0a53"}.mdi-hand-pointing-left:before{content:"\f0a54"}.mdi-hand-pointing-right:before{content:"\f02c7"}.mdi-hand-pointing-up:before{content:"\f0a55"}.mdi-hand-saw:before{content:"\f0e48"}.mdi-hand-wash:before{content:"\f157f"}.mdi-hand-wash-outline:before{content:"\f1580"}.mdi-hand-water:before{content:"\f139f"}.mdi-hand-wave:before{content:"\f1821"}.mdi-hand-wave-outline:before{content:"\f1822"}.mdi-handball:before{content:"\f0f53"}.mdi-handcuffs:before{content:"\f113e"}.mdi-hands-pray:before{content:"\f0579"}.mdi-handshake:before{content:"\f1218"}.mdi-handshake-outline:before{content:"\f15a1"}.mdi-hanger:before{content:"\f02c8"}.mdi-hard-hat:before{content:"\f096f"}.mdi-harddisk:before{content:"\f02ca"}.mdi-harddisk-plus:before{content:"\f104b"}.mdi-harddisk-remove:before{content:"\f104c"}.mdi-hat-fedora:before{content:"\f0ba4"}.mdi-hazard-lights:before{content:"\f0c89"}.mdi-hdmi-port:before{content:"\f1bb8"}.mdi-hdr:before{content:"\f0d7d"}.mdi-hdr-off:before{content:"\f0d7e"}.mdi-head:before{content:"\f135e"}.mdi-head-alert:before{content:"\f1338"}.mdi-head-alert-outline:before{content:"\f1339"}.mdi-head-check:before{content:"\f133a"}.mdi-head-check-outline:before{content:"\f133b"}.mdi-head-cog:before{content:"\f133c"}.mdi-head-cog-outline:before{content:"\f133d"}.mdi-head-dots-horizontal:before{content:"\f133e"}.mdi-head-dots-horizontal-outline:before{content:"\f133f"}.mdi-head-flash:before{content:"\f1340"}.mdi-head-flash-outline:before{content:"\f1341"}.mdi-head-heart:before{content:"\f1342"}.mdi-head-heart-outline:before{content:"\f1343"}.mdi-head-lightbulb:before{content:"\f1344"}.mdi-head-lightbulb-outline:before{content:"\f1345"}.mdi-head-minus:before{content:"\f1346"}.mdi-head-minus-outline:before{content:"\f1347"}.mdi-head-outline:before{content:"\f135f"}.mdi-head-plus:before{content:"\f1348"}.mdi-head-plus-outline:before{content:"\f1349"}.mdi-head-question:before{content:"\f134a"}.mdi-head-question-outline:before{content:"\f134b"}.mdi-head-remove:before{content:"\f134c"}.mdi-head-remove-outline:before{content:"\f134d"}.mdi-head-snowflake:before{content:"\f134e"}.mdi-head-snowflake-outline:before{content:"\f134f"}.mdi-head-sync:before{content:"\f1350"}.mdi-head-sync-outline:before{content:"\f1351"}.mdi-headphones:before{content:"\f02cb"}.mdi-headphones-bluetooth:before{content:"\f0970"}.mdi-headphones-box:before{content:"\f02cc"}.mdi-headphones-off:before{content:"\f07ce"}.mdi-headphones-settings:before{content:"\f02cd"}.mdi-headset:before{content:"\f02ce"}.mdi-headset-dock:before{content:"\f02cf"}.mdi-headset-off:before{content:"\f02d0"}.mdi-heart:before{content:"\f02d1"}.mdi-heart-box:before{content:"\f02d2"}.mdi-heart-box-outline:before{content:"\f02d3"}.mdi-heart-broken:before{content:"\f02d4"}.mdi-heart-broken-outline:before{content:"\f0d14"}.mdi-heart-circle:before{content:"\f0971"}.mdi-heart-circle-outline:before{content:"\f0972"}.mdi-heart-cog:before{content:"\f1663"}.mdi-heart-cog-outline:before{content:"\f1664"}.mdi-heart-flash:before{content:"\f0ef9"}.mdi-heart-half:before{content:"\f06df"}.mdi-heart-half-full:before{content:"\f06de"}.mdi-heart-half-outline:before{content:"\f06e0"}.mdi-heart-minus:before{content:"\f142f"}.mdi-heart-minus-outline:before{content:"\f1432"}.mdi-heart-multiple:before{content:"\f0a56"}.mdi-heart-multiple-outline:before{content:"\f0a57"}.mdi-heart-off:before{content:"\f0759"}.mdi-heart-off-outline:before{content:"\f1434"}.mdi-heart-outline:before{content:"\f02d5"}.mdi-heart-plus:before{content:"\f142e"}.mdi-heart-plus-outline:before{content:"\f1431"}.mdi-heart-pulse:before{content:"\f05f6"}.mdi-heart-remove:before{content:"\f1430"}.mdi-heart-remove-outline:before{content:"\f1433"}.mdi-heart-search:before{content:"\f1c8d"}.mdi-heart-settings:before{content:"\f1665"}.mdi-heart-settings-outline:before{content:"\f1666"}.mdi-heat-pump:before{content:"\f1a43"}.mdi-heat-pump-outline:before{content:"\f1a44"}.mdi-heat-wave:before{content:"\f1a45"}.mdi-heating-coil:before{content:"\f1aaf"}.mdi-helicopter:before{content:"\f0ac2"}.mdi-help:before{content:"\f02d6"}.mdi-help-box:before{content:"\f078b"}.mdi-help-box-multiple:before{content:"\f1c0a"}.mdi-help-box-multiple-outline:before{content:"\f1c0b"}.mdi-help-box-outline:before{content:"\f1c0c"}.mdi-help-circle:before{content:"\f02d7"}.mdi-help-circle-outline:before{content:"\f0625"}.mdi-help-network:before{content:"\f06f5"}.mdi-help-network-outline:before{content:"\f0c8a"}.mdi-help-rhombus:before{content:"\f0ba5"}.mdi-help-rhombus-outline:before{content:"\f0ba6"}.mdi-hexadecimal:before{content:"\f12a7"}.mdi-hexagon:before{content:"\f02d8"}.mdi-hexagon-multiple:before{content:"\f06e1"}.mdi-hexagon-multiple-outline:before{content:"\f10f2"}.mdi-hexagon-outline:before{content:"\f02d9"}.mdi-hexagon-slice-1:before{content:"\f0ac3"}.mdi-hexagon-slice-2:before{content:"\f0ac4"}.mdi-hexagon-slice-3:before{content:"\f0ac5"}.mdi-hexagon-slice-4:before{content:"\f0ac6"}.mdi-hexagon-slice-5:before{content:"\f0ac7"}.mdi-hexagon-slice-6:before{content:"\f0ac8"}.mdi-hexagram:before{content:"\f0ac9"}.mdi-hexagram-outline:before{content:"\f0aca"}.mdi-high-definition:before{content:"\f07cf"}.mdi-high-definition-box:before{content:"\f0878"}.mdi-highway:before{content:"\f05f7"}.mdi-hiking:before{content:"\f0d7f"}.mdi-history:before{content:"\f02da"}.mdi-hockey-puck:before{content:"\f0879"}.mdi-hockey-sticks:before{content:"\f087a"}.mdi-hololens:before{content:"\f02db"}.mdi-home:before{content:"\f02dc"}.mdi-home-account:before{content:"\f0826"}.mdi-home-alert:before{content:"\f087b"}.mdi-home-alert-outline:before{content:"\f15d0"}.mdi-home-analytics:before{content:"\f0eba"}.mdi-home-assistant:before{content:"\f07d0"}.mdi-home-automation:before{content:"\f07d1"}.mdi-home-battery:before{content:"\f1901"}.mdi-home-battery-outline:before{content:"\f1902"}.mdi-home-circle:before{content:"\f07d2"}.mdi-home-circle-outline:before{content:"\f104d"}.mdi-home-city:before{content:"\f0d15"}.mdi-home-city-outline:before{content:"\f0d16"}.mdi-home-clock:before{content:"\f1a12"}.mdi-home-clock-outline:before{content:"\f1a13"}.mdi-home-edit:before{content:"\f1159"}.mdi-home-edit-outline:before{content:"\f115a"}.mdi-home-export-outline:before{content:"\f0f9b"}.mdi-home-flood:before{content:"\f0efa"}.mdi-home-floor-0:before{content:"\f0dd2"}.mdi-home-floor-1:before{content:"\f0d80"}.mdi-home-floor-2:before{content:"\f0d81"}.mdi-home-floor-3:before{content:"\f0d82"}.mdi-home-floor-a:before{content:"\f0d83"}.mdi-home-floor-b:before{content:"\f0d84"}.mdi-home-floor-g:before{content:"\f0d85"}.mdi-home-floor-l:before{content:"\f0d86"}.mdi-home-floor-negative-1:before{content:"\f0dd3"}.mdi-home-group:before{content:"\f0dd4"}.mdi-home-group-minus:before{content:"\f19c1"}.mdi-home-group-plus:before{content:"\f19c0"}.mdi-home-group-remove:before{content:"\f19c2"}.mdi-home-heart:before{content:"\f0827"}.mdi-home-import-outline:before{content:"\f0f9c"}.mdi-home-lightbulb:before{content:"\f1251"}.mdi-home-lightbulb-outline:before{content:"\f1252"}.mdi-home-lightning-bolt:before{content:"\f1903"}.mdi-home-lightning-bolt-outline:before{content:"\f1904"}.mdi-home-lock:before{content:"\f08eb"}.mdi-home-lock-open:before{content:"\f08ec"}.mdi-home-map-marker:before{content:"\f05f8"}.mdi-home-minus:before{content:"\f0974"}.mdi-home-minus-outline:before{content:"\f13d5"}.mdi-home-modern:before{content:"\f02dd"}.mdi-home-off:before{content:"\f1a46"}.mdi-home-off-outline:before{content:"\f1a47"}.mdi-home-outline:before{content:"\f06a1"}.mdi-home-percent:before{content:"\f1c7c"}.mdi-home-percent-outline:before{content:"\f1c7d"}.mdi-home-plus:before{content:"\f0975"}.mdi-home-plus-outline:before{content:"\f13d6"}.mdi-home-remove:before{content:"\f1247"}.mdi-home-remove-outline:before{content:"\f13d7"}.mdi-home-roof:before{content:"\f112b"}.mdi-home-search:before{content:"\f13b0"}.mdi-home-search-outline:before{content:"\f13b1"}.mdi-home-silo:before{content:"\f1ba0"}.mdi-home-silo-outline:before{content:"\f1ba1"}.mdi-home-sound-in:before{content:"\f1c2f"}.mdi-home-sound-in-outline:before{content:"\f1c30"}.mdi-home-sound-out:before{content:"\f1c31"}.mdi-home-sound-out-outline:before{content:"\f1c32"}.mdi-home-switch:before{content:"\f1794"}.mdi-home-switch-outline:before{content:"\f1795"}.mdi-home-thermometer:before{content:"\f0f54"}.mdi-home-thermometer-outline:before{content:"\f0f55"}.mdi-home-variant:before{content:"\f02de"}.mdi-home-variant-outline:before{content:"\f0ba7"}.mdi-hook:before{content:"\f06e2"}.mdi-hook-off:before{content:"\f06e3"}.mdi-hoop-house:before{content:"\f0e56"}.mdi-hops:before{content:"\f02df"}.mdi-horizontal-rotate-clockwise:before{content:"\f10f3"}.mdi-horizontal-rotate-counterclockwise:before{content:"\f10f4"}.mdi-horse:before{content:"\f15bf"}.mdi-horse-human:before{content:"\f15c0"}.mdi-horse-variant:before{content:"\f15c1"}.mdi-horse-variant-fast:before{content:"\f186e"}.mdi-horseshoe:before{content:"\f0a58"}.mdi-hospital:before{content:"\f0ff6"}.mdi-hospital-box:before{content:"\f02e0"}.mdi-hospital-box-outline:before{content:"\f0ff7"}.mdi-hospital-building:before{content:"\f02e1"}.mdi-hospital-marker:before{content:"\f02e2"}.mdi-hot-tub:before{content:"\f0828"}.mdi-hours-12:before{content:"\f1c94"}.mdi-hours-24:before{content:"\f1478"}.mdi-hub:before{content:"\f1c95"}.mdi-hub-outline:before{content:"\f1c96"}.mdi-hubspot:before{content:"\f0d17"}.mdi-hulu:before{content:"\f0829"}.mdi-human:before{content:"\f02e6"}.mdi-human-baby-changing-table:before{content:"\f138b"}.mdi-human-cane:before{content:"\f1581"}.mdi-human-capacity-decrease:before{content:"\f159b"}.mdi-human-capacity-increase:before{content:"\f159c"}.mdi-human-child:before{content:"\f02e7"}.mdi-human-dolly:before{content:"\f1980"}.mdi-human-edit:before{content:"\f14e8"}.mdi-human-female:before{content:"\f0649"}.mdi-human-female-boy:before{content:"\f0a59"}.mdi-human-female-dance:before{content:"\f15c9"}.mdi-human-female-female:before{content:"\f0a5a"}.mdi-human-female-female-child:before{content:"\f1c8e"}.mdi-human-female-girl:before{content:"\f0a5b"}.mdi-human-greeting:before{content:"\f17c4"}.mdi-human-greeting-proximity:before{content:"\f159d"}.mdi-human-greeting-variant:before{content:"\f064a"}.mdi-human-handsdown:before{content:"\f064b"}.mdi-human-handsup:before{content:"\f064c"}.mdi-human-male:before{content:"\f064d"}.mdi-human-male-board:before{content:"\f0890"}.mdi-human-male-board-poll:before{content:"\f0846"}.mdi-human-male-boy:before{content:"\f0a5c"}.mdi-human-male-child:before{content:"\f138c"}.mdi-human-male-female:before{content:"\f02e8"}.mdi-human-male-female-child:before{content:"\f1823"}.mdi-human-male-girl:before{content:"\f0a5d"}.mdi-human-male-height:before{content:"\f0efb"}.mdi-human-male-height-variant:before{content:"\f0efc"}.mdi-human-male-male:before{content:"\f0a5e"}.mdi-human-male-male-child:before{content:"\f1c8f"}.mdi-human-non-binary:before{content:"\f1848"}.mdi-human-pregnant:before{content:"\f05cf"}.mdi-human-queue:before{content:"\f1571"}.mdi-human-scooter:before{content:"\f11e9"}.mdi-human-walker:before{content:"\f1b71"}.mdi-human-wheelchair:before{content:"\f138d"}.mdi-human-white-cane:before{content:"\f1981"}.mdi-humble-bundle:before{content:"\f0744"}.mdi-hvac:before{content:"\f1352"}.mdi-hvac-off:before{content:"\f159e"}.mdi-hydraulic-oil-level:before{content:"\f1324"}.mdi-hydraulic-oil-temperature:before{content:"\f1325"}.mdi-hydro-power:before{content:"\f12e5"}.mdi-hydrogen-station:before{content:"\f1894"}.mdi-ice-cream:before{content:"\f082a"}.mdi-ice-cream-off:before{content:"\f0e52"}.mdi-ice-pop:before{content:"\f0efd"}.mdi-id-card:before{content:"\f0fc0"}.mdi-identifier:before{content:"\f0efe"}.mdi-ideogram-cjk:before{content:"\f1331"}.mdi-ideogram-cjk-variant:before{content:"\f1332"}.mdi-image:before{content:"\f02e9"}.mdi-image-album:before{content:"\f02ea"}.mdi-image-area:before{content:"\f02eb"}.mdi-image-area-close:before{content:"\f02ec"}.mdi-image-auto-adjust:before{content:"\f0fc1"}.mdi-image-broken:before{content:"\f02ed"}.mdi-image-broken-variant:before{content:"\f02ee"}.mdi-image-check:before{content:"\f1b25"}.mdi-image-check-outline:before{content:"\f1b26"}.mdi-image-edit:before{content:"\f11e3"}.mdi-image-edit-outline:before{content:"\f11e4"}.mdi-image-filter-black-white:before{content:"\f02f0"}.mdi-image-filter-center-focus:before{content:"\f02f1"}.mdi-image-filter-center-focus-strong:before{content:"\f0eff"}.mdi-image-filter-center-focus-strong-outline:before{content:"\f0f00"}.mdi-image-filter-center-focus-weak:before{content:"\f02f2"}.mdi-image-filter-drama:before{content:"\f02f3"}.mdi-image-filter-drama-outline:before{content:"\f1bff"}.mdi-image-filter-frames:before{content:"\f02f4"}.mdi-image-filter-hdr:before{content:"\f02f5"}.mdi-image-filter-hdr-outline:before{content:"\f1c64"}.mdi-image-filter-none:before{content:"\f02f6"}.mdi-image-filter-tilt-shift:before{content:"\f02f7"}.mdi-image-filter-vintage:before{content:"\f02f8"}.mdi-image-frame:before{content:"\f0e49"}.mdi-image-lock:before{content:"\f1ab0"}.mdi-image-lock-outline:before{content:"\f1ab1"}.mdi-image-marker:before{content:"\f177b"}.mdi-image-marker-outline:before{content:"\f177c"}.mdi-image-minus:before{content:"\f1419"}.mdi-image-minus-outline:before{content:"\f1b47"}.mdi-image-move:before{content:"\f09f8"}.mdi-image-multiple:before{content:"\f02f9"}.mdi-image-multiple-outline:before{content:"\f02ef"}.mdi-image-off:before{content:"\f082b"}.mdi-image-off-outline:before{content:"\f11d1"}.mdi-image-outline:before{content:"\f0976"}.mdi-image-plus:before{content:"\f087c"}.mdi-image-plus-outline:before{content:"\f1b46"}.mdi-image-refresh:before{content:"\f19fe"}.mdi-image-refresh-outline:before{content:"\f19ff"}.mdi-image-remove:before{content:"\f1418"}.mdi-image-remove-outline:before{content:"\f1b48"}.mdi-image-search:before{content:"\f0977"}.mdi-image-search-outline:before{content:"\f0978"}.mdi-image-size-select-actual:before{content:"\f0c8d"}.mdi-image-size-select-large:before{content:"\f0c8e"}.mdi-image-size-select-small:before{content:"\f0c8f"}.mdi-image-sync:before{content:"\f1a00"}.mdi-image-sync-outline:before{content:"\f1a01"}.mdi-image-text:before{content:"\f160d"}.mdi-import:before{content:"\f02fa"}.mdi-inbox:before{content:"\f0687"}.mdi-inbox-arrow-down:before{content:"\f02fb"}.mdi-inbox-arrow-down-outline:before{content:"\f1270"}.mdi-inbox-arrow-up:before{content:"\f03d1"}.mdi-inbox-arrow-up-outline:before{content:"\f1271"}.mdi-inbox-full:before{content:"\f1272"}.mdi-inbox-full-outline:before{content:"\f1273"}.mdi-inbox-multiple:before{content:"\f08b0"}.mdi-inbox-multiple-outline:before{content:"\f0ba8"}.mdi-inbox-outline:before{content:"\f1274"}.mdi-inbox-remove:before{content:"\f159f"}.mdi-inbox-remove-outline:before{content:"\f15a0"}.mdi-incognito:before{content:"\f05f9"}.mdi-incognito-circle:before{content:"\f1421"}.mdi-incognito-circle-off:before{content:"\f1422"}.mdi-incognito-off:before{content:"\f0075"}.mdi-induction:before{content:"\f184c"}.mdi-infinity:before{content:"\f06e4"}.mdi-information:before{content:"\f02fc"}.mdi-information-box:before{content:"\f1c65"}.mdi-information-box-outline:before{content:"\f1c66"}.mdi-information-off:before{content:"\f178c"}.mdi-information-off-outline:before{content:"\f178d"}.mdi-information-outline:before{content:"\f02fd"}.mdi-information-slab-box:before{content:"\f1c67"}.mdi-information-slab-box-outline:before{content:"\f1c68"}.mdi-information-slab-circle:before{content:"\f1c69"}.mdi-information-slab-circle-outline:before{content:"\f1c6a"}.mdi-information-slab-symbol:before{content:"\f1c6b"}.mdi-information-symbol:before{content:"\f1c6c"}.mdi-information-variant:before{content:"\f064e"}.mdi-information-variant-box:before{content:"\f1c6d"}.mdi-information-variant-box-outline:before{content:"\f1c6e"}.mdi-information-variant-circle:before{content:"\f1c6f"}.mdi-information-variant-circle-outline:before{content:"\f1c70"}.mdi-instagram:before{content:"\f02fe"}.mdi-instrument-triangle:before{content:"\f104e"}.mdi-integrated-circuit-chip:before{content:"\f1913"}.mdi-invert-colors:before{content:"\f0301"}.mdi-invert-colors-off:before{content:"\f0e4a"}.mdi-invoice:before{content:"\f1cd2"}.mdi-invoice-arrow-left:before{content:"\f1cd3"}.mdi-invoice-arrow-left-outline:before{content:"\f1cd4"}.mdi-invoice-arrow-right:before{content:"\f1cd5"}.mdi-invoice-arrow-right-outline:before{content:"\f1cd6"}.mdi-invoice-check:before{content:"\f1cd7"}.mdi-invoice-check-outline:before{content:"\f1cd8"}.mdi-invoice-clock:before{content:"\f1cd9"}.mdi-invoice-clock-outline:before{content:"\f1cda"}.mdi-invoice-edit:before{content:"\f1cdb"}.mdi-invoice-edit-outline:before{content:"\f1cdc"}.mdi-invoice-export-outline:before{content:"\f1cdd"}.mdi-invoice-fast:before{content:"\f1cde"}.mdi-invoice-fast-outline:before{content:"\f1cdf"}.mdi-invoice-import:before{content:"\f1ce0"}.mdi-invoice-import-outline:before{content:"\f1ce1"}.mdi-invoice-list:before{content:"\f1ce2"}.mdi-invoice-list-outline:before{content:"\f1ce3"}.mdi-invoice-minus:before{content:"\f1ce4"}.mdi-invoice-minus-outline:before{content:"\f1ce5"}.mdi-invoice-multiple:before{content:"\f1ce6"}.mdi-invoice-multiple-outline:before{content:"\f1ce7"}.mdi-invoice-outline:before{content:"\f1ce8"}.mdi-invoice-plus:before{content:"\f1ce9"}.mdi-invoice-plus-outline:before{content:"\f1cea"}.mdi-invoice-remove:before{content:"\f1ceb"}.mdi-invoice-remove-outline:before{content:"\f1cec"}.mdi-invoice-send:before{content:"\f1ced"}.mdi-invoice-send-outline:before{content:"\f1cee"}.mdi-invoice-text:before{content:"\f1cef"}.mdi-invoice-text-arrow-left:before{content:"\f1cf0"}.mdi-invoice-text-arrow-left-outline:before{content:"\f1cf1"}.mdi-invoice-text-arrow-right:before{content:"\f1cf2"}.mdi-invoice-text-arrow-right-outline:before{content:"\f1cf3"}.mdi-invoice-text-check:before{content:"\f1cf4"}.mdi-invoice-text-check-outline:before{content:"\f1cf5"}.mdi-invoice-text-clock:before{content:"\f1cf6"}.mdi-invoice-text-clock-outline:before{content:"\f1cf7"}.mdi-invoice-text-edit:before{content:"\f1cf8"}.mdi-invoice-text-edit-outline:before{content:"\f1cf9"}.mdi-invoice-text-fast:before{content:"\f1cfa"}.mdi-invoice-text-fast-outline:before{content:"\f1cfb"}.mdi-invoice-text-minus:before{content:"\f1cfc"}.mdi-invoice-text-minus-outline:before{content:"\f1cfd"}.mdi-invoice-text-multiple:before{content:"\f1cfe"}.mdi-invoice-text-multiple-outline:before{content:"\f1cff"}.mdi-invoice-text-outline:before{content:"\f1d00"}.mdi-invoice-text-plus:before{content:"\f1d01"}.mdi-invoice-text-plus-outline:before{content:"\f1d02"}.mdi-invoice-text-remove:before{content:"\f1d03"}.mdi-invoice-text-remove-outline:before{content:"\f1d04"}.mdi-invoice-text-send:before{content:"\f1d05"}.mdi-invoice-text-send-outline:before{content:"\f1d06"}.mdi-iobroker:before{content:"\f12e8"}.mdi-ip:before{content:"\f0a5f"}.mdi-ip-network:before{content:"\f0a60"}.mdi-ip-network-outline:before{content:"\f0c90"}.mdi-ip-outline:before{content:"\f1982"}.mdi-ipod:before{content:"\f0c91"}.mdi-iron:before{content:"\f1824"}.mdi-iron-board:before{content:"\f1838"}.mdi-iron-outline:before{content:"\f1825"}.mdi-island:before{content:"\f104f"}.mdi-island-variant:before{content:"\f1cc6"}.mdi-iv-bag:before{content:"\f10b9"}.mdi-jabber:before{content:"\f0dd5"}.mdi-jeepney:before{content:"\f0302"}.mdi-jellyfish:before{content:"\f0f01"}.mdi-jellyfish-outline:before{content:"\f0f02"}.mdi-jira:before{content:"\f0303"}.mdi-jquery:before{content:"\f087d"}.mdi-jsfiddle:before{content:"\f0304"}.mdi-jump-rope:before{content:"\f12ff"}.mdi-kabaddi:before{content:"\f0d87"}.mdi-kangaroo:before{content:"\f1558"}.mdi-karate:before{content:"\f082c"}.mdi-kayaking:before{content:"\f08af"}.mdi-keg:before{content:"\f0305"}.mdi-kettle:before{content:"\f05fa"}.mdi-kettle-alert:before{content:"\f1317"}.mdi-kettle-alert-outline:before{content:"\f1318"}.mdi-kettle-off:before{content:"\f131b"}.mdi-kettle-off-outline:before{content:"\f131c"}.mdi-kettle-outline:before{content:"\f0f56"}.mdi-kettle-pour-over:before{content:"\f173c"}.mdi-kettle-steam:before{content:"\f1319"}.mdi-kettle-steam-outline:before{content:"\f131a"}.mdi-kettlebell:before{content:"\f1300"}.mdi-key:before{content:"\f0306"}.mdi-key-alert:before{content:"\f1983"}.mdi-key-alert-outline:before{content:"\f1984"}.mdi-key-arrow-right:before{content:"\f1312"}.mdi-key-chain:before{content:"\f1574"}.mdi-key-chain-variant:before{content:"\f1575"}.mdi-key-change:before{content:"\f0307"}.mdi-key-link:before{content:"\f119f"}.mdi-key-minus:before{content:"\f0308"}.mdi-key-outline:before{content:"\f0dd6"}.mdi-key-plus:before{content:"\f0309"}.mdi-key-remove:before{content:"\f030a"}.mdi-key-star:before{content:"\f119e"}.mdi-key-variant:before{content:"\f030b"}.mdi-key-wireless:before{content:"\f0fc2"}.mdi-keyboard:before{content:"\f030c"}.mdi-keyboard-backspace:before{content:"\f030d"}.mdi-keyboard-caps:before{content:"\f030e"}.mdi-keyboard-close:before{content:"\f030f"}.mdi-keyboard-close-outline:before{content:"\f1c00"}.mdi-keyboard-esc:before{content:"\f12b7"}.mdi-keyboard-f1:before{content:"\f12ab"}.mdi-keyboard-f10:before{content:"\f12b4"}.mdi-keyboard-f11:before{content:"\f12b5"}.mdi-keyboard-f12:before{content:"\f12b6"}.mdi-keyboard-f2:before{content:"\f12ac"}.mdi-keyboard-f3:before{content:"\f12ad"}.mdi-keyboard-f4:before{content:"\f12ae"}.mdi-keyboard-f5:before{content:"\f12af"}.mdi-keyboard-f6:before{content:"\f12b0"}.mdi-keyboard-f7:before{content:"\f12b1"}.mdi-keyboard-f8:before{content:"\f12b2"}.mdi-keyboard-f9:before{content:"\f12b3"}.mdi-keyboard-off:before{content:"\f0310"}.mdi-keyboard-off-outline:before{content:"\f0e4b"}.mdi-keyboard-outline:before{content:"\f097b"}.mdi-keyboard-return:before{content:"\f0311"}.mdi-keyboard-settings:before{content:"\f09f9"}.mdi-keyboard-settings-outline:before{content:"\f09fa"}.mdi-keyboard-space:before{content:"\f1050"}.mdi-keyboard-tab:before{content:"\f0312"}.mdi-keyboard-tab-reverse:before{content:"\f0325"}.mdi-keyboard-variant:before{content:"\f0313"}.mdi-khanda:before{content:"\f10fd"}.mdi-kickstarter:before{content:"\f0745"}.mdi-kite:before{content:"\f1985"}.mdi-kite-outline:before{content:"\f1986"}.mdi-kitesurfing:before{content:"\f1744"}.mdi-klingon:before{content:"\f135b"}.mdi-knife:before{content:"\f09fb"}.mdi-knife-military:before{content:"\f09fc"}.mdi-knob:before{content:"\f1b96"}.mdi-koala:before{content:"\f173f"}.mdi-kodi:before{content:"\f0314"}.mdi-kubernetes:before{content:"\f10fe"}.mdi-label:before{content:"\f0315"}.mdi-label-multiple:before{content:"\f1375"}.mdi-label-multiple-outline:before{content:"\f1376"}.mdi-label-off:before{content:"\f0acb"}.mdi-label-off-outline:before{content:"\f0acc"}.mdi-label-outline:before{content:"\f0316"}.mdi-label-percent:before{content:"\f12ea"}.mdi-label-percent-outline:before{content:"\f12eb"}.mdi-label-variant:before{content:"\f0acd"}.mdi-label-variant-outline:before{content:"\f0ace"}.mdi-ladder:before{content:"\f15a2"}.mdi-ladybug:before{content:"\f082d"}.mdi-lambda:before{content:"\f0627"}.mdi-lamp:before{content:"\f06b5"}.mdi-lamp-outline:before{content:"\f17d0"}.mdi-lamps:before{content:"\f1576"}.mdi-lamps-outline:before{content:"\f17d1"}.mdi-lan:before{content:"\f0317"}.mdi-lan-check:before{content:"\f12aa"}.mdi-lan-connect:before{content:"\f0318"}.mdi-lan-disconnect:before{content:"\f0319"}.mdi-lan-pending:before{content:"\f031a"}.mdi-land-fields:before{content:"\f1ab2"}.mdi-land-plots:before{content:"\f1ab3"}.mdi-land-plots-circle:before{content:"\f1ab4"}.mdi-land-plots-circle-variant:before{content:"\f1ab5"}.mdi-land-plots-marker:before{content:"\f1c5d"}.mdi-land-rows-horizontal:before{content:"\f1ab6"}.mdi-land-rows-vertical:before{content:"\f1ab7"}.mdi-landslide:before{content:"\f1a48"}.mdi-landslide-outline:before{content:"\f1a49"}.mdi-language-c:before{content:"\f0671"}.mdi-language-cpp:before{content:"\f0672"}.mdi-language-csharp:before{content:"\f031b"}.mdi-language-css3:before{content:"\f031c"}.mdi-language-fortran:before{content:"\f121a"}.mdi-language-go:before{content:"\f07d3"}.mdi-language-haskell:before{content:"\f0c92"}.mdi-language-html5:before{content:"\f031d"}.mdi-language-java:before{content:"\f0b37"}.mdi-language-javascript:before{content:"\f031e"}.mdi-language-kotlin:before{content:"\f1219"}.mdi-language-lua:before{content:"\f08b1"}.mdi-language-markdown:before{content:"\f0354"}.mdi-language-markdown-outline:before{content:"\f0f5b"}.mdi-language-php:before{content:"\f031f"}.mdi-language-python:before{content:"\f0320"}.mdi-language-r:before{content:"\f07d4"}.mdi-language-ruby:before{content:"\f0d2d"}.mdi-language-ruby-on-rails:before{content:"\f0acf"}.mdi-language-rust:before{content:"\f1617"}.mdi-language-swift:before{content:"\f06e5"}.mdi-language-typescript:before{content:"\f06e6"}.mdi-language-xaml:before{content:"\f0673"}.mdi-laptop:before{content:"\f0322"}.mdi-laptop-account:before{content:"\f1a4a"}.mdi-laptop-off:before{content:"\f06e7"}.mdi-laravel:before{content:"\f0ad0"}.mdi-laser-pointer:before{content:"\f1484"}.mdi-lasso:before{content:"\f0f03"}.mdi-lastpass:before{content:"\f0446"}.mdi-latitude:before{content:"\f0f57"}.mdi-launch:before{content:"\f0327"}.mdi-lava-lamp:before{content:"\f07d5"}.mdi-layers:before{content:"\f0328"}.mdi-layers-edit:before{content:"\f1892"}.mdi-layers-minus:before{content:"\f0e4c"}.mdi-layers-off:before{content:"\f0329"}.mdi-layers-off-outline:before{content:"\f09fd"}.mdi-layers-outline:before{content:"\f09fe"}.mdi-layers-plus:before{content:"\f0e4d"}.mdi-layers-remove:before{content:"\f0e4e"}.mdi-layers-search:before{content:"\f1206"}.mdi-layers-search-outline:before{content:"\f1207"}.mdi-layers-triple:before{content:"\f0f58"}.mdi-layers-triple-outline:before{content:"\f0f59"}.mdi-lead-pencil:before{content:"\f064f"}.mdi-leaf:before{content:"\f032a"}.mdi-leaf-circle:before{content:"\f1905"}.mdi-leaf-circle-outline:before{content:"\f1906"}.mdi-leaf-maple:before{content:"\f0c93"}.mdi-leaf-maple-off:before{content:"\f12da"}.mdi-leaf-off:before{content:"\f12d9"}.mdi-leak:before{content:"\f0dd7"}.mdi-leak-off:before{content:"\f0dd8"}.mdi-lectern:before{content:"\f1af0"}.mdi-led-off:before{content:"\f032b"}.mdi-led-on:before{content:"\f032c"}.mdi-led-outline:before{content:"\f032d"}.mdi-led-strip:before{content:"\f07d6"}.mdi-led-strip-variant:before{content:"\f1051"}.mdi-led-strip-variant-off:before{content:"\f1a4b"}.mdi-led-variant-off:before{content:"\f032e"}.mdi-led-variant-on:before{content:"\f032f"}.mdi-led-variant-outline:before{content:"\f0330"}.mdi-leek:before{content:"\f117d"}.mdi-less-than:before{content:"\f097c"}.mdi-less-than-or-equal:before{content:"\f097d"}.mdi-library:before{content:"\f0331"}.mdi-library-outline:before{content:"\f1a22"}.mdi-library-shelves:before{content:"\f0ba9"}.mdi-license:before{content:"\f0fc3"}.mdi-lifebuoy:before{content:"\f087e"}.mdi-light-flood-down:before{content:"\f1987"}.mdi-light-flood-up:before{content:"\f1988"}.mdi-light-recessed:before{content:"\f179b"}.mdi-light-switch:before{content:"\f097e"}.mdi-light-switch-off:before{content:"\f1a24"}.mdi-lightbulb:before{content:"\f0335"}.mdi-lightbulb-alert:before{content:"\f19e1"}.mdi-lightbulb-alert-outline:before{content:"\f19e2"}.mdi-lightbulb-auto:before{content:"\f1800"}.mdi-lightbulb-auto-outline:before{content:"\f1801"}.mdi-lightbulb-cfl:before{content:"\f1208"}.mdi-lightbulb-cfl-off:before{content:"\f1209"}.mdi-lightbulb-cfl-spiral:before{content:"\f1275"}.mdi-lightbulb-cfl-spiral-off:before{content:"\f12c3"}.mdi-lightbulb-fluorescent-tube:before{content:"\f1804"}.mdi-lightbulb-fluorescent-tube-outline:before{content:"\f1805"}.mdi-lightbulb-group:before{content:"\f1253"}.mdi-lightbulb-group-off:before{content:"\f12cd"}.mdi-lightbulb-group-off-outline:before{content:"\f12ce"}.mdi-lightbulb-group-outline:before{content:"\f1254"}.mdi-lightbulb-multiple:before{content:"\f1255"}.mdi-lightbulb-multiple-off:before{content:"\f12cf"}.mdi-lightbulb-multiple-off-outline:before{content:"\f12d0"}.mdi-lightbulb-multiple-outline:before{content:"\f1256"}.mdi-lightbulb-night:before{content:"\f1a4c"}.mdi-lightbulb-night-outline:before{content:"\f1a4d"}.mdi-lightbulb-off:before{content:"\f0e4f"}.mdi-lightbulb-off-outline:before{content:"\f0e50"}.mdi-lightbulb-on:before{content:"\f06e8"}.mdi-lightbulb-on-10:before{content:"\f1a4e"}.mdi-lightbulb-on-20:before{content:"\f1a4f"}.mdi-lightbulb-on-30:before{content:"\f1a50"}.mdi-lightbulb-on-40:before{content:"\f1a51"}.mdi-lightbulb-on-50:before{content:"\f1a52"}.mdi-lightbulb-on-60:before{content:"\f1a53"}.mdi-lightbulb-on-70:before{content:"\f1a54"}.mdi-lightbulb-on-80:before{content:"\f1a55"}.mdi-lightbulb-on-90:before{content:"\f1a56"}.mdi-lightbulb-on-outline:before{content:"\f06e9"}.mdi-lightbulb-outline:before{content:"\f0336"}.mdi-lightbulb-question:before{content:"\f19e3"}.mdi-lightbulb-question-outline:before{content:"\f19e4"}.mdi-lightbulb-spot:before{content:"\f17f4"}.mdi-lightbulb-spot-off:before{content:"\f17f5"}.mdi-lightbulb-variant:before{content:"\f1802"}.mdi-lightbulb-variant-outline:before{content:"\f1803"}.mdi-lighthouse:before{content:"\f09ff"}.mdi-lighthouse-on:before{content:"\f0a00"}.mdi-lightning-bolt:before{content:"\f140b"}.mdi-lightning-bolt-circle:before{content:"\f0820"}.mdi-lightning-bolt-outline:before{content:"\f140c"}.mdi-line-scan:before{content:"\f0624"}.mdi-lingerie:before{content:"\f1476"}.mdi-link:before{content:"\f0337"}.mdi-link-box:before{content:"\f0d1a"}.mdi-link-box-outline:before{content:"\f0d1b"}.mdi-link-box-variant:before{content:"\f0d1c"}.mdi-link-box-variant-outline:before{content:"\f0d1d"}.mdi-link-circle:before{content:"\f1cac"}.mdi-link-circle-outline:before{content:"\f1cad"}.mdi-link-edit:before{content:"\f1cae"}.mdi-link-lock:before{content:"\f10ba"}.mdi-link-off:before{content:"\f0338"}.mdi-link-plus:before{content:"\f0c94"}.mdi-link-variant:before{content:"\f0339"}.mdi-link-variant-minus:before{content:"\f10ff"}.mdi-link-variant-off:before{content:"\f033a"}.mdi-link-variant-plus:before{content:"\f1100"}.mdi-link-variant-remove:before{content:"\f1101"}.mdi-linkedin:before{content:"\f033b"}.mdi-linux:before{content:"\f033d"}.mdi-linux-mint:before{content:"\f08ed"}.mdi-lipstick:before{content:"\f13b5"}.mdi-liquid-spot:before{content:"\f1826"}.mdi-liquor:before{content:"\f191e"}.mdi-list-box:before{content:"\f1b7b"}.mdi-list-box-outline:before{content:"\f1b7c"}.mdi-list-status:before{content:"\f15ab"}.mdi-litecoin:before{content:"\f0a61"}.mdi-loading:before{content:"\f0772"}.mdi-location-enter:before{content:"\f0fc4"}.mdi-location-exit:before{content:"\f0fc5"}.mdi-lock:before{content:"\f033e"}.mdi-lock-alert:before{content:"\f08ee"}.mdi-lock-alert-outline:before{content:"\f15d1"}.mdi-lock-check:before{content:"\f139a"}.mdi-lock-check-outline:before{content:"\f16a8"}.mdi-lock-clock:before{content:"\f097f"}.mdi-lock-minus:before{content:"\f16a9"}.mdi-lock-minus-outline:before{content:"\f16aa"}.mdi-lock-off:before{content:"\f1671"}.mdi-lock-off-outline:before{content:"\f1672"}.mdi-lock-open:before{content:"\f033f"}.mdi-lock-open-alert:before{content:"\f139b"}.mdi-lock-open-alert-outline:before{content:"\f15d2"}.mdi-lock-open-check:before{content:"\f139c"}.mdi-lock-open-check-outline:before{content:"\f16ab"}.mdi-lock-open-minus:before{content:"\f16ac"}.mdi-lock-open-minus-outline:before{content:"\f16ad"}.mdi-lock-open-outline:before{content:"\f0340"}.mdi-lock-open-plus:before{content:"\f16ae"}.mdi-lock-open-plus-outline:before{content:"\f16af"}.mdi-lock-open-remove:before{content:"\f16b0"}.mdi-lock-open-remove-outline:before{content:"\f16b1"}.mdi-lock-open-variant:before{content:"\f0fc6"}.mdi-lock-open-variant-outline:before{content:"\f0fc7"}.mdi-lock-outline:before{content:"\f0341"}.mdi-lock-pattern:before{content:"\f06ea"}.mdi-lock-percent:before{content:"\f1c12"}.mdi-lock-percent-open:before{content:"\f1c13"}.mdi-lock-percent-open-outline:before{content:"\f1c14"}.mdi-lock-percent-open-variant:before{content:"\f1c15"}.mdi-lock-percent-open-variant-outline:before{content:"\f1c16"}.mdi-lock-percent-outline:before{content:"\f1c17"}.mdi-lock-plus:before{content:"\f05fb"}.mdi-lock-plus-outline:before{content:"\f16b2"}.mdi-lock-question:before{content:"\f08ef"}.mdi-lock-remove:before{content:"\f16b3"}.mdi-lock-remove-outline:before{content:"\f16b4"}.mdi-lock-reset:before{content:"\f0773"}.mdi-lock-smart:before{content:"\f08b2"}.mdi-locker:before{content:"\f07d7"}.mdi-locker-multiple:before{content:"\f07d8"}.mdi-login:before{content:"\f0342"}.mdi-login-variant:before{content:"\f05fc"}.mdi-logout:before{content:"\f0343"}.mdi-logout-variant:before{content:"\f05fd"}.mdi-longitude:before{content:"\f0f5a"}.mdi-looks:before{content:"\f0344"}.mdi-lotion:before{content:"\f1582"}.mdi-lotion-outline:before{content:"\f1583"}.mdi-lotion-plus:before{content:"\f1584"}.mdi-lotion-plus-outline:before{content:"\f1585"}.mdi-loupe:before{content:"\f0345"}.mdi-lumx:before{content:"\f0346"}.mdi-lungs:before{content:"\f1084"}.mdi-mace:before{content:"\f1843"}.mdi-magazine-pistol:before{content:"\f0324"}.mdi-magazine-rifle:before{content:"\f0323"}.mdi-magic-staff:before{content:"\f1844"}.mdi-magnet:before{content:"\f0347"}.mdi-magnet-on:before{content:"\f0348"}.mdi-magnify:before{content:"\f0349"}.mdi-magnify-close:before{content:"\f0980"}.mdi-magnify-expand:before{content:"\f1874"}.mdi-magnify-minus:before{content:"\f034a"}.mdi-magnify-minus-cursor:before{content:"\f0a62"}.mdi-magnify-minus-outline:before{content:"\f06ec"}.mdi-magnify-plus:before{content:"\f034b"}.mdi-magnify-plus-cursor:before{content:"\f0a63"}.mdi-magnify-plus-outline:before{content:"\f06ed"}.mdi-magnify-remove-cursor:before{content:"\f120c"}.mdi-magnify-remove-outline:before{content:"\f120d"}.mdi-magnify-scan:before{content:"\f1276"}.mdi-mail:before{content:"\f0ebb"}.mdi-mailbox:before{content:"\f06ee"}.mdi-mailbox-open:before{content:"\f0d88"}.mdi-mailbox-open-outline:before{content:"\f0d89"}.mdi-mailbox-open-up:before{content:"\f0d8a"}.mdi-mailbox-open-up-outline:before{content:"\f0d8b"}.mdi-mailbox-outline:before{content:"\f0d8c"}.mdi-mailbox-up:before{content:"\f0d8d"}.mdi-mailbox-up-outline:before{content:"\f0d8e"}.mdi-manjaro:before{content:"\f160a"}.mdi-map:before{content:"\f034d"}.mdi-map-check:before{content:"\f0ebc"}.mdi-map-check-outline:before{content:"\f0ebd"}.mdi-map-clock:before{content:"\f0d1e"}.mdi-map-clock-outline:before{content:"\f0d1f"}.mdi-map-legend:before{content:"\f0a01"}.mdi-map-marker:before{content:"\f034e"}.mdi-map-marker-account:before{content:"\f18e3"}.mdi-map-marker-account-outline:before{content:"\f18e4"}.mdi-map-marker-alert:before{content:"\f0f05"}.mdi-map-marker-alert-outline:before{content:"\f0f06"}.mdi-map-marker-check:before{content:"\f0c95"}.mdi-map-marker-check-outline:before{content:"\f12fb"}.mdi-map-marker-circle:before{content:"\f034f"}.mdi-map-marker-distance:before{content:"\f08f0"}.mdi-map-marker-down:before{content:"\f1102"}.mdi-map-marker-left:before{content:"\f12db"}.mdi-map-marker-left-outline:before{content:"\f12dd"}.mdi-map-marker-minus:before{content:"\f0650"}.mdi-map-marker-minus-outline:before{content:"\f12f9"}.mdi-map-marker-multiple:before{content:"\f0350"}.mdi-map-marker-multiple-outline:before{content:"\f1277"}.mdi-map-marker-off:before{content:"\f0351"}.mdi-map-marker-off-outline:before{content:"\f12fd"}.mdi-map-marker-outline:before{content:"\f07d9"}.mdi-map-marker-path:before{content:"\f0d20"}.mdi-map-marker-plus:before{content:"\f0651"}.mdi-map-marker-plus-outline:before{content:"\f12f8"}.mdi-map-marker-question:before{content:"\f0f07"}.mdi-map-marker-question-outline:before{content:"\f0f08"}.mdi-map-marker-radius:before{content:"\f0352"}.mdi-map-marker-radius-outline:before{content:"\f12fc"}.mdi-map-marker-remove:before{content:"\f0f09"}.mdi-map-marker-remove-outline:before{content:"\f12fa"}.mdi-map-marker-remove-variant:before{content:"\f0f0a"}.mdi-map-marker-right:before{content:"\f12dc"}.mdi-map-marker-right-outline:before{content:"\f12de"}.mdi-map-marker-star:before{content:"\f1608"}.mdi-map-marker-star-outline:before{content:"\f1609"}.mdi-map-marker-up:before{content:"\f1103"}.mdi-map-minus:before{content:"\f0981"}.mdi-map-outline:before{content:"\f0982"}.mdi-map-plus:before{content:"\f0983"}.mdi-map-search:before{content:"\f0984"}.mdi-map-search-outline:before{content:"\f0985"}.mdi-mapbox:before{content:"\f0baa"}.mdi-margin:before{content:"\f0353"}.mdi-marker:before{content:"\f0652"}.mdi-marker-cancel:before{content:"\f0dd9"}.mdi-marker-check:before{content:"\f0355"}.mdi-mastodon:before{content:"\f0ad1"}.mdi-material-design:before{content:"\f0986"}.mdi-material-ui:before{content:"\f0357"}.mdi-math-compass:before{content:"\f0358"}.mdi-math-cos:before{content:"\f0c96"}.mdi-math-integral:before{content:"\f0fc8"}.mdi-math-integral-box:before{content:"\f0fc9"}.mdi-math-log:before{content:"\f1085"}.mdi-math-norm:before{content:"\f0fca"}.mdi-math-norm-box:before{content:"\f0fcb"}.mdi-math-sin:before{content:"\f0c97"}.mdi-math-tan:before{content:"\f0c98"}.mdi-matrix:before{content:"\f0628"}.mdi-medal:before{content:"\f0987"}.mdi-medal-outline:before{content:"\f1326"}.mdi-medical-bag:before{content:"\f06ef"}.mdi-medical-cotton-swab:before{content:"\f1ab8"}.mdi-medication:before{content:"\f1b14"}.mdi-medication-outline:before{content:"\f1b15"}.mdi-meditation:before{content:"\f117b"}.mdi-memory:before{content:"\f035b"}.mdi-memory-arrow-down:before{content:"\f1ca6"}.mdi-menorah:before{content:"\f17d4"}.mdi-menorah-fire:before{content:"\f17d5"}.mdi-menu:before{content:"\f035c"}.mdi-menu-close:before{content:"\f1c90"}.mdi-menu-down:before{content:"\f035d"}.mdi-menu-down-outline:before{content:"\f06b6"}.mdi-menu-left:before{content:"\f035e"}.mdi-menu-left-outline:before{content:"\f0a02"}.mdi-menu-open:before{content:"\f0bab"}.mdi-menu-right:before{content:"\f035f"}.mdi-menu-right-outline:before{content:"\f0a03"}.mdi-menu-swap:before{content:"\f0a64"}.mdi-menu-swap-outline:before{content:"\f0a65"}.mdi-menu-up:before{content:"\f0360"}.mdi-menu-up-outline:before{content:"\f06b7"}.mdi-merge:before{content:"\f0f5c"}.mdi-message:before{content:"\f0361"}.mdi-message-alert:before{content:"\f0362"}.mdi-message-alert-outline:before{content:"\f0a04"}.mdi-message-arrow-left:before{content:"\f12f2"}.mdi-message-arrow-left-outline:before{content:"\f12f3"}.mdi-message-arrow-right:before{content:"\f12f4"}.mdi-message-arrow-right-outline:before{content:"\f12f5"}.mdi-message-badge:before{content:"\f1941"}.mdi-message-badge-outline:before{content:"\f1942"}.mdi-message-bookmark:before{content:"\f15ac"}.mdi-message-bookmark-outline:before{content:"\f15ad"}.mdi-message-bulleted:before{content:"\f06a2"}.mdi-message-bulleted-off:before{content:"\f06a3"}.mdi-message-check:before{content:"\f1b8a"}.mdi-message-check-outline:before{content:"\f1b8b"}.mdi-message-cog:before{content:"\f06f1"}.mdi-message-cog-outline:before{content:"\f1172"}.mdi-message-draw:before{content:"\f0363"}.mdi-message-fast:before{content:"\f19cc"}.mdi-message-fast-outline:before{content:"\f19cd"}.mdi-message-flash:before{content:"\f15a9"}.mdi-message-flash-outline:before{content:"\f15aa"}.mdi-message-image:before{content:"\f0364"}.mdi-message-image-outline:before{content:"\f116c"}.mdi-message-lock:before{content:"\f0fcc"}.mdi-message-lock-outline:before{content:"\f116d"}.mdi-message-minus:before{content:"\f116e"}.mdi-message-minus-outline:before{content:"\f116f"}.mdi-message-off:before{content:"\f164d"}.mdi-message-off-outline:before{content:"\f164e"}.mdi-message-outline:before{content:"\f0365"}.mdi-message-plus:before{content:"\f0653"}.mdi-message-plus-outline:before{content:"\f10bb"}.mdi-message-processing:before{content:"\f0366"}.mdi-message-processing-outline:before{content:"\f1170"}.mdi-message-question:before{content:"\f173a"}.mdi-message-question-outline:before{content:"\f173b"}.mdi-message-reply:before{content:"\f0367"}.mdi-message-reply-outline:before{content:"\f173d"}.mdi-message-reply-text:before{content:"\f0368"}.mdi-message-reply-text-outline:before{content:"\f173e"}.mdi-message-settings:before{content:"\f06f0"}.mdi-message-settings-outline:before{content:"\f1171"}.mdi-message-star:before{content:"\f069a"}.mdi-message-star-outline:before{content:"\f1250"}.mdi-message-text:before{content:"\f0369"}.mdi-message-text-clock:before{content:"\f1173"}.mdi-message-text-clock-outline:before{content:"\f1174"}.mdi-message-text-fast:before{content:"\f19ce"}.mdi-message-text-fast-outline:before{content:"\f19cf"}.mdi-message-text-lock:before{content:"\f0fcd"}.mdi-message-text-lock-outline:before{content:"\f1175"}.mdi-message-text-outline:before{content:"\f036a"}.mdi-message-video:before{content:"\f036b"}.mdi-meteor:before{content:"\f0629"}.mdi-meter-electric:before{content:"\f1a57"}.mdi-meter-electric-outline:before{content:"\f1a58"}.mdi-meter-gas:before{content:"\f1a59"}.mdi-meter-gas-outline:before{content:"\f1a5a"}.mdi-metronome:before{content:"\f07da"}.mdi-metronome-tick:before{content:"\f07db"}.mdi-micro-sd:before{content:"\f07dc"}.mdi-microphone:before{content:"\f036c"}.mdi-microphone-message:before{content:"\f050a"}.mdi-microphone-message-off:before{content:"\f050b"}.mdi-microphone-minus:before{content:"\f08b3"}.mdi-microphone-off:before{content:"\f036d"}.mdi-microphone-outline:before{content:"\f036e"}.mdi-microphone-plus:before{content:"\f08b4"}.mdi-microphone-question:before{content:"\f1989"}.mdi-microphone-question-outline:before{content:"\f198a"}.mdi-microphone-settings:before{content:"\f036f"}.mdi-microphone-variant:before{content:"\f0370"}.mdi-microphone-variant-off:before{content:"\f0371"}.mdi-microscope:before{content:"\f0654"}.mdi-microsoft:before{content:"\f0372"}.mdi-microsoft-access:before{content:"\f138e"}.mdi-microsoft-azure:before{content:"\f0805"}.mdi-microsoft-azure-devops:before{content:"\f0fd5"}.mdi-microsoft-bing:before{content:"\f00a4"}.mdi-microsoft-dynamics-365:before{content:"\f0988"}.mdi-microsoft-edge:before{content:"\f01e9"}.mdi-microsoft-excel:before{content:"\f138f"}.mdi-microsoft-internet-explorer:before{content:"\f0300"}.mdi-microsoft-office:before{content:"\f03c6"}.mdi-microsoft-onedrive:before{content:"\f03ca"}.mdi-microsoft-onenote:before{content:"\f0747"}.mdi-microsoft-outlook:before{content:"\f0d22"}.mdi-microsoft-powerpoint:before{content:"\f1390"}.mdi-microsoft-sharepoint:before{content:"\f1391"}.mdi-microsoft-teams:before{content:"\f02bb"}.mdi-microsoft-visual-studio:before{content:"\f0610"}.mdi-microsoft-visual-studio-code:before{content:"\f0a1e"}.mdi-microsoft-windows:before{content:"\f05b3"}.mdi-microsoft-windows-classic:before{content:"\f0a21"}.mdi-microsoft-word:before{content:"\f1392"}.mdi-microsoft-xbox:before{content:"\f05b9"}.mdi-microsoft-xbox-controller:before{content:"\f05ba"}.mdi-microsoft-xbox-controller-battery-alert:before{content:"\f074b"}.mdi-microsoft-xbox-controller-battery-charging:before{content:"\f0a22"}.mdi-microsoft-xbox-controller-battery-empty:before{content:"\f074c"}.mdi-microsoft-xbox-controller-battery-full:before{content:"\f074d"}.mdi-microsoft-xbox-controller-battery-low:before{content:"\f074e"}.mdi-microsoft-xbox-controller-battery-medium:before{content:"\f074f"}.mdi-microsoft-xbox-controller-battery-unknown:before{content:"\f0750"}.mdi-microsoft-xbox-controller-menu:before{content:"\f0e6f"}.mdi-microsoft-xbox-controller-off:before{content:"\f05bb"}.mdi-microsoft-xbox-controller-view:before{content:"\f0e70"}.mdi-microwave:before{content:"\f0c99"}.mdi-microwave-off:before{content:"\f1423"}.mdi-middleware:before{content:"\f0f5d"}.mdi-middleware-outline:before{content:"\f0f5e"}.mdi-midi:before{content:"\f08f1"}.mdi-midi-port:before{content:"\f08f2"}.mdi-mine:before{content:"\f0dda"}.mdi-minecraft:before{content:"\f0373"}.mdi-mini-sd:before{content:"\f0a05"}.mdi-minidisc:before{content:"\f0a06"}.mdi-minus:before{content:"\f0374"}.mdi-minus-box:before{content:"\f0375"}.mdi-minus-box-multiple:before{content:"\f1141"}.mdi-minus-box-multiple-outline:before{content:"\f1142"}.mdi-minus-box-outline:before{content:"\f06f2"}.mdi-minus-circle:before{content:"\f0376"}.mdi-minus-circle-multiple:before{content:"\f035a"}.mdi-minus-circle-multiple-outline:before{content:"\f0ad3"}.mdi-minus-circle-off:before{content:"\f1459"}.mdi-minus-circle-off-outline:before{content:"\f145a"}.mdi-minus-circle-outline:before{content:"\f0377"}.mdi-minus-network:before{content:"\f0378"}.mdi-minus-network-outline:before{content:"\f0c9a"}.mdi-minus-thick:before{content:"\f1639"}.mdi-mirror:before{content:"\f11fd"}.mdi-mirror-rectangle:before{content:"\f179f"}.mdi-mirror-variant:before{content:"\f17a0"}.mdi-mixed-martial-arts:before{content:"\f0d8f"}.mdi-mixed-reality:before{content:"\f087f"}.mdi-molecule:before{content:"\f0bac"}.mdi-molecule-co:before{content:"\f12fe"}.mdi-molecule-co2:before{content:"\f07e4"}.mdi-monitor:before{content:"\f0379"}.mdi-monitor-account:before{content:"\f1a5b"}.mdi-monitor-arrow-down:before{content:"\f19d0"}.mdi-monitor-arrow-down-variant:before{content:"\f19d1"}.mdi-monitor-cellphone:before{content:"\f0989"}.mdi-monitor-cellphone-star:before{content:"\f098a"}.mdi-monitor-dashboard:before{content:"\f0a07"}.mdi-monitor-edit:before{content:"\f12c6"}.mdi-monitor-eye:before{content:"\f13b4"}.mdi-monitor-lock:before{content:"\f0ddb"}.mdi-monitor-multiple:before{content:"\f037a"}.mdi-monitor-off:before{content:"\f0d90"}.mdi-monitor-screenshot:before{content:"\f0e51"}.mdi-monitor-share:before{content:"\f1483"}.mdi-monitor-shimmer:before{content:"\f1104"}.mdi-monitor-small:before{content:"\f1876"}.mdi-monitor-speaker:before{content:"\f0f5f"}.mdi-monitor-speaker-off:before{content:"\f0f60"}.mdi-monitor-star:before{content:"\f0ddc"}.mdi-monitor-vertical:before{content:"\f1c33"}.mdi-moon-first-quarter:before{content:"\f0f61"}.mdi-moon-full:before{content:"\f0f62"}.mdi-moon-last-quarter:before{content:"\f0f63"}.mdi-moon-new:before{content:"\f0f64"}.mdi-moon-waning-crescent:before{content:"\f0f65"}.mdi-moon-waning-gibbous:before{content:"\f0f66"}.mdi-moon-waxing-crescent:before{content:"\f0f67"}.mdi-moon-waxing-gibbous:before{content:"\f0f68"}.mdi-moped:before{content:"\f1086"}.mdi-moped-electric:before{content:"\f15b7"}.mdi-moped-electric-outline:before{content:"\f15b8"}.mdi-moped-outline:before{content:"\f15b9"}.mdi-more:before{content:"\f037b"}.mdi-mortar-pestle:before{content:"\f1748"}.mdi-mortar-pestle-plus:before{content:"\f03f1"}.mdi-mosque:before{content:"\f0d45"}.mdi-mosque-outline:before{content:"\f1827"}.mdi-mother-heart:before{content:"\f1314"}.mdi-mother-nurse:before{content:"\f0d21"}.mdi-motion:before{content:"\f15b2"}.mdi-motion-outline:before{content:"\f15b3"}.mdi-motion-pause:before{content:"\f1590"}.mdi-motion-pause-outline:before{content:"\f1592"}.mdi-motion-play:before{content:"\f158f"}.mdi-motion-play-outline:before{content:"\f1591"}.mdi-motion-sensor:before{content:"\f0d91"}.mdi-motion-sensor-off:before{content:"\f1435"}.mdi-motorbike:before{content:"\f037c"}.mdi-motorbike-electric:before{content:"\f15ba"}.mdi-motorbike-off:before{content:"\f1b16"}.mdi-mouse:before{content:"\f037d"}.mdi-mouse-bluetooth:before{content:"\f098b"}.mdi-mouse-left-click:before{content:"\f1d07"}.mdi-mouse-left-click-outline:before{content:"\f1d08"}.mdi-mouse-move-down:before{content:"\f1550"}.mdi-mouse-move-up:before{content:"\f1551"}.mdi-mouse-move-vertical:before{content:"\f1552"}.mdi-mouse-off:before{content:"\f037e"}.mdi-mouse-outline:before{content:"\f1d09"}.mdi-mouse-right-click:before{content:"\f1d0a"}.mdi-mouse-right-click-outline:before{content:"\f1d0b"}.mdi-mouse-scroll-wheel:before{content:"\f1d0c"}.mdi-mouse-variant:before{content:"\f037f"}.mdi-mouse-variant-off:before{content:"\f0380"}.mdi-move-resize:before{content:"\f0655"}.mdi-move-resize-variant:before{content:"\f0656"}.mdi-movie:before{content:"\f0381"}.mdi-movie-check:before{content:"\f16f3"}.mdi-movie-check-outline:before{content:"\f16f4"}.mdi-movie-cog:before{content:"\f16f5"}.mdi-movie-cog-outline:before{content:"\f16f6"}.mdi-movie-edit:before{content:"\f1122"}.mdi-movie-edit-outline:before{content:"\f1123"}.mdi-movie-filter:before{content:"\f1124"}.mdi-movie-filter-outline:before{content:"\f1125"}.mdi-movie-minus:before{content:"\f16f7"}.mdi-movie-minus-outline:before{content:"\f16f8"}.mdi-movie-off:before{content:"\f16f9"}.mdi-movie-off-outline:before{content:"\f16fa"}.mdi-movie-open:before{content:"\f0fce"}.mdi-movie-open-check:before{content:"\f16fb"}.mdi-movie-open-check-outline:before{content:"\f16fc"}.mdi-movie-open-cog:before{content:"\f16fd"}.mdi-movie-open-cog-outline:before{content:"\f16fe"}.mdi-movie-open-edit:before{content:"\f16ff"}.mdi-movie-open-edit-outline:before{content:"\f1700"}.mdi-movie-open-minus:before{content:"\f1701"}.mdi-movie-open-minus-outline:before{content:"\f1702"}.mdi-movie-open-off:before{content:"\f1703"}.mdi-movie-open-off-outline:before{content:"\f1704"}.mdi-movie-open-outline:before{content:"\f0fcf"}.mdi-movie-open-play:before{content:"\f1705"}.mdi-movie-open-play-outline:before{content:"\f1706"}.mdi-movie-open-plus:before{content:"\f1707"}.mdi-movie-open-plus-outline:before{content:"\f1708"}.mdi-movie-open-remove:before{content:"\f1709"}.mdi-movie-open-remove-outline:before{content:"\f170a"}.mdi-movie-open-settings:before{content:"\f170b"}.mdi-movie-open-settings-outline:before{content:"\f170c"}.mdi-movie-open-star:before{content:"\f170d"}.mdi-movie-open-star-outline:before{content:"\f170e"}.mdi-movie-outline:before{content:"\f0ddd"}.mdi-movie-play:before{content:"\f170f"}.mdi-movie-play-outline:before{content:"\f1710"}.mdi-movie-plus:before{content:"\f1711"}.mdi-movie-plus-outline:before{content:"\f1712"}.mdi-movie-remove:before{content:"\f1713"}.mdi-movie-remove-outline:before{content:"\f1714"}.mdi-movie-roll:before{content:"\f07de"}.mdi-movie-search:before{content:"\f11d2"}.mdi-movie-search-outline:before{content:"\f11d3"}.mdi-movie-settings:before{content:"\f1715"}.mdi-movie-settings-outline:before{content:"\f1716"}.mdi-movie-star:before{content:"\f1717"}.mdi-movie-star-outline:before{content:"\f1718"}.mdi-mower:before{content:"\f166f"}.mdi-mower-bag:before{content:"\f1670"}.mdi-mower-bag-on:before{content:"\f1b60"}.mdi-mower-on:before{content:"\f1b5f"}.mdi-muffin:before{content:"\f098c"}.mdi-multicast:before{content:"\f1893"}.mdi-multimedia:before{content:"\f1b97"}.mdi-multiplication:before{content:"\f0382"}.mdi-multiplication-box:before{content:"\f0383"}.mdi-mushroom:before{content:"\f07df"}.mdi-mushroom-off:before{content:"\f13fa"}.mdi-mushroom-off-outline:before{content:"\f13fb"}.mdi-mushroom-outline:before{content:"\f07e0"}.mdi-music:before{content:"\f075a"}.mdi-music-accidental-double-flat:before{content:"\f0f69"}.mdi-music-accidental-double-sharp:before{content:"\f0f6a"}.mdi-music-accidental-flat:before{content:"\f0f6b"}.mdi-music-accidental-natural:before{content:"\f0f6c"}.mdi-music-accidental-sharp:before{content:"\f0f6d"}.mdi-music-box:before{content:"\f0384"}.mdi-music-box-multiple:before{content:"\f0333"}.mdi-music-box-multiple-outline:before{content:"\f0f04"}.mdi-music-box-outline:before{content:"\f0385"}.mdi-music-circle:before{content:"\f0386"}.mdi-music-circle-outline:before{content:"\f0ad4"}.mdi-music-clef-alto:before{content:"\f0f6e"}.mdi-music-clef-bass:before{content:"\f0f6f"}.mdi-music-clef-treble:before{content:"\f0f70"}.mdi-music-note:before{content:"\f0387"}.mdi-music-note-bluetooth:before{content:"\f05fe"}.mdi-music-note-bluetooth-off:before{content:"\f05ff"}.mdi-music-note-eighth:before{content:"\f0388"}.mdi-music-note-eighth-dotted:before{content:"\f0f71"}.mdi-music-note-half:before{content:"\f0389"}.mdi-music-note-half-dotted:before{content:"\f0f72"}.mdi-music-note-minus:before{content:"\f1b89"}.mdi-music-note-off:before{content:"\f038a"}.mdi-music-note-off-outline:before{content:"\f0f73"}.mdi-music-note-outline:before{content:"\f0f74"}.mdi-music-note-plus:before{content:"\f0dde"}.mdi-music-note-quarter:before{content:"\f038b"}.mdi-music-note-quarter-dotted:before{content:"\f0f75"}.mdi-music-note-sixteenth:before{content:"\f038c"}.mdi-music-note-sixteenth-dotted:before{content:"\f0f76"}.mdi-music-note-whole:before{content:"\f038d"}.mdi-music-note-whole-dotted:before{content:"\f0f77"}.mdi-music-off:before{content:"\f075b"}.mdi-music-rest-eighth:before{content:"\f0f78"}.mdi-music-rest-half:before{content:"\f0f79"}.mdi-music-rest-quarter:before{content:"\f0f7a"}.mdi-music-rest-sixteenth:before{content:"\f0f7b"}.mdi-music-rest-whole:before{content:"\f0f7c"}.mdi-mustache:before{content:"\f15de"}.mdi-nail:before{content:"\f0ddf"}.mdi-nas:before{content:"\f08f3"}.mdi-nativescript:before{content:"\f0880"}.mdi-nature:before{content:"\f038e"}.mdi-nature-outline:before{content:"\f1c71"}.mdi-nature-people:before{content:"\f038f"}.mdi-nature-people-outline:before{content:"\f1c72"}.mdi-navigation:before{content:"\f0390"}.mdi-navigation-outline:before{content:"\f1607"}.mdi-navigation-variant:before{content:"\f18f0"}.mdi-navigation-variant-outline:before{content:"\f18f1"}.mdi-near-me:before{content:"\f05cd"}.mdi-necklace:before{content:"\f0f0b"}.mdi-needle:before{content:"\f0391"}.mdi-needle-off:before{content:"\f19d2"}.mdi-netflix:before{content:"\f0746"}.mdi-network:before{content:"\f06f3"}.mdi-network-off:before{content:"\f0c9b"}.mdi-network-off-outline:before{content:"\f0c9c"}.mdi-network-outline:before{content:"\f0c9d"}.mdi-network-pos:before{content:"\f1acb"}.mdi-network-strength-1:before{content:"\f08f4"}.mdi-network-strength-1-alert:before{content:"\f08f5"}.mdi-network-strength-2:before{content:"\f08f6"}.mdi-network-strength-2-alert:before{content:"\f08f7"}.mdi-network-strength-3:before{content:"\f08f8"}.mdi-network-strength-3-alert:before{content:"\f08f9"}.mdi-network-strength-4:before{content:"\f08fa"}.mdi-network-strength-4-alert:before{content:"\f08fb"}.mdi-network-strength-4-cog:before{content:"\f191a"}.mdi-network-strength-off:before{content:"\f08fc"}.mdi-network-strength-off-outline:before{content:"\f08fd"}.mdi-network-strength-outline:before{content:"\f08fe"}.mdi-new-box:before{content:"\f0394"}.mdi-newspaper:before{content:"\f0395"}.mdi-newspaper-check:before{content:"\f1943"}.mdi-newspaper-minus:before{content:"\f0f0c"}.mdi-newspaper-plus:before{content:"\f0f0d"}.mdi-newspaper-remove:before{content:"\f1944"}.mdi-newspaper-variant:before{content:"\f1001"}.mdi-newspaper-variant-multiple:before{content:"\f1002"}.mdi-newspaper-variant-multiple-outline:before{content:"\f1003"}.mdi-newspaper-variant-outline:before{content:"\f1004"}.mdi-nfc:before{content:"\f0396"}.mdi-nfc-search-variant:before{content:"\f0e53"}.mdi-nfc-tap:before{content:"\f0397"}.mdi-nfc-variant:before{content:"\f0398"}.mdi-nfc-variant-off:before{content:"\f0e54"}.mdi-ninja:before{content:"\f0774"}.mdi-nintendo-game-boy:before{content:"\f1393"}.mdi-nintendo-switch:before{content:"\f07e1"}.mdi-nintendo-wii:before{content:"\f05ab"}.mdi-nintendo-wiiu:before{content:"\f072d"}.mdi-nix:before{content:"\f1105"}.mdi-nodejs:before{content:"\f0399"}.mdi-noodles:before{content:"\f117e"}.mdi-not-equal:before{content:"\f098d"}.mdi-not-equal-variant:before{content:"\f098e"}.mdi-note:before{content:"\f039a"}.mdi-note-alert:before{content:"\f177d"}.mdi-note-alert-outline:before{content:"\f177e"}.mdi-note-check:before{content:"\f177f"}.mdi-note-check-outline:before{content:"\f1780"}.mdi-note-edit:before{content:"\f1781"}.mdi-note-edit-outline:before{content:"\f1782"}.mdi-note-minus:before{content:"\f164f"}.mdi-note-minus-outline:before{content:"\f1650"}.mdi-note-multiple:before{content:"\f06b8"}.mdi-note-multiple-outline:before{content:"\f06b9"}.mdi-note-off:before{content:"\f1783"}.mdi-note-off-outline:before{content:"\f1784"}.mdi-note-outline:before{content:"\f039b"}.mdi-note-plus:before{content:"\f039c"}.mdi-note-plus-outline:before{content:"\f039d"}.mdi-note-remove:before{content:"\f1651"}.mdi-note-remove-outline:before{content:"\f1652"}.mdi-note-search:before{content:"\f1653"}.mdi-note-search-outline:before{content:"\f1654"}.mdi-note-text:before{content:"\f039e"}.mdi-note-text-outline:before{content:"\f11d7"}.mdi-notebook:before{content:"\f082e"}.mdi-notebook-check:before{content:"\f14f5"}.mdi-notebook-check-outline:before{content:"\f14f6"}.mdi-notebook-edit:before{content:"\f14e7"}.mdi-notebook-edit-outline:before{content:"\f14e9"}.mdi-notebook-heart:before{content:"\f1a0b"}.mdi-notebook-heart-outline:before{content:"\f1a0c"}.mdi-notebook-minus:before{content:"\f1610"}.mdi-notebook-minus-outline:before{content:"\f1611"}.mdi-notebook-multiple:before{content:"\f0e55"}.mdi-notebook-outline:before{content:"\f0ebf"}.mdi-notebook-plus:before{content:"\f1612"}.mdi-notebook-plus-outline:before{content:"\f1613"}.mdi-notebook-remove:before{content:"\f1614"}.mdi-notebook-remove-outline:before{content:"\f1615"}.mdi-notification-clear-all:before{content:"\f039f"}.mdi-npm:before{content:"\f06f7"}.mdi-nuke:before{content:"\f06a4"}.mdi-null:before{content:"\f07e2"}.mdi-numeric:before{content:"\f03a0"}.mdi-numeric-0:before{content:"\f0b39"}.mdi-numeric-0-box:before{content:"\f03a1"}.mdi-numeric-0-box-multiple:before{content:"\f0f0e"}.mdi-numeric-0-box-multiple-outline:before{content:"\f03a2"}.mdi-numeric-0-box-outline:before{content:"\f03a3"}.mdi-numeric-0-circle:before{content:"\f0c9e"}.mdi-numeric-0-circle-outline:before{content:"\f0c9f"}.mdi-numeric-1:before{content:"\f0b3a"}.mdi-numeric-1-box:before{content:"\f03a4"}.mdi-numeric-1-box-multiple:before{content:"\f0f0f"}.mdi-numeric-1-box-multiple-outline:before{content:"\f03a5"}.mdi-numeric-1-box-outline:before{content:"\f03a6"}.mdi-numeric-1-circle:before{content:"\f0ca0"}.mdi-numeric-1-circle-outline:before{content:"\f0ca1"}.mdi-numeric-10:before{content:"\f0fe9"}.mdi-numeric-10-box:before{content:"\f0f7d"}.mdi-numeric-10-box-multiple:before{content:"\f0fea"}.mdi-numeric-10-box-multiple-outline:before{content:"\f0feb"}.mdi-numeric-10-box-outline:before{content:"\f0f7e"}.mdi-numeric-10-circle:before{content:"\f0fec"}.mdi-numeric-10-circle-outline:before{content:"\f0fed"}.mdi-numeric-2:before{content:"\f0b3b"}.mdi-numeric-2-box:before{content:"\f03a7"}.mdi-numeric-2-box-multiple:before{content:"\f0f10"}.mdi-numeric-2-box-multiple-outline:before{content:"\f03a8"}.mdi-numeric-2-box-outline:before{content:"\f03a9"}.mdi-numeric-2-circle:before{content:"\f0ca2"}.mdi-numeric-2-circle-outline:before{content:"\f0ca3"}.mdi-numeric-3:before{content:"\f0b3c"}.mdi-numeric-3-box:before{content:"\f03aa"}.mdi-numeric-3-box-multiple:before{content:"\f0f11"}.mdi-numeric-3-box-multiple-outline:before{content:"\f03ab"}.mdi-numeric-3-box-outline:before{content:"\f03ac"}.mdi-numeric-3-circle:before{content:"\f0ca4"}.mdi-numeric-3-circle-outline:before{content:"\f0ca5"}.mdi-numeric-4:before{content:"\f0b3d"}.mdi-numeric-4-box:before{content:"\f03ad"}.mdi-numeric-4-box-multiple:before{content:"\f0f12"}.mdi-numeric-4-box-multiple-outline:before{content:"\f03b2"}.mdi-numeric-4-box-outline:before{content:"\f03ae"}.mdi-numeric-4-circle:before{content:"\f0ca6"}.mdi-numeric-4-circle-outline:before{content:"\f0ca7"}.mdi-numeric-5:before{content:"\f0b3e"}.mdi-numeric-5-box:before{content:"\f03b1"}.mdi-numeric-5-box-multiple:before{content:"\f0f13"}.mdi-numeric-5-box-multiple-outline:before{content:"\f03af"}.mdi-numeric-5-box-outline:before{content:"\f03b0"}.mdi-numeric-5-circle:before{content:"\f0ca8"}.mdi-numeric-5-circle-outline:before{content:"\f0ca9"}.mdi-numeric-6:before{content:"\f0b3f"}.mdi-numeric-6-box:before{content:"\f03b3"}.mdi-numeric-6-box-multiple:before{content:"\f0f14"}.mdi-numeric-6-box-multiple-outline:before{content:"\f03b4"}.mdi-numeric-6-box-outline:before{content:"\f03b5"}.mdi-numeric-6-circle:before{content:"\f0caa"}.mdi-numeric-6-circle-outline:before{content:"\f0cab"}.mdi-numeric-7:before{content:"\f0b40"}.mdi-numeric-7-box:before{content:"\f03b6"}.mdi-numeric-7-box-multiple:before{content:"\f0f15"}.mdi-numeric-7-box-multiple-outline:before{content:"\f03b7"}.mdi-numeric-7-box-outline:before{content:"\f03b8"}.mdi-numeric-7-circle:before{content:"\f0cac"}.mdi-numeric-7-circle-outline:before{content:"\f0cad"}.mdi-numeric-8:before{content:"\f0b41"}.mdi-numeric-8-box:before{content:"\f03b9"}.mdi-numeric-8-box-multiple:before{content:"\f0f16"}.mdi-numeric-8-box-multiple-outline:before{content:"\f03ba"}.mdi-numeric-8-box-outline:before{content:"\f03bb"}.mdi-numeric-8-circle:before{content:"\f0cae"}.mdi-numeric-8-circle-outline:before{content:"\f0caf"}.mdi-numeric-9:before{content:"\f0b42"}.mdi-numeric-9-box:before{content:"\f03bc"}.mdi-numeric-9-box-multiple:before{content:"\f0f17"}.mdi-numeric-9-box-multiple-outline:before{content:"\f03bd"}.mdi-numeric-9-box-outline:before{content:"\f03be"}.mdi-numeric-9-circle:before{content:"\f0cb0"}.mdi-numeric-9-circle-outline:before{content:"\f0cb1"}.mdi-numeric-9-plus:before{content:"\f0fee"}.mdi-numeric-9-plus-box:before{content:"\f03bf"}.mdi-numeric-9-plus-box-multiple:before{content:"\f0f18"}.mdi-numeric-9-plus-box-multiple-outline:before{content:"\f03c0"}.mdi-numeric-9-plus-box-outline:before{content:"\f03c1"}.mdi-numeric-9-plus-circle:before{content:"\f0cb2"}.mdi-numeric-9-plus-circle-outline:before{content:"\f0cb3"}.mdi-numeric-negative-1:before{content:"\f1052"}.mdi-numeric-off:before{content:"\f19d3"}.mdi-numeric-positive-1:before{content:"\f15cb"}.mdi-nut:before{content:"\f06f8"}.mdi-nutrition:before{content:"\f03c2"}.mdi-nuxt:before{content:"\f1106"}.mdi-oar:before{content:"\f067c"}.mdi-ocarina:before{content:"\f0de0"}.mdi-oci:before{content:"\f12e9"}.mdi-ocr:before{content:"\f113a"}.mdi-octagon:before{content:"\f03c3"}.mdi-octagon-outline:before{content:"\f03c4"}.mdi-octagram:before{content:"\f06f9"}.mdi-octagram-edit:before{content:"\f1c34"}.mdi-octagram-edit-outline:before{content:"\f1c35"}.mdi-octagram-minus:before{content:"\f1c36"}.mdi-octagram-minus-outline:before{content:"\f1c37"}.mdi-octagram-outline:before{content:"\f0775"}.mdi-octagram-plus:before{content:"\f1c38"}.mdi-octagram-plus-outline:before{content:"\f1c39"}.mdi-octahedron:before{content:"\f1950"}.mdi-octahedron-off:before{content:"\f1951"}.mdi-odnoklassniki:before{content:"\f03c5"}.mdi-offer:before{content:"\f121b"}.mdi-office-building:before{content:"\f0991"}.mdi-office-building-cog:before{content:"\f1949"}.mdi-office-building-cog-outline:before{content:"\f194a"}.mdi-office-building-marker:before{content:"\f1520"}.mdi-office-building-marker-outline:before{content:"\f1521"}.mdi-office-building-minus:before{content:"\f1baa"}.mdi-office-building-minus-outline:before{content:"\f1bab"}.mdi-office-building-outline:before{content:"\f151f"}.mdi-office-building-plus:before{content:"\f1ba8"}.mdi-office-building-plus-outline:before{content:"\f1ba9"}.mdi-office-building-remove:before{content:"\f1bac"}.mdi-office-building-remove-outline:before{content:"\f1bad"}.mdi-oil:before{content:"\f03c7"}.mdi-oil-lamp:before{content:"\f0f19"}.mdi-oil-level:before{content:"\f1053"}.mdi-oil-temperature:before{content:"\f0ff8"}.mdi-om:before{content:"\f0973"}.mdi-omega:before{content:"\f03c9"}.mdi-one-up:before{content:"\f0bad"}.mdi-onepassword:before{content:"\f0881"}.mdi-opacity:before{content:"\f05cc"}.mdi-open-in-app:before{content:"\f03cb"}.mdi-open-in-new:before{content:"\f03cc"}.mdi-open-source-initiative:before{content:"\f0bae"}.mdi-openid:before{content:"\f03cd"}.mdi-opera:before{content:"\f03ce"}.mdi-orbit:before{content:"\f0018"}.mdi-orbit-variant:before{content:"\f15db"}.mdi-order-alphabetical-ascending:before{content:"\f020d"}.mdi-order-alphabetical-descending:before{content:"\f0d07"}.mdi-order-bool-ascending:before{content:"\f02be"}.mdi-order-bool-ascending-variant:before{content:"\f098f"}.mdi-order-bool-descending:before{content:"\f1384"}.mdi-order-bool-descending-variant:before{content:"\f0990"}.mdi-order-numeric-ascending:before{content:"\f0545"}.mdi-order-numeric-descending:before{content:"\f0546"}.mdi-origin:before{content:"\f0b43"}.mdi-ornament:before{content:"\f03cf"}.mdi-ornament-variant:before{content:"\f03d0"}.mdi-outdoor-lamp:before{content:"\f1054"}.mdi-overscan:before{content:"\f1005"}.mdi-owl:before{content:"\f03d2"}.mdi-pac-man:before{content:"\f0baf"}.mdi-package:before{content:"\f03d3"}.mdi-package-check:before{content:"\f1b51"}.mdi-package-down:before{content:"\f03d4"}.mdi-package-up:before{content:"\f03d5"}.mdi-package-variant:before{content:"\f03d6"}.mdi-package-variant-closed:before{content:"\f03d7"}.mdi-package-variant-closed-check:before{content:"\f1b52"}.mdi-package-variant-closed-minus:before{content:"\f19d4"}.mdi-package-variant-closed-plus:before{content:"\f19d5"}.mdi-package-variant-closed-remove:before{content:"\f19d6"}.mdi-package-variant-minus:before{content:"\f19d7"}.mdi-package-variant-plus:before{content:"\f19d8"}.mdi-package-variant-remove:before{content:"\f19d9"}.mdi-page-first:before{content:"\f0600"}.mdi-page-last:before{content:"\f0601"}.mdi-page-layout-body:before{content:"\f06fa"}.mdi-page-layout-footer:before{content:"\f06fb"}.mdi-page-layout-header:before{content:"\f06fc"}.mdi-page-layout-header-footer:before{content:"\f0f7f"}.mdi-page-layout-sidebar-left:before{content:"\f06fd"}.mdi-page-layout-sidebar-right:before{content:"\f06fe"}.mdi-page-next:before{content:"\f0bb0"}.mdi-page-next-outline:before{content:"\f0bb1"}.mdi-page-previous:before{content:"\f0bb2"}.mdi-page-previous-outline:before{content:"\f0bb3"}.mdi-pail:before{content:"\f1417"}.mdi-pail-minus:before{content:"\f1437"}.mdi-pail-minus-outline:before{content:"\f143c"}.mdi-pail-off:before{content:"\f1439"}.mdi-pail-off-outline:before{content:"\f143e"}.mdi-pail-outline:before{content:"\f143a"}.mdi-pail-plus:before{content:"\f1436"}.mdi-pail-plus-outline:before{content:"\f143b"}.mdi-pail-remove:before{content:"\f1438"}.mdi-pail-remove-outline:before{content:"\f143d"}.mdi-palette:before{content:"\f03d8"}.mdi-palette-advanced:before{content:"\f03d9"}.mdi-palette-outline:before{content:"\f0e0c"}.mdi-palette-swatch:before{content:"\f08b5"}.mdi-palette-swatch-outline:before{content:"\f135c"}.mdi-palette-swatch-variant:before{content:"\f195a"}.mdi-palm-tree:before{content:"\f1055"}.mdi-pan:before{content:"\f0bb4"}.mdi-pan-bottom-left:before{content:"\f0bb5"}.mdi-pan-bottom-right:before{content:"\f0bb6"}.mdi-pan-down:before{content:"\f0bb7"}.mdi-pan-horizontal:before{content:"\f0bb8"}.mdi-pan-left:before{content:"\f0bb9"}.mdi-pan-right:before{content:"\f0bba"}.mdi-pan-top-left:before{content:"\f0bbb"}.mdi-pan-top-right:before{content:"\f0bbc"}.mdi-pan-up:before{content:"\f0bbd"}.mdi-pan-vertical:before{content:"\f0bbe"}.mdi-panda:before{content:"\f03da"}.mdi-pandora:before{content:"\f03db"}.mdi-panorama:before{content:"\f03dc"}.mdi-panorama-fisheye:before{content:"\f03dd"}.mdi-panorama-horizontal:before{content:"\f1928"}.mdi-panorama-horizontal-outline:before{content:"\f03de"}.mdi-panorama-outline:before{content:"\f198c"}.mdi-panorama-sphere:before{content:"\f198d"}.mdi-panorama-sphere-outline:before{content:"\f198e"}.mdi-panorama-variant:before{content:"\f198f"}.mdi-panorama-variant-outline:before{content:"\f1990"}.mdi-panorama-vertical:before{content:"\f1929"}.mdi-panorama-vertical-outline:before{content:"\f03df"}.mdi-panorama-wide-angle:before{content:"\f195f"}.mdi-panorama-wide-angle-outline:before{content:"\f03e0"}.mdi-paper-cut-vertical:before{content:"\f03e1"}.mdi-paper-roll:before{content:"\f1157"}.mdi-paper-roll-outline:before{content:"\f1158"}.mdi-paperclip:before{content:"\f03e2"}.mdi-paperclip-check:before{content:"\f1ac6"}.mdi-paperclip-lock:before{content:"\f19da"}.mdi-paperclip-minus:before{content:"\f1ac7"}.mdi-paperclip-off:before{content:"\f1ac8"}.mdi-paperclip-plus:before{content:"\f1ac9"}.mdi-paperclip-remove:before{content:"\f1aca"}.mdi-parachute:before{content:"\f0cb4"}.mdi-parachute-outline:before{content:"\f0cb5"}.mdi-paragliding:before{content:"\f1745"}.mdi-parking:before{content:"\f03e3"}.mdi-party-popper:before{content:"\f1056"}.mdi-passport:before{content:"\f07e3"}.mdi-passport-alert:before{content:"\f1cb8"}.mdi-passport-biometric:before{content:"\f0de1"}.mdi-passport-cancel:before{content:"\f1cb9"}.mdi-passport-check:before{content:"\f1cba"}.mdi-passport-minus:before{content:"\f1cbb"}.mdi-passport-plus:before{content:"\f1cbc"}.mdi-passport-remove:before{content:"\f1cbd"}.mdi-pasta:before{content:"\f1160"}.mdi-patio-heater:before{content:"\f0f80"}.mdi-patreon:before{content:"\f0882"}.mdi-pause:before{content:"\f03e4"}.mdi-pause-box:before{content:"\f00bc"}.mdi-pause-box-outline:before{content:"\f1b7a"}.mdi-pause-circle:before{content:"\f03e5"}.mdi-pause-circle-outline:before{content:"\f03e6"}.mdi-pause-octagon:before{content:"\f03e7"}.mdi-pause-octagon-outline:before{content:"\f03e8"}.mdi-paw:before{content:"\f03e9"}.mdi-paw-off:before{content:"\f0657"}.mdi-paw-off-outline:before{content:"\f1676"}.mdi-paw-outline:before{content:"\f1675"}.mdi-peace:before{content:"\f0884"}.mdi-peanut:before{content:"\f0ffc"}.mdi-peanut-off:before{content:"\f0ffd"}.mdi-peanut-off-outline:before{content:"\f0fff"}.mdi-peanut-outline:before{content:"\f0ffe"}.mdi-pen:before{content:"\f03ea"}.mdi-pen-lock:before{content:"\f0de2"}.mdi-pen-minus:before{content:"\f0de3"}.mdi-pen-off:before{content:"\f0de4"}.mdi-pen-plus:before{content:"\f0de5"}.mdi-pen-remove:before{content:"\f0de6"}.mdi-pencil:before{content:"\f03eb"}.mdi-pencil-box:before{content:"\f03ec"}.mdi-pencil-box-multiple:before{content:"\f1144"}.mdi-pencil-box-multiple-outline:before{content:"\f1145"}.mdi-pencil-box-outline:before{content:"\f03ed"}.mdi-pencil-circle:before{content:"\f06ff"}.mdi-pencil-circle-outline:before{content:"\f0776"}.mdi-pencil-lock:before{content:"\f03ee"}.mdi-pencil-lock-outline:before{content:"\f0de7"}.mdi-pencil-minus:before{content:"\f0de8"}.mdi-pencil-minus-outline:before{content:"\f0de9"}.mdi-pencil-off:before{content:"\f03ef"}.mdi-pencil-off-outline:before{content:"\f0dea"}.mdi-pencil-outline:before{content:"\f0cb6"}.mdi-pencil-plus:before{content:"\f0deb"}.mdi-pencil-plus-outline:before{content:"\f0dec"}.mdi-pencil-remove:before{content:"\f0ded"}.mdi-pencil-remove-outline:before{content:"\f0dee"}.mdi-pencil-ruler:before{content:"\f1353"}.mdi-pencil-ruler-outline:before{content:"\f1c11"}.mdi-penguin:before{content:"\f0ec0"}.mdi-pentagon:before{content:"\f0701"}.mdi-pentagon-outline:before{content:"\f0700"}.mdi-pentagram:before{content:"\f1667"}.mdi-percent:before{content:"\f03f0"}.mdi-percent-box:before{content:"\f1a02"}.mdi-percent-box-outline:before{content:"\f1a03"}.mdi-percent-circle:before{content:"\f1a04"}.mdi-percent-circle-outline:before{content:"\f1a05"}.mdi-percent-outline:before{content:"\f1278"}.mdi-periodic-table:before{content:"\f08b6"}.mdi-perspective-less:before{content:"\f0d23"}.mdi-perspective-more:before{content:"\f0d24"}.mdi-ph:before{content:"\f17c5"}.mdi-phone:before{content:"\f03f2"}.mdi-phone-alert:before{content:"\f0f1a"}.mdi-phone-alert-outline:before{content:"\f118e"}.mdi-phone-bluetooth:before{content:"\f03f3"}.mdi-phone-bluetooth-outline:before{content:"\f118f"}.mdi-phone-cancel:before{content:"\f10bc"}.mdi-phone-cancel-outline:before{content:"\f1190"}.mdi-phone-check:before{content:"\f11a9"}.mdi-phone-check-outline:before{content:"\f11aa"}.mdi-phone-classic:before{content:"\f0602"}.mdi-phone-classic-off:before{content:"\f1279"}.mdi-phone-clock:before{content:"\f19db"}.mdi-phone-dial:before{content:"\f1559"}.mdi-phone-dial-outline:before{content:"\f155a"}.mdi-phone-forward:before{content:"\f03f4"}.mdi-phone-forward-outline:before{content:"\f1191"}.mdi-phone-hangup:before{content:"\f03f5"}.mdi-phone-hangup-outline:before{content:"\f1192"}.mdi-phone-in-talk:before{content:"\f03f6"}.mdi-phone-in-talk-outline:before{content:"\f1182"}.mdi-phone-incoming:before{content:"\f03f7"}.mdi-phone-incoming-outgoing:before{content:"\f1b3f"}.mdi-phone-incoming-outgoing-outline:before{content:"\f1b40"}.mdi-phone-incoming-outline:before{content:"\f1193"}.mdi-phone-lock:before{content:"\f03f8"}.mdi-phone-lock-outline:before{content:"\f1194"}.mdi-phone-log:before{content:"\f03f9"}.mdi-phone-log-outline:before{content:"\f1195"}.mdi-phone-message:before{content:"\f1196"}.mdi-phone-message-outline:before{content:"\f1197"}.mdi-phone-minus:before{content:"\f0658"}.mdi-phone-minus-outline:before{content:"\f1198"}.mdi-phone-missed:before{content:"\f03fa"}.mdi-phone-missed-outline:before{content:"\f11a5"}.mdi-phone-off:before{content:"\f0def"}.mdi-phone-off-outline:before{content:"\f11a6"}.mdi-phone-outgoing:before{content:"\f03fb"}.mdi-phone-outgoing-outline:before{content:"\f1199"}.mdi-phone-outline:before{content:"\f0df0"}.mdi-phone-paused:before{content:"\f03fc"}.mdi-phone-paused-outline:before{content:"\f119a"}.mdi-phone-plus:before{content:"\f0659"}.mdi-phone-plus-outline:before{content:"\f119b"}.mdi-phone-refresh:before{content:"\f1993"}.mdi-phone-refresh-outline:before{content:"\f1994"}.mdi-phone-remove:before{content:"\f152f"}.mdi-phone-remove-outline:before{content:"\f1530"}.mdi-phone-return:before{content:"\f082f"}.mdi-phone-return-outline:before{content:"\f119c"}.mdi-phone-ring:before{content:"\f11ab"}.mdi-phone-ring-outline:before{content:"\f11ac"}.mdi-phone-rotate-landscape:before{content:"\f0885"}.mdi-phone-rotate-portrait:before{content:"\f0886"}.mdi-phone-settings:before{content:"\f03fd"}.mdi-phone-settings-outline:before{content:"\f119d"}.mdi-phone-sync:before{content:"\f1995"}.mdi-phone-sync-outline:before{content:"\f1996"}.mdi-phone-voip:before{content:"\f03fe"}.mdi-pi:before{content:"\f03ff"}.mdi-pi-box:before{content:"\f0400"}.mdi-pi-hole:before{content:"\f0df1"}.mdi-piano:before{content:"\f067d"}.mdi-piano-off:before{content:"\f0698"}.mdi-pickaxe:before{content:"\f08b7"}.mdi-picture-in-picture-bottom-right:before{content:"\f0e57"}.mdi-picture-in-picture-bottom-right-outline:before{content:"\f0e58"}.mdi-picture-in-picture-top-right:before{content:"\f0e59"}.mdi-picture-in-picture-top-right-outline:before{content:"\f0e5a"}.mdi-pier:before{content:"\f0887"}.mdi-pier-crane:before{content:"\f0888"}.mdi-pig:before{content:"\f0401"}.mdi-pig-variant:before{content:"\f1006"}.mdi-pig-variant-outline:before{content:"\f1678"}.mdi-piggy-bank:before{content:"\f1007"}.mdi-piggy-bank-outline:before{content:"\f1679"}.mdi-pill:before{content:"\f0402"}.mdi-pill-multiple:before{content:"\f1b4c"}.mdi-pill-off:before{content:"\f1a5c"}.mdi-pillar:before{content:"\f0702"}.mdi-pin:before{content:"\f0403"}.mdi-pin-off:before{content:"\f0404"}.mdi-pin-off-outline:before{content:"\f0930"}.mdi-pin-outline:before{content:"\f0931"}.mdi-pine-tree:before{content:"\f0405"}.mdi-pine-tree-box:before{content:"\f0406"}.mdi-pine-tree-fire:before{content:"\f141a"}.mdi-pine-tree-variant:before{content:"\f1c73"}.mdi-pine-tree-variant-outline:before{content:"\f1c74"}.mdi-pinterest:before{content:"\f0407"}.mdi-pinwheel:before{content:"\f0ad5"}.mdi-pinwheel-outline:before{content:"\f0ad6"}.mdi-pipe:before{content:"\f07e5"}.mdi-pipe-disconnected:before{content:"\f07e6"}.mdi-pipe-leak:before{content:"\f0889"}.mdi-pipe-valve:before{content:"\f184d"}.mdi-pipe-wrench:before{content:"\f1354"}.mdi-pirate:before{content:"\f0a08"}.mdi-pistol:before{content:"\f0703"}.mdi-piston:before{content:"\f088a"}.mdi-pitchfork:before{content:"\f1553"}.mdi-pizza:before{content:"\f0409"}.mdi-plane-car:before{content:"\f1aff"}.mdi-plane-train:before{content:"\f1b00"}.mdi-play:before{content:"\f040a"}.mdi-play-box:before{content:"\f127a"}.mdi-play-box-edit-outline:before{content:"\f1c3a"}.mdi-play-box-lock:before{content:"\f1a16"}.mdi-play-box-lock-open:before{content:"\f1a17"}.mdi-play-box-lock-open-outline:before{content:"\f1a18"}.mdi-play-box-lock-outline:before{content:"\f1a19"}.mdi-play-box-multiple:before{content:"\f0d19"}.mdi-play-box-multiple-outline:before{content:"\f13e6"}.mdi-play-box-outline:before{content:"\f040b"}.mdi-play-circle:before{content:"\f040c"}.mdi-play-circle-outline:before{content:"\f040d"}.mdi-play-network:before{content:"\f088b"}.mdi-play-network-outline:before{content:"\f0cb7"}.mdi-play-outline:before{content:"\f0f1b"}.mdi-play-pause:before{content:"\f040e"}.mdi-play-protected-content:before{content:"\f040f"}.mdi-play-speed:before{content:"\f08ff"}.mdi-playlist-check:before{content:"\f05c7"}.mdi-playlist-edit:before{content:"\f0900"}.mdi-playlist-minus:before{content:"\f0410"}.mdi-playlist-music:before{content:"\f0cb8"}.mdi-playlist-music-outline:before{content:"\f0cb9"}.mdi-playlist-play:before{content:"\f0411"}.mdi-playlist-plus:before{content:"\f0412"}.mdi-playlist-remove:before{content:"\f0413"}.mdi-playlist-star:before{content:"\f0df2"}.mdi-plex:before{content:"\f06ba"}.mdi-pliers:before{content:"\f19a4"}.mdi-plus:before{content:"\f0415"}.mdi-plus-box:before{content:"\f0416"}.mdi-plus-box-multiple:before{content:"\f0334"}.mdi-plus-box-multiple-outline:before{content:"\f1143"}.mdi-plus-box-outline:before{content:"\f0704"}.mdi-plus-circle:before{content:"\f0417"}.mdi-plus-circle-multiple:before{content:"\f034c"}.mdi-plus-circle-multiple-outline:before{content:"\f0418"}.mdi-plus-circle-outline:before{content:"\f0419"}.mdi-plus-lock:before{content:"\f1a5d"}.mdi-plus-lock-open:before{content:"\f1a5e"}.mdi-plus-minus:before{content:"\f0992"}.mdi-plus-minus-box:before{content:"\f0993"}.mdi-plus-minus-variant:before{content:"\f14c9"}.mdi-plus-network:before{content:"\f041a"}.mdi-plus-network-outline:before{content:"\f0cba"}.mdi-plus-outline:before{content:"\f0705"}.mdi-plus-thick:before{content:"\f11ec"}.mdi-pocket:before{content:"\f1cbe"}.mdi-podcast:before{content:"\f0994"}.mdi-podium:before{content:"\f0d25"}.mdi-podium-bronze:before{content:"\f0d26"}.mdi-podium-gold:before{content:"\f0d27"}.mdi-podium-silver:before{content:"\f0d28"}.mdi-point-of-sale:before{content:"\f0d92"}.mdi-pokeball:before{content:"\f041d"}.mdi-pokemon-go:before{content:"\f0a09"}.mdi-poker-chip:before{content:"\f0830"}.mdi-polaroid:before{content:"\f041e"}.mdi-police-badge:before{content:"\f1167"}.mdi-police-badge-outline:before{content:"\f1168"}.mdi-police-station:before{content:"\f1839"}.mdi-poll:before{content:"\f041f"}.mdi-polo:before{content:"\f14c3"}.mdi-polymer:before{content:"\f0421"}.mdi-pool:before{content:"\f0606"}.mdi-pool-thermometer:before{content:"\f1a5f"}.mdi-popcorn:before{content:"\f0422"}.mdi-post:before{content:"\f1008"}.mdi-post-lamp:before{content:"\f1a60"}.mdi-post-outline:before{content:"\f1009"}.mdi-postage-stamp:before{content:"\f0cbb"}.mdi-pot:before{content:"\f02e5"}.mdi-pot-mix:before{content:"\f065b"}.mdi-pot-mix-outline:before{content:"\f0677"}.mdi-pot-outline:before{content:"\f02ff"}.mdi-pot-steam:before{content:"\f065a"}.mdi-pot-steam-outline:before{content:"\f0326"}.mdi-pound:before{content:"\f0423"}.mdi-pound-box:before{content:"\f0424"}.mdi-pound-box-outline:before{content:"\f117f"}.mdi-power:before{content:"\f0425"}.mdi-power-cycle:before{content:"\f0901"}.mdi-power-off:before{content:"\f0902"}.mdi-power-on:before{content:"\f0903"}.mdi-power-plug:before{content:"\f06a5"}.mdi-power-plug-battery:before{content:"\f1c3b"}.mdi-power-plug-battery-outline:before{content:"\f1c3c"}.mdi-power-plug-off:before{content:"\f06a6"}.mdi-power-plug-off-outline:before{content:"\f1424"}.mdi-power-plug-outline:before{content:"\f1425"}.mdi-power-settings:before{content:"\f0426"}.mdi-power-sleep:before{content:"\f0904"}.mdi-power-socket:before{content:"\f0427"}.mdi-power-socket-au:before{content:"\f0905"}.mdi-power-socket-ch:before{content:"\f0fb3"}.mdi-power-socket-de:before{content:"\f1107"}.mdi-power-socket-eu:before{content:"\f07e7"}.mdi-power-socket-fr:before{content:"\f1108"}.mdi-power-socket-it:before{content:"\f14ff"}.mdi-power-socket-jp:before{content:"\f1109"}.mdi-power-socket-uk:before{content:"\f07e8"}.mdi-power-socket-us:before{content:"\f07e9"}.mdi-power-standby:before{content:"\f0906"}.mdi-powershell:before{content:"\f0a0a"}.mdi-prescription:before{content:"\f0706"}.mdi-presentation:before{content:"\f0428"}.mdi-presentation-play:before{content:"\f0429"}.mdi-pretzel:before{content:"\f1562"}.mdi-printer:before{content:"\f042a"}.mdi-printer-3d:before{content:"\f042b"}.mdi-printer-3d-nozzle:before{content:"\f0e5b"}.mdi-printer-3d-nozzle-alert:before{content:"\f11c0"}.mdi-printer-3d-nozzle-alert-outline:before{content:"\f11c1"}.mdi-printer-3d-nozzle-heat:before{content:"\f18b8"}.mdi-printer-3d-nozzle-heat-outline:before{content:"\f18b9"}.mdi-printer-3d-nozzle-off:before{content:"\f1b19"}.mdi-printer-3d-nozzle-off-outline:before{content:"\f1b1a"}.mdi-printer-3d-nozzle-outline:before{content:"\f0e5c"}.mdi-printer-3d-off:before{content:"\f1b0e"}.mdi-printer-alert:before{content:"\f042c"}.mdi-printer-check:before{content:"\f1146"}.mdi-printer-eye:before{content:"\f1458"}.mdi-printer-off:before{content:"\f0e5d"}.mdi-printer-off-outline:before{content:"\f1785"}.mdi-printer-outline:before{content:"\f1786"}.mdi-printer-pos:before{content:"\f1057"}.mdi-printer-pos-alert:before{content:"\f1bbc"}.mdi-printer-pos-alert-outline:before{content:"\f1bbd"}.mdi-printer-pos-cancel:before{content:"\f1bbe"}.mdi-printer-pos-cancel-outline:before{content:"\f1bbf"}.mdi-printer-pos-check:before{content:"\f1bc0"}.mdi-printer-pos-check-outline:before{content:"\f1bc1"}.mdi-printer-pos-cog:before{content:"\f1bc2"}.mdi-printer-pos-cog-outline:before{content:"\f1bc3"}.mdi-printer-pos-edit:before{content:"\f1bc4"}.mdi-printer-pos-edit-outline:before{content:"\f1bc5"}.mdi-printer-pos-minus:before{content:"\f1bc6"}.mdi-printer-pos-minus-outline:before{content:"\f1bc7"}.mdi-printer-pos-network:before{content:"\f1bc8"}.mdi-printer-pos-network-outline:before{content:"\f1bc9"}.mdi-printer-pos-off:before{content:"\f1bca"}.mdi-printer-pos-off-outline:before{content:"\f1bcb"}.mdi-printer-pos-outline:before{content:"\f1bcc"}.mdi-printer-pos-pause:before{content:"\f1bcd"}.mdi-printer-pos-pause-outline:before{content:"\f1bce"}.mdi-printer-pos-play:before{content:"\f1bcf"}.mdi-printer-pos-play-outline:before{content:"\f1bd0"}.mdi-printer-pos-plus:before{content:"\f1bd1"}.mdi-printer-pos-plus-outline:before{content:"\f1bd2"}.mdi-printer-pos-refresh:before{content:"\f1bd3"}.mdi-printer-pos-refresh-outline:before{content:"\f1bd4"}.mdi-printer-pos-remove:before{content:"\f1bd5"}.mdi-printer-pos-remove-outline:before{content:"\f1bd6"}.mdi-printer-pos-star:before{content:"\f1bd7"}.mdi-printer-pos-star-outline:before{content:"\f1bd8"}.mdi-printer-pos-stop:before{content:"\f1bd9"}.mdi-printer-pos-stop-outline:before{content:"\f1bda"}.mdi-printer-pos-sync:before{content:"\f1bdb"}.mdi-printer-pos-sync-outline:before{content:"\f1bdc"}.mdi-printer-pos-wrench:before{content:"\f1bdd"}.mdi-printer-pos-wrench-outline:before{content:"\f1bde"}.mdi-printer-search:before{content:"\f1457"}.mdi-printer-settings:before{content:"\f0707"}.mdi-printer-wireless:before{content:"\f0a0b"}.mdi-priority-high:before{content:"\f0603"}.mdi-priority-low:before{content:"\f0604"}.mdi-professional-hexagon:before{content:"\f042d"}.mdi-progress-alert:before{content:"\f0cbc"}.mdi-progress-check:before{content:"\f0995"}.mdi-progress-clock:before{content:"\f0996"}.mdi-progress-close:before{content:"\f110a"}.mdi-progress-download:before{content:"\f0997"}.mdi-progress-helper:before{content:"\f1ba2"}.mdi-progress-pencil:before{content:"\f1787"}.mdi-progress-question:before{content:"\f1522"}.mdi-progress-star:before{content:"\f1788"}.mdi-progress-star-four-points:before{content:"\f1c3d"}.mdi-progress-tag:before{content:"\f1d0d"}.mdi-progress-upload:before{content:"\f0998"}.mdi-progress-wrench:before{content:"\f0cbd"}.mdi-projector:before{content:"\f042e"}.mdi-projector-off:before{content:"\f1a23"}.mdi-projector-screen:before{content:"\f042f"}.mdi-projector-screen-off:before{content:"\f180d"}.mdi-projector-screen-off-outline:before{content:"\f180e"}.mdi-projector-screen-outline:before{content:"\f1724"}.mdi-projector-screen-variant:before{content:"\f180f"}.mdi-projector-screen-variant-off:before{content:"\f1810"}.mdi-projector-screen-variant-off-outline:before{content:"\f1811"}.mdi-projector-screen-variant-outline:before{content:"\f1812"}.mdi-propane-tank:before{content:"\f1357"}.mdi-propane-tank-outline:before{content:"\f1358"}.mdi-protocol:before{content:"\f0fd8"}.mdi-publish:before{content:"\f06a7"}.mdi-publish-off:before{content:"\f1945"}.mdi-pulse:before{content:"\f0430"}.mdi-pump:before{content:"\f1402"}.mdi-pump-off:before{content:"\f1b22"}.mdi-pumpkin:before{content:"\f0bbf"}.mdi-purse:before{content:"\f0f1c"}.mdi-purse-outline:before{content:"\f0f1d"}.mdi-puzzle:before{content:"\f0431"}.mdi-puzzle-check:before{content:"\f1426"}.mdi-puzzle-check-outline:before{content:"\f1427"}.mdi-puzzle-edit:before{content:"\f14d3"}.mdi-puzzle-edit-outline:before{content:"\f14d9"}.mdi-puzzle-heart:before{content:"\f14d4"}.mdi-puzzle-heart-outline:before{content:"\f14da"}.mdi-puzzle-minus:before{content:"\f14d1"}.mdi-puzzle-minus-outline:before{content:"\f14d7"}.mdi-puzzle-outline:before{content:"\f0a66"}.mdi-puzzle-plus:before{content:"\f14d0"}.mdi-puzzle-plus-outline:before{content:"\f14d6"}.mdi-puzzle-remove:before{content:"\f14d2"}.mdi-puzzle-remove-outline:before{content:"\f14d8"}.mdi-puzzle-star:before{content:"\f14d5"}.mdi-puzzle-star-outline:before{content:"\f14db"}.mdi-pyramid:before{content:"\f1952"}.mdi-pyramid-off:before{content:"\f1953"}.mdi-qi:before{content:"\f0999"}.mdi-qqchat:before{content:"\f0605"}.mdi-qrcode:before{content:"\f0432"}.mdi-qrcode-edit:before{content:"\f08b8"}.mdi-qrcode-minus:before{content:"\f118c"}.mdi-qrcode-plus:before{content:"\f118b"}.mdi-qrcode-remove:before{content:"\f118d"}.mdi-qrcode-scan:before{content:"\f0433"}.mdi-quadcopter:before{content:"\f0434"}.mdi-quality-high:before{content:"\f0435"}.mdi-quality-low:before{content:"\f0a0c"}.mdi-quality-medium:before{content:"\f0a0d"}.mdi-queue-first-in-last-out:before{content:"\f1caf"}.mdi-quora:before{content:"\f0d29"}.mdi-rabbit:before{content:"\f0907"}.mdi-rabbit-variant:before{content:"\f1a61"}.mdi-rabbit-variant-outline:before{content:"\f1a62"}.mdi-racing-helmet:before{content:"\f0d93"}.mdi-racquetball:before{content:"\f0d94"}.mdi-radar:before{content:"\f0437"}.mdi-radiator:before{content:"\f0438"}.mdi-radiator-disabled:before{content:"\f0ad7"}.mdi-radiator-off:before{content:"\f0ad8"}.mdi-radio:before{content:"\f0439"}.mdi-radio-am:before{content:"\f0cbe"}.mdi-radio-fm:before{content:"\f0cbf"}.mdi-radio-handheld:before{content:"\f043a"}.mdi-radio-off:before{content:"\f121c"}.mdi-radio-tower:before{content:"\f043b"}.mdi-radioactive:before{content:"\f043c"}.mdi-radioactive-circle:before{content:"\f185d"}.mdi-radioactive-circle-outline:before{content:"\f185e"}.mdi-radioactive-off:before{content:"\f0ec1"}.mdi-radiobox-blank:before{content:"\f043d"}.mdi-radiobox-indeterminate-variant:before{content:"\f1c5e"}.mdi-radiobox-marked:before{content:"\f043e"}.mdi-radiology-box:before{content:"\f14c5"}.mdi-radiology-box-outline:before{content:"\f14c6"}.mdi-radius:before{content:"\f0cc0"}.mdi-radius-outline:before{content:"\f0cc1"}.mdi-railroad-light:before{content:"\f0f1e"}.mdi-rake:before{content:"\f1544"}.mdi-raspberry-pi:before{content:"\f043f"}.mdi-raw:before{content:"\f1a0f"}.mdi-raw-off:before{content:"\f1a10"}.mdi-ray-end:before{content:"\f0440"}.mdi-ray-end-arrow:before{content:"\f0441"}.mdi-ray-start:before{content:"\f0442"}.mdi-ray-start-arrow:before{content:"\f0443"}.mdi-ray-start-end:before{content:"\f0444"}.mdi-ray-start-vertex-end:before{content:"\f15d8"}.mdi-ray-vertex:before{content:"\f0445"}.mdi-razor-double-edge:before{content:"\f1997"}.mdi-razor-single-edge:before{content:"\f1998"}.mdi-react:before{content:"\f0708"}.mdi-read:before{content:"\f0447"}.mdi-receipt:before{content:"\f0824"}.mdi-receipt-clock:before{content:"\f1c3e"}.mdi-receipt-clock-outline:before{content:"\f1c3f"}.mdi-receipt-outline:before{content:"\f04f7"}.mdi-receipt-send:before{content:"\f1c40"}.mdi-receipt-send-outline:before{content:"\f1c41"}.mdi-receipt-text:before{content:"\f0449"}.mdi-receipt-text-arrow-left:before{content:"\f1c42"}.mdi-receipt-text-arrow-left-outline:before{content:"\f1c43"}.mdi-receipt-text-arrow-right:before{content:"\f1c44"}.mdi-receipt-text-arrow-right-outline:before{content:"\f1c45"}.mdi-receipt-text-check:before{content:"\f1a63"}.mdi-receipt-text-check-outline:before{content:"\f1a64"}.mdi-receipt-text-clock:before{content:"\f1c46"}.mdi-receipt-text-clock-outline:before{content:"\f1c47"}.mdi-receipt-text-edit:before{content:"\f1c48"}.mdi-receipt-text-edit-outline:before{content:"\f1c49"}.mdi-receipt-text-minus:before{content:"\f1a65"}.mdi-receipt-text-minus-outline:before{content:"\f1a66"}.mdi-receipt-text-outline:before{content:"\f19dc"}.mdi-receipt-text-plus:before{content:"\f1a67"}.mdi-receipt-text-plus-outline:before{content:"\f1a68"}.mdi-receipt-text-remove:before{content:"\f1a69"}.mdi-receipt-text-remove-outline:before{content:"\f1a6a"}.mdi-receipt-text-send:before{content:"\f1c4a"}.mdi-receipt-text-send-outline:before{content:"\f1c4b"}.mdi-record:before{content:"\f044a"}.mdi-record-circle:before{content:"\f0ec2"}.mdi-record-circle-outline:before{content:"\f0ec3"}.mdi-record-player:before{content:"\f099a"}.mdi-record-rec:before{content:"\f044b"}.mdi-rectangle:before{content:"\f0e5e"}.mdi-rectangle-outline:before{content:"\f0e5f"}.mdi-recycle:before{content:"\f044c"}.mdi-recycle-variant:before{content:"\f139d"}.mdi-reddit:before{content:"\f044d"}.mdi-redhat:before{content:"\f111b"}.mdi-redo:before{content:"\f044e"}.mdi-redo-variant:before{content:"\f044f"}.mdi-reflect-horizontal:before{content:"\f0a0e"}.mdi-reflect-vertical:before{content:"\f0a0f"}.mdi-refresh:before{content:"\f0450"}.mdi-refresh-auto:before{content:"\f18f2"}.mdi-refresh-circle:before{content:"\f1377"}.mdi-regex:before{content:"\f0451"}.mdi-registered-trademark:before{content:"\f0a67"}.mdi-reiterate:before{content:"\f1588"}.mdi-relation-many-to-many:before{content:"\f1496"}.mdi-relation-many-to-one:before{content:"\f1497"}.mdi-relation-many-to-one-or-many:before{content:"\f1498"}.mdi-relation-many-to-only-one:before{content:"\f1499"}.mdi-relation-many-to-zero-or-many:before{content:"\f149a"}.mdi-relation-many-to-zero-or-one:before{content:"\f149b"}.mdi-relation-one-or-many-to-many:before{content:"\f149c"}.mdi-relation-one-or-many-to-one:before{content:"\f149d"}.mdi-relation-one-or-many-to-one-or-many:before{content:"\f149e"}.mdi-relation-one-or-many-to-only-one:before{content:"\f149f"}.mdi-relation-one-or-many-to-zero-or-many:before{content:"\f14a0"}.mdi-relation-one-or-many-to-zero-or-one:before{content:"\f14a1"}.mdi-relation-one-to-many:before{content:"\f14a2"}.mdi-relation-one-to-one:before{content:"\f14a3"}.mdi-relation-one-to-one-or-many:before{content:"\f14a4"}.mdi-relation-one-to-only-one:before{content:"\f14a5"}.mdi-relation-one-to-zero-or-many:before{content:"\f14a6"}.mdi-relation-one-to-zero-or-one:before{content:"\f14a7"}.mdi-relation-only-one-to-many:before{content:"\f14a8"}.mdi-relation-only-one-to-one:before{content:"\f14a9"}.mdi-relation-only-one-to-one-or-many:before{content:"\f14aa"}.mdi-relation-only-one-to-only-one:before{content:"\f14ab"}.mdi-relation-only-one-to-zero-or-many:before{content:"\f14ac"}.mdi-relation-only-one-to-zero-or-one:before{content:"\f14ad"}.mdi-relation-zero-or-many-to-many:before{content:"\f14ae"}.mdi-relation-zero-or-many-to-one:before{content:"\f14af"}.mdi-relation-zero-or-many-to-one-or-many:before{content:"\f14b0"}.mdi-relation-zero-or-many-to-only-one:before{content:"\f14b1"}.mdi-relation-zero-or-many-to-zero-or-many:before{content:"\f14b2"}.mdi-relation-zero-or-many-to-zero-or-one:before{content:"\f14b3"}.mdi-relation-zero-or-one-to-many:before{content:"\f14b4"}.mdi-relation-zero-or-one-to-one:before{content:"\f14b5"}.mdi-relation-zero-or-one-to-one-or-many:before{content:"\f14b6"}.mdi-relation-zero-or-one-to-only-one:before{content:"\f14b7"}.mdi-relation-zero-or-one-to-zero-or-many:before{content:"\f14b8"}.mdi-relation-zero-or-one-to-zero-or-one:before{content:"\f14b9"}.mdi-relative-scale:before{content:"\f0452"}.mdi-reload:before{content:"\f0453"}.mdi-reload-alert:before{content:"\f110b"}.mdi-reminder:before{content:"\f088c"}.mdi-remote:before{content:"\f0454"}.mdi-remote-desktop:before{content:"\f08b9"}.mdi-remote-off:before{content:"\f0ec4"}.mdi-remote-tv:before{content:"\f0ec5"}.mdi-remote-tv-off:before{content:"\f0ec6"}.mdi-rename:before{content:"\f1c18"}.mdi-rename-box:before{content:"\f0455"}.mdi-rename-box-outline:before{content:"\f1c19"}.mdi-rename-outline:before{content:"\f1c1a"}.mdi-reorder-horizontal:before{content:"\f0688"}.mdi-reorder-vertical:before{content:"\f0689"}.mdi-repeat:before{content:"\f0456"}.mdi-repeat-off:before{content:"\f0457"}.mdi-repeat-once:before{content:"\f0458"}.mdi-repeat-variant:before{content:"\f0547"}.mdi-replay:before{content:"\f0459"}.mdi-reply:before{content:"\f045a"}.mdi-reply-all:before{content:"\f045b"}.mdi-reply-all-outline:before{content:"\f0f1f"}.mdi-reply-circle:before{content:"\f11ae"}.mdi-reply-outline:before{content:"\f0f20"}.mdi-reproduction:before{content:"\f045c"}.mdi-resistor:before{content:"\f0b44"}.mdi-resistor-nodes:before{content:"\f0b45"}.mdi-resize:before{content:"\f0a68"}.mdi-resize-bottom-right:before{content:"\f045d"}.mdi-responsive:before{content:"\f045e"}.mdi-restart:before{content:"\f0709"}.mdi-restart-alert:before{content:"\f110c"}.mdi-restart-off:before{content:"\f0d95"}.mdi-restore:before{content:"\f099b"}.mdi-restore-alert:before{content:"\f110d"}.mdi-rewind:before{content:"\f045f"}.mdi-rewind-10:before{content:"\f0d2a"}.mdi-rewind-15:before{content:"\f1946"}.mdi-rewind-30:before{content:"\f0d96"}.mdi-rewind-45:before{content:"\f1b13"}.mdi-rewind-5:before{content:"\f11f9"}.mdi-rewind-60:before{content:"\f160c"}.mdi-rewind-outline:before{content:"\f070a"}.mdi-rhombus:before{content:"\f070b"}.mdi-rhombus-medium:before{content:"\f0a10"}.mdi-rhombus-medium-outline:before{content:"\f14dc"}.mdi-rhombus-outline:before{content:"\f070c"}.mdi-rhombus-split:before{content:"\f0a11"}.mdi-rhombus-split-outline:before{content:"\f14dd"}.mdi-ribbon:before{content:"\f0460"}.mdi-rice:before{content:"\f07ea"}.mdi-rickshaw:before{content:"\f15bb"}.mdi-rickshaw-electric:before{content:"\f15bc"}.mdi-ring:before{content:"\f07eb"}.mdi-rivet:before{content:"\f0e60"}.mdi-road:before{content:"\f0461"}.mdi-road-variant:before{content:"\f0462"}.mdi-robber:before{content:"\f1058"}.mdi-robot:before{content:"\f06a9"}.mdi-robot-angry:before{content:"\f169d"}.mdi-robot-angry-outline:before{content:"\f169e"}.mdi-robot-confused:before{content:"\f169f"}.mdi-robot-confused-outline:before{content:"\f16a0"}.mdi-robot-dead:before{content:"\f16a1"}.mdi-robot-dead-outline:before{content:"\f16a2"}.mdi-robot-excited:before{content:"\f16a3"}.mdi-robot-excited-outline:before{content:"\f16a4"}.mdi-robot-happy:before{content:"\f1719"}.mdi-robot-happy-outline:before{content:"\f171a"}.mdi-robot-industrial:before{content:"\f0b46"}.mdi-robot-industrial-outline:before{content:"\f1a1a"}.mdi-robot-love:before{content:"\f16a5"}.mdi-robot-love-outline:before{content:"\f16a6"}.mdi-robot-mower:before{content:"\f11f7"}.mdi-robot-mower-outline:before{content:"\f11f3"}.mdi-robot-off:before{content:"\f16a7"}.mdi-robot-off-outline:before{content:"\f167b"}.mdi-robot-outline:before{content:"\f167a"}.mdi-robot-vacuum:before{content:"\f070d"}.mdi-robot-vacuum-alert:before{content:"\f1b5d"}.mdi-robot-vacuum-off:before{content:"\f1c01"}.mdi-robot-vacuum-variant:before{content:"\f0908"}.mdi-robot-vacuum-variant-alert:before{content:"\f1b5e"}.mdi-robot-vacuum-variant-off:before{content:"\f1c02"}.mdi-rocket:before{content:"\f0463"}.mdi-rocket-launch:before{content:"\f14de"}.mdi-rocket-launch-outline:before{content:"\f14df"}.mdi-rocket-outline:before{content:"\f13af"}.mdi-rodent:before{content:"\f1327"}.mdi-roller-shade:before{content:"\f1a6b"}.mdi-roller-shade-closed:before{content:"\f1a6c"}.mdi-roller-skate:before{content:"\f0d2b"}.mdi-roller-skate-off:before{content:"\f0145"}.mdi-rollerblade:before{content:"\f0d2c"}.mdi-rollerblade-off:before{content:"\f002e"}.mdi-rollupjs:before{content:"\f0bc0"}.mdi-rolodex:before{content:"\f1ab9"}.mdi-rolodex-outline:before{content:"\f1aba"}.mdi-roman-numeral-1:before{content:"\f1088"}.mdi-roman-numeral-10:before{content:"\f1091"}.mdi-roman-numeral-2:before{content:"\f1089"}.mdi-roman-numeral-3:before{content:"\f108a"}.mdi-roman-numeral-4:before{content:"\f108b"}.mdi-roman-numeral-5:before{content:"\f108c"}.mdi-roman-numeral-6:before{content:"\f108d"}.mdi-roman-numeral-7:before{content:"\f108e"}.mdi-roman-numeral-8:before{content:"\f108f"}.mdi-roman-numeral-9:before{content:"\f1090"}.mdi-room-service:before{content:"\f088d"}.mdi-room-service-outline:before{content:"\f0d97"}.mdi-rotate-360:before{content:"\f1999"}.mdi-rotate-3d:before{content:"\f0ec7"}.mdi-rotate-3d-variant:before{content:"\f0464"}.mdi-rotate-left:before{content:"\f0465"}.mdi-rotate-left-variant:before{content:"\f0466"}.mdi-rotate-orbit:before{content:"\f0d98"}.mdi-rotate-right:before{content:"\f0467"}.mdi-rotate-right-variant:before{content:"\f0468"}.mdi-rounded-corner:before{content:"\f0607"}.mdi-router:before{content:"\f11e2"}.mdi-router-network:before{content:"\f1087"}.mdi-router-network-wireless:before{content:"\f1c97"}.mdi-router-wireless:before{content:"\f0469"}.mdi-router-wireless-off:before{content:"\f15a3"}.mdi-router-wireless-settings:before{content:"\f0a69"}.mdi-routes:before{content:"\f046a"}.mdi-routes-clock:before{content:"\f1059"}.mdi-rowing:before{content:"\f0608"}.mdi-rss:before{content:"\f046b"}.mdi-rss-box:before{content:"\f046c"}.mdi-rss-off:before{content:"\f0f21"}.mdi-rug:before{content:"\f1475"}.mdi-rugby:before{content:"\f0d99"}.mdi-ruler:before{content:"\f046d"}.mdi-ruler-square:before{content:"\f0cc2"}.mdi-ruler-square-compass:before{content:"\f0ebe"}.mdi-run:before{content:"\f070e"}.mdi-run-fast:before{content:"\f046e"}.mdi-rv-truck:before{content:"\f11d4"}.mdi-sack:before{content:"\f0d2e"}.mdi-sack-outline:before{content:"\f1c4c"}.mdi-sack-percent:before{content:"\f0d2f"}.mdi-safe:before{content:"\f0a6a"}.mdi-safe-square:before{content:"\f127c"}.mdi-safe-square-outline:before{content:"\f127d"}.mdi-safety-goggles:before{content:"\f0d30"}.mdi-sail-boat:before{content:"\f0ec8"}.mdi-sail-boat-sink:before{content:"\f1aef"}.mdi-sale:before{content:"\f046f"}.mdi-sale-outline:before{content:"\f1a06"}.mdi-salesforce:before{content:"\f088e"}.mdi-sass:before{content:"\f07ec"}.mdi-satellite:before{content:"\f0470"}.mdi-satellite-uplink:before{content:"\f0909"}.mdi-satellite-variant:before{content:"\f0471"}.mdi-sausage:before{content:"\f08ba"}.mdi-sausage-off:before{content:"\f1789"}.mdi-saw-blade:before{content:"\f0e61"}.mdi-sawtooth-wave:before{content:"\f147a"}.mdi-saxophone:before{content:"\f0609"}.mdi-scale:before{content:"\f0472"}.mdi-scale-balance:before{content:"\f05d1"}.mdi-scale-bathroom:before{content:"\f0473"}.mdi-scale-off:before{content:"\f105a"}.mdi-scale-unbalanced:before{content:"\f19b8"}.mdi-scan-helper:before{content:"\f13d8"}.mdi-scanner:before{content:"\f06ab"}.mdi-scanner-off:before{content:"\f090a"}.mdi-scatter-plot:before{content:"\f0ec9"}.mdi-scatter-plot-outline:before{content:"\f0eca"}.mdi-scent:before{content:"\f1958"}.mdi-scent-off:before{content:"\f1959"}.mdi-school:before{content:"\f0474"}.mdi-school-outline:before{content:"\f1180"}.mdi-scissors-cutting:before{content:"\f0a6b"}.mdi-scooter:before{content:"\f15bd"}.mdi-scooter-electric:before{content:"\f15be"}.mdi-scoreboard:before{content:"\f127e"}.mdi-scoreboard-outline:before{content:"\f127f"}.mdi-screen-rotation:before{content:"\f0475"}.mdi-screen-rotation-lock:before{content:"\f0478"}.mdi-screw-flat-top:before{content:"\f0df3"}.mdi-screw-lag:before{content:"\f0df4"}.mdi-screw-machine-flat-top:before{content:"\f0df5"}.mdi-screw-machine-round-top:before{content:"\f0df6"}.mdi-screw-round-top:before{content:"\f0df7"}.mdi-screwdriver:before{content:"\f0476"}.mdi-script:before{content:"\f0bc1"}.mdi-script-outline:before{content:"\f0477"}.mdi-script-text:before{content:"\f0bc2"}.mdi-script-text-key:before{content:"\f1725"}.mdi-script-text-key-outline:before{content:"\f1726"}.mdi-script-text-outline:before{content:"\f0bc3"}.mdi-script-text-play:before{content:"\f1727"}.mdi-script-text-play-outline:before{content:"\f1728"}.mdi-sd:before{content:"\f0479"}.mdi-seal:before{content:"\f047a"}.mdi-seal-variant:before{content:"\f0fd9"}.mdi-search-web:before{content:"\f070f"}.mdi-seat:before{content:"\f0cc3"}.mdi-seat-flat:before{content:"\f047b"}.mdi-seat-flat-angled:before{content:"\f047c"}.mdi-seat-individual-suite:before{content:"\f047d"}.mdi-seat-legroom-extra:before{content:"\f047e"}.mdi-seat-legroom-normal:before{content:"\f047f"}.mdi-seat-legroom-reduced:before{content:"\f0480"}.mdi-seat-outline:before{content:"\f0cc4"}.mdi-seat-passenger:before{content:"\f1249"}.mdi-seat-recline-extra:before{content:"\f0481"}.mdi-seat-recline-normal:before{content:"\f0482"}.mdi-seatbelt:before{content:"\f0cc5"}.mdi-security:before{content:"\f0483"}.mdi-security-network:before{content:"\f0484"}.mdi-seed:before{content:"\f0e62"}.mdi-seed-off:before{content:"\f13fd"}.mdi-seed-off-outline:before{content:"\f13fe"}.mdi-seed-outline:before{content:"\f0e63"}.mdi-seed-plus:before{content:"\f1a6d"}.mdi-seed-plus-outline:before{content:"\f1a6e"}.mdi-seesaw:before{content:"\f15a4"}.mdi-segment:before{content:"\f0ecb"}.mdi-select:before{content:"\f0485"}.mdi-select-all:before{content:"\f0486"}.mdi-select-arrow-down:before{content:"\f1b59"}.mdi-select-arrow-up:before{content:"\f1b58"}.mdi-select-color:before{content:"\f0d31"}.mdi-select-compare:before{content:"\f0ad9"}.mdi-select-drag:before{content:"\f0a6c"}.mdi-select-group:before{content:"\f0f82"}.mdi-select-inverse:before{content:"\f0487"}.mdi-select-marker:before{content:"\f1280"}.mdi-select-multiple:before{content:"\f1281"}.mdi-select-multiple-marker:before{content:"\f1282"}.mdi-select-off:before{content:"\f0488"}.mdi-select-place:before{content:"\f0fda"}.mdi-select-remove:before{content:"\f17c1"}.mdi-select-search:before{content:"\f1204"}.mdi-selection:before{content:"\f0489"}.mdi-selection-drag:before{content:"\f0a6d"}.mdi-selection-ellipse:before{content:"\f0d32"}.mdi-selection-ellipse-arrow-inside:before{content:"\f0f22"}.mdi-selection-ellipse-remove:before{content:"\f17c2"}.mdi-selection-marker:before{content:"\f1283"}.mdi-selection-multiple:before{content:"\f1285"}.mdi-selection-multiple-marker:before{content:"\f1284"}.mdi-selection-off:before{content:"\f0777"}.mdi-selection-remove:before{content:"\f17c3"}.mdi-selection-search:before{content:"\f1205"}.mdi-semantic-web:before{content:"\f1316"}.mdi-send:before{content:"\f048a"}.mdi-send-check:before{content:"\f1161"}.mdi-send-check-outline:before{content:"\f1162"}.mdi-send-circle:before{content:"\f0df8"}.mdi-send-circle-outline:before{content:"\f0df9"}.mdi-send-clock:before{content:"\f1163"}.mdi-send-clock-outline:before{content:"\f1164"}.mdi-send-lock:before{content:"\f07ed"}.mdi-send-lock-outline:before{content:"\f1166"}.mdi-send-outline:before{content:"\f1165"}.mdi-send-variant:before{content:"\f1c4d"}.mdi-send-variant-clock:before{content:"\f1c7e"}.mdi-send-variant-clock-outline:before{content:"\f1c7f"}.mdi-send-variant-outline:before{content:"\f1c4e"}.mdi-serial-port:before{content:"\f065c"}.mdi-server:before{content:"\f048b"}.mdi-server-minus:before{content:"\f048c"}.mdi-server-minus-outline:before{content:"\f1c98"}.mdi-server-network:before{content:"\f048d"}.mdi-server-network-off:before{content:"\f048e"}.mdi-server-network-outline:before{content:"\f1c99"}.mdi-server-off:before{content:"\f048f"}.mdi-server-outline:before{content:"\f1c9a"}.mdi-server-plus:before{content:"\f0490"}.mdi-server-plus-outline:before{content:"\f1c9b"}.mdi-server-remove:before{content:"\f0491"}.mdi-server-security:before{content:"\f0492"}.mdi-set-all:before{content:"\f0778"}.mdi-set-center:before{content:"\f0779"}.mdi-set-center-right:before{content:"\f077a"}.mdi-set-left:before{content:"\f077b"}.mdi-set-left-center:before{content:"\f077c"}.mdi-set-left-right:before{content:"\f077d"}.mdi-set-merge:before{content:"\f14e0"}.mdi-set-none:before{content:"\f077e"}.mdi-set-right:before{content:"\f077f"}.mdi-set-split:before{content:"\f14e1"}.mdi-set-square:before{content:"\f145d"}.mdi-set-top-box:before{content:"\f099f"}.mdi-settings-helper:before{content:"\f0a6e"}.mdi-shaker:before{content:"\f110e"}.mdi-shaker-outline:before{content:"\f110f"}.mdi-shape:before{content:"\f0831"}.mdi-shape-circle-plus:before{content:"\f065d"}.mdi-shape-outline:before{content:"\f0832"}.mdi-shape-oval-plus:before{content:"\f11fa"}.mdi-shape-plus:before{content:"\f0495"}.mdi-shape-plus-outline:before{content:"\f1c4f"}.mdi-shape-polygon-plus:before{content:"\f065e"}.mdi-shape-rectangle-plus:before{content:"\f065f"}.mdi-shape-square-plus:before{content:"\f0660"}.mdi-shape-square-rounded-plus:before{content:"\f14fa"}.mdi-share:before{content:"\f0496"}.mdi-share-all:before{content:"\f11f4"}.mdi-share-all-outline:before{content:"\f11f5"}.mdi-share-circle:before{content:"\f11ad"}.mdi-share-off:before{content:"\f0f23"}.mdi-share-off-outline:before{content:"\f0f24"}.mdi-share-outline:before{content:"\f0932"}.mdi-share-variant:before{content:"\f0497"}.mdi-share-variant-outline:before{content:"\f1514"}.mdi-shark:before{content:"\f18ba"}.mdi-shark-fin:before{content:"\f1673"}.mdi-shark-fin-outline:before{content:"\f1674"}.mdi-shark-off:before{content:"\f18bb"}.mdi-sheep:before{content:"\f0cc6"}.mdi-shield:before{content:"\f0498"}.mdi-shield-account:before{content:"\f088f"}.mdi-shield-account-outline:before{content:"\f0a12"}.mdi-shield-account-variant:before{content:"\f15a7"}.mdi-shield-account-variant-outline:before{content:"\f15a8"}.mdi-shield-airplane:before{content:"\f06bb"}.mdi-shield-airplane-outline:before{content:"\f0cc7"}.mdi-shield-alert:before{content:"\f0ecc"}.mdi-shield-alert-outline:before{content:"\f0ecd"}.mdi-shield-bug:before{content:"\f13da"}.mdi-shield-bug-outline:before{content:"\f13db"}.mdi-shield-car:before{content:"\f0f83"}.mdi-shield-check:before{content:"\f0565"}.mdi-shield-check-outline:before{content:"\f0cc8"}.mdi-shield-cross:before{content:"\f0cc9"}.mdi-shield-cross-outline:before{content:"\f0cca"}.mdi-shield-crown:before{content:"\f18bc"}.mdi-shield-crown-outline:before{content:"\f18bd"}.mdi-shield-edit:before{content:"\f11a0"}.mdi-shield-edit-outline:before{content:"\f11a1"}.mdi-shield-half:before{content:"\f1360"}.mdi-shield-half-full:before{content:"\f0780"}.mdi-shield-home:before{content:"\f068a"}.mdi-shield-home-outline:before{content:"\f0ccb"}.mdi-shield-key:before{content:"\f0bc4"}.mdi-shield-key-outline:before{content:"\f0bc5"}.mdi-shield-link-variant:before{content:"\f0d33"}.mdi-shield-link-variant-outline:before{content:"\f0d34"}.mdi-shield-lock:before{content:"\f099d"}.mdi-shield-lock-open:before{content:"\f199a"}.mdi-shield-lock-open-outline:before{content:"\f199b"}.mdi-shield-lock-outline:before{content:"\f0ccc"}.mdi-shield-moon:before{content:"\f1828"}.mdi-shield-moon-outline:before{content:"\f1829"}.mdi-shield-off:before{content:"\f099e"}.mdi-shield-off-outline:before{content:"\f099c"}.mdi-shield-outline:before{content:"\f0499"}.mdi-shield-plus:before{content:"\f0ada"}.mdi-shield-plus-outline:before{content:"\f0adb"}.mdi-shield-refresh:before{content:"\f00aa"}.mdi-shield-refresh-outline:before{content:"\f01e0"}.mdi-shield-remove:before{content:"\f0adc"}.mdi-shield-remove-outline:before{content:"\f0add"}.mdi-shield-search:before{content:"\f0d9a"}.mdi-shield-star:before{content:"\f113b"}.mdi-shield-star-outline:before{content:"\f113c"}.mdi-shield-sun:before{content:"\f105d"}.mdi-shield-sun-outline:before{content:"\f105e"}.mdi-shield-sword:before{content:"\f18be"}.mdi-shield-sword-outline:before{content:"\f18bf"}.mdi-shield-sync:before{content:"\f11a2"}.mdi-shield-sync-outline:before{content:"\f11a3"}.mdi-shimmer:before{content:"\f1545"}.mdi-ship-wheel:before{content:"\f0833"}.mdi-shipping-pallet:before{content:"\f184e"}.mdi-shoe-ballet:before{content:"\f15ca"}.mdi-shoe-cleat:before{content:"\f15c7"}.mdi-shoe-formal:before{content:"\f0b47"}.mdi-shoe-heel:before{content:"\f0b48"}.mdi-shoe-print:before{content:"\f0dfa"}.mdi-shoe-sneaker:before{content:"\f15c8"}.mdi-shopping:before{content:"\f049a"}.mdi-shopping-music:before{content:"\f049b"}.mdi-shopping-outline:before{content:"\f11d5"}.mdi-shopping-search:before{content:"\f0f84"}.mdi-shopping-search-outline:before{content:"\f1a6f"}.mdi-shore:before{content:"\f14f9"}.mdi-shovel:before{content:"\f0710"}.mdi-shovel-off:before{content:"\f0711"}.mdi-shower:before{content:"\f09a0"}.mdi-shower-head:before{content:"\f09a1"}.mdi-shredder:before{content:"\f049c"}.mdi-shuffle:before{content:"\f049d"}.mdi-shuffle-disabled:before{content:"\f049e"}.mdi-shuffle-variant:before{content:"\f049f"}.mdi-shuriken:before{content:"\f137f"}.mdi-sickle:before{content:"\f18c0"}.mdi-sigma:before{content:"\f04a0"}.mdi-sigma-lower:before{content:"\f062b"}.mdi-sign-caution:before{content:"\f04a1"}.mdi-sign-direction:before{content:"\f0781"}.mdi-sign-direction-minus:before{content:"\f1000"}.mdi-sign-direction-plus:before{content:"\f0fdc"}.mdi-sign-direction-remove:before{content:"\f0fdd"}.mdi-sign-language:before{content:"\f1b4d"}.mdi-sign-language-outline:before{content:"\f1b4e"}.mdi-sign-pole:before{content:"\f14f8"}.mdi-sign-real-estate:before{content:"\f1118"}.mdi-sign-text:before{content:"\f0782"}.mdi-sign-yield:before{content:"\f1baf"}.mdi-signal:before{content:"\f04a2"}.mdi-signal-2g:before{content:"\f0712"}.mdi-signal-3g:before{content:"\f0713"}.mdi-signal-4g:before{content:"\f0714"}.mdi-signal-5g:before{content:"\f0a6f"}.mdi-signal-cellular-1:before{content:"\f08bc"}.mdi-signal-cellular-2:before{content:"\f08bd"}.mdi-signal-cellular-3:before{content:"\f08be"}.mdi-signal-cellular-outline:before{content:"\f08bf"}.mdi-signal-distance-variant:before{content:"\f0e64"}.mdi-signal-hspa:before{content:"\f0715"}.mdi-signal-hspa-plus:before{content:"\f0716"}.mdi-signal-off:before{content:"\f0783"}.mdi-signal-variant:before{content:"\f060a"}.mdi-signature:before{content:"\f0dfb"}.mdi-signature-freehand:before{content:"\f0dfc"}.mdi-signature-image:before{content:"\f0dfd"}.mdi-signature-text:before{content:"\f0dfe"}.mdi-silo:before{content:"\f1b9f"}.mdi-silo-outline:before{content:"\f0b49"}.mdi-silverware:before{content:"\f04a3"}.mdi-silverware-clean:before{content:"\f0fde"}.mdi-silverware-fork:before{content:"\f04a4"}.mdi-silverware-fork-knife:before{content:"\f0a70"}.mdi-silverware-spoon:before{content:"\f04a5"}.mdi-silverware-variant:before{content:"\f04a6"}.mdi-sim:before{content:"\f04a7"}.mdi-sim-alert:before{content:"\f04a8"}.mdi-sim-alert-outline:before{content:"\f15d3"}.mdi-sim-off:before{content:"\f04a9"}.mdi-sim-off-outline:before{content:"\f15d4"}.mdi-sim-outline:before{content:"\f15d5"}.mdi-simple-icons:before{content:"\f131d"}.mdi-sina-weibo:before{content:"\f0adf"}.mdi-sine-wave:before{content:"\f095b"}.mdi-sitemap:before{content:"\f04aa"}.mdi-sitemap-outline:before{content:"\f199c"}.mdi-size-l:before{content:"\f13a6"}.mdi-size-m:before{content:"\f13a5"}.mdi-size-s:before{content:"\f13a4"}.mdi-size-xl:before{content:"\f13a7"}.mdi-size-xs:before{content:"\f13a3"}.mdi-size-xxl:before{content:"\f13a8"}.mdi-size-xxs:before{content:"\f13a2"}.mdi-size-xxxl:before{content:"\f13a9"}.mdi-skate:before{content:"\f0d35"}.mdi-skate-off:before{content:"\f0699"}.mdi-skateboard:before{content:"\f14c2"}.mdi-skateboarding:before{content:"\f0501"}.mdi-skew-less:before{content:"\f0d36"}.mdi-skew-more:before{content:"\f0d37"}.mdi-ski:before{content:"\f1304"}.mdi-ski-cross-country:before{content:"\f1305"}.mdi-ski-water:before{content:"\f1306"}.mdi-skip-backward:before{content:"\f04ab"}.mdi-skip-backward-outline:before{content:"\f0f25"}.mdi-skip-forward:before{content:"\f04ac"}.mdi-skip-forward-outline:before{content:"\f0f26"}.mdi-skip-next:before{content:"\f04ad"}.mdi-skip-next-circle:before{content:"\f0661"}.mdi-skip-next-circle-outline:before{content:"\f0662"}.mdi-skip-next-outline:before{content:"\f0f27"}.mdi-skip-previous:before{content:"\f04ae"}.mdi-skip-previous-circle:before{content:"\f0663"}.mdi-skip-previous-circle-outline:before{content:"\f0664"}.mdi-skip-previous-outline:before{content:"\f0f28"}.mdi-skull:before{content:"\f068c"}.mdi-skull-crossbones:before{content:"\f0bc6"}.mdi-skull-crossbones-outline:before{content:"\f0bc7"}.mdi-skull-outline:before{content:"\f0bc8"}.mdi-skull-scan:before{content:"\f14c7"}.mdi-skull-scan-outline:before{content:"\f14c8"}.mdi-skype:before{content:"\f04af"}.mdi-skype-business:before{content:"\f04b0"}.mdi-slack:before{content:"\f04b1"}.mdi-slash-forward:before{content:"\f0fdf"}.mdi-slash-forward-box:before{content:"\f0fe0"}.mdi-sledding:before{content:"\f041b"}.mdi-sleep:before{content:"\f04b2"}.mdi-sleep-off:before{content:"\f04b3"}.mdi-slide:before{content:"\f15a5"}.mdi-slope-downhill:before{content:"\f0dff"}.mdi-slope-uphill:before{content:"\f0e00"}.mdi-slot-machine:before{content:"\f1114"}.mdi-slot-machine-outline:before{content:"\f1115"}.mdi-smart-card:before{content:"\f10bd"}.mdi-smart-card-off:before{content:"\f18f7"}.mdi-smart-card-off-outline:before{content:"\f18f8"}.mdi-smart-card-outline:before{content:"\f10be"}.mdi-smart-card-reader:before{content:"\f10bf"}.mdi-smart-card-reader-outline:before{content:"\f10c0"}.mdi-smog:before{content:"\f0a71"}.mdi-smoke:before{content:"\f1799"}.mdi-smoke-detector:before{content:"\f0392"}.mdi-smoke-detector-alert:before{content:"\f192e"}.mdi-smoke-detector-alert-outline:before{content:"\f192f"}.mdi-smoke-detector-off:before{content:"\f1809"}.mdi-smoke-detector-off-outline:before{content:"\f180a"}.mdi-smoke-detector-outline:before{content:"\f1808"}.mdi-smoke-detector-variant:before{content:"\f180b"}.mdi-smoke-detector-variant-alert:before{content:"\f1930"}.mdi-smoke-detector-variant-off:before{content:"\f180c"}.mdi-smoking:before{content:"\f04b4"}.mdi-smoking-off:before{content:"\f04b5"}.mdi-smoking-pipe:before{content:"\f140d"}.mdi-smoking-pipe-off:before{content:"\f1428"}.mdi-snail:before{content:"\f1677"}.mdi-snake:before{content:"\f150e"}.mdi-snapchat:before{content:"\f04b6"}.mdi-snowboard:before{content:"\f1307"}.mdi-snowflake:before{content:"\f0717"}.mdi-snowflake-alert:before{content:"\f0f29"}.mdi-snowflake-check:before{content:"\f1a70"}.mdi-snowflake-melt:before{content:"\f12cb"}.mdi-snowflake-off:before{content:"\f14e3"}.mdi-snowflake-thermometer:before{content:"\f1a71"}.mdi-snowflake-variant:before{content:"\f0f2a"}.mdi-snowman:before{content:"\f04b7"}.mdi-snowmobile:before{content:"\f06dd"}.mdi-snowshoeing:before{content:"\f1a72"}.mdi-soccer:before{content:"\f04b8"}.mdi-soccer-field:before{content:"\f0834"}.mdi-social-distance-2-meters:before{content:"\f1579"}.mdi-social-distance-6-feet:before{content:"\f157a"}.mdi-sofa:before{content:"\f04b9"}.mdi-sofa-outline:before{content:"\f156d"}.mdi-sofa-single:before{content:"\f156e"}.mdi-sofa-single-outline:before{content:"\f156f"}.mdi-solar-panel:before{content:"\f0d9b"}.mdi-solar-panel-large:before{content:"\f0d9c"}.mdi-solar-power:before{content:"\f0a72"}.mdi-solar-power-variant:before{content:"\f1a73"}.mdi-solar-power-variant-outline:before{content:"\f1a74"}.mdi-soldering-iron:before{content:"\f1092"}.mdi-solid:before{content:"\f068d"}.mdi-sony-playstation:before{content:"\f0414"}.mdi-sort:before{content:"\f04ba"}.mdi-sort-alphabetical-ascending:before{content:"\f05bd"}.mdi-sort-alphabetical-ascending-variant:before{content:"\f1148"}.mdi-sort-alphabetical-descending:before{content:"\f05bf"}.mdi-sort-alphabetical-descending-variant:before{content:"\f1149"}.mdi-sort-alphabetical-variant:before{content:"\f04bb"}.mdi-sort-ascending:before{content:"\f04bc"}.mdi-sort-bool-ascending:before{content:"\f1385"}.mdi-sort-bool-ascending-variant:before{content:"\f1386"}.mdi-sort-bool-descending:before{content:"\f1387"}.mdi-sort-bool-descending-variant:before{content:"\f1388"}.mdi-sort-calendar-ascending:before{content:"\f1547"}.mdi-sort-calendar-descending:before{content:"\f1548"}.mdi-sort-clock-ascending:before{content:"\f1549"}.mdi-sort-clock-ascending-outline:before{content:"\f154a"}.mdi-sort-clock-descending:before{content:"\f154b"}.mdi-sort-clock-descending-outline:before{content:"\f154c"}.mdi-sort-descending:before{content:"\f04bd"}.mdi-sort-numeric-ascending:before{content:"\f1389"}.mdi-sort-numeric-ascending-variant:before{content:"\f090d"}.mdi-sort-numeric-descending:before{content:"\f138a"}.mdi-sort-numeric-descending-variant:before{content:"\f0ad2"}.mdi-sort-numeric-variant:before{content:"\f04be"}.mdi-sort-reverse-variant:before{content:"\f033c"}.mdi-sort-variant:before{content:"\f04bf"}.mdi-sort-variant-lock:before{content:"\f0ccd"}.mdi-sort-variant-lock-open:before{content:"\f0cce"}.mdi-sort-variant-off:before{content:"\f1abb"}.mdi-sort-variant-remove:before{content:"\f1147"}.mdi-soundbar:before{content:"\f17db"}.mdi-soundcloud:before{content:"\f04c0"}.mdi-source-branch:before{content:"\f062c"}.mdi-source-branch-check:before{content:"\f14cf"}.mdi-source-branch-minus:before{content:"\f14cb"}.mdi-source-branch-plus:before{content:"\f14ca"}.mdi-source-branch-refresh:before{content:"\f14cd"}.mdi-source-branch-remove:before{content:"\f14cc"}.mdi-source-branch-sync:before{content:"\f14ce"}.mdi-source-commit:before{content:"\f0718"}.mdi-source-commit-end:before{content:"\f0719"}.mdi-source-commit-end-local:before{content:"\f071a"}.mdi-source-commit-local:before{content:"\f071b"}.mdi-source-commit-next-local:before{content:"\f071c"}.mdi-source-commit-start:before{content:"\f071d"}.mdi-source-commit-start-next-local:before{content:"\f071e"}.mdi-source-fork:before{content:"\f04c1"}.mdi-source-merge:before{content:"\f062d"}.mdi-source-pull:before{content:"\f04c2"}.mdi-source-repository:before{content:"\f0ccf"}.mdi-source-repository-multiple:before{content:"\f0cd0"}.mdi-soy-sauce:before{content:"\f07ee"}.mdi-soy-sauce-off:before{content:"\f13fc"}.mdi-spa:before{content:"\f0cd1"}.mdi-spa-outline:before{content:"\f0cd2"}.mdi-space-invaders:before{content:"\f0bc9"}.mdi-space-station:before{content:"\f1383"}.mdi-spade:before{content:"\f0e65"}.mdi-speaker:before{content:"\f04c3"}.mdi-speaker-bluetooth:before{content:"\f09a2"}.mdi-speaker-message:before{content:"\f1b11"}.mdi-speaker-multiple:before{content:"\f0d38"}.mdi-speaker-off:before{content:"\f04c4"}.mdi-speaker-pause:before{content:"\f1b73"}.mdi-speaker-play:before{content:"\f1b72"}.mdi-speaker-stop:before{content:"\f1b74"}.mdi-speaker-wireless:before{content:"\f071f"}.mdi-spear:before{content:"\f1845"}.mdi-speedometer:before{content:"\f04c5"}.mdi-speedometer-medium:before{content:"\f0f85"}.mdi-speedometer-slow:before{content:"\f0f86"}.mdi-spellcheck:before{content:"\f04c6"}.mdi-sphere:before{content:"\f1954"}.mdi-sphere-off:before{content:"\f1955"}.mdi-spider:before{content:"\f11ea"}.mdi-spider-outline:before{content:"\f1c75"}.mdi-spider-thread:before{content:"\f11eb"}.mdi-spider-web:before{content:"\f0bca"}.mdi-spirit-level:before{content:"\f14f1"}.mdi-spoon-sugar:before{content:"\f1429"}.mdi-spotify:before{content:"\f04c7"}.mdi-spotlight:before{content:"\f04c8"}.mdi-spotlight-beam:before{content:"\f04c9"}.mdi-spray:before{content:"\f0665"}.mdi-spray-bottle:before{content:"\f0ae0"}.mdi-sprinkler:before{content:"\f105f"}.mdi-sprinkler-fire:before{content:"\f199d"}.mdi-sprinkler-variant:before{content:"\f1060"}.mdi-sprout:before{content:"\f0e66"}.mdi-sprout-outline:before{content:"\f0e67"}.mdi-square:before{content:"\f0764"}.mdi-square-circle:before{content:"\f1500"}.mdi-square-circle-outline:before{content:"\f1c50"}.mdi-square-edit-outline:before{content:"\f090c"}.mdi-square-medium:before{content:"\f0a13"}.mdi-square-medium-outline:before{content:"\f0a14"}.mdi-square-off:before{content:"\f12ee"}.mdi-square-off-outline:before{content:"\f12ef"}.mdi-square-opacity:before{content:"\f1854"}.mdi-square-outline:before{content:"\f0763"}.mdi-square-root:before{content:"\f0784"}.mdi-square-root-box:before{content:"\f09a3"}.mdi-square-rounded:before{content:"\f14fb"}.mdi-square-rounded-badge:before{content:"\f1a07"}.mdi-square-rounded-badge-outline:before{content:"\f1a08"}.mdi-square-rounded-outline:before{content:"\f14fc"}.mdi-square-small:before{content:"\f0a15"}.mdi-square-wave:before{content:"\f147b"}.mdi-squeegee:before{content:"\f0ae1"}.mdi-ssh:before{content:"\f08c0"}.mdi-stack-exchange:before{content:"\f060b"}.mdi-stack-overflow:before{content:"\f04cc"}.mdi-stackpath:before{content:"\f0359"}.mdi-stadium:before{content:"\f0ff9"}.mdi-stadium-outline:before{content:"\f1b03"}.mdi-stadium-variant:before{content:"\f0720"}.mdi-stairs:before{content:"\f04cd"}.mdi-stairs-box:before{content:"\f139e"}.mdi-stairs-down:before{content:"\f12be"}.mdi-stairs-up:before{content:"\f12bd"}.mdi-stamper:before{content:"\f0d39"}.mdi-standard-definition:before{content:"\f07ef"}.mdi-star:before{content:"\f04ce"}.mdi-star-box:before{content:"\f0a73"}.mdi-star-box-multiple:before{content:"\f1286"}.mdi-star-box-multiple-outline:before{content:"\f1287"}.mdi-star-box-outline:before{content:"\f0a74"}.mdi-star-check:before{content:"\f1566"}.mdi-star-check-outline:before{content:"\f156a"}.mdi-star-circle:before{content:"\f04cf"}.mdi-star-circle-outline:before{content:"\f09a4"}.mdi-star-cog:before{content:"\f1668"}.mdi-star-cog-outline:before{content:"\f1669"}.mdi-star-crescent:before{content:"\f0979"}.mdi-star-david:before{content:"\f097a"}.mdi-star-face:before{content:"\f09a5"}.mdi-star-four-points:before{content:"\f0ae2"}.mdi-star-four-points-box:before{content:"\f1c51"}.mdi-star-four-points-box-outline:before{content:"\f1c52"}.mdi-star-four-points-circle:before{content:"\f1c53"}.mdi-star-four-points-circle-outline:before{content:"\f1c54"}.mdi-star-four-points-outline:before{content:"\f0ae3"}.mdi-star-four-points-small:before{content:"\f1c55"}.mdi-star-half:before{content:"\f0246"}.mdi-star-half-full:before{content:"\f04d0"}.mdi-star-minus:before{content:"\f1564"}.mdi-star-minus-outline:before{content:"\f1568"}.mdi-star-off:before{content:"\f04d1"}.mdi-star-off-outline:before{content:"\f155b"}.mdi-star-outline:before{content:"\f04d2"}.mdi-star-plus:before{content:"\f1563"}.mdi-star-plus-outline:before{content:"\f1567"}.mdi-star-remove:before{content:"\f1565"}.mdi-star-remove-outline:before{content:"\f1569"}.mdi-star-settings:before{content:"\f166a"}.mdi-star-settings-outline:before{content:"\f166b"}.mdi-star-shooting:before{content:"\f1741"}.mdi-star-shooting-outline:before{content:"\f1742"}.mdi-star-three-points:before{content:"\f0ae4"}.mdi-star-three-points-outline:before{content:"\f0ae5"}.mdi-state-machine:before{content:"\f11ef"}.mdi-steam:before{content:"\f04d3"}.mdi-steering:before{content:"\f04d4"}.mdi-steering-off:before{content:"\f090e"}.mdi-step-backward:before{content:"\f04d5"}.mdi-step-backward-2:before{content:"\f04d6"}.mdi-step-forward:before{content:"\f04d7"}.mdi-step-forward-2:before{content:"\f04d8"}.mdi-stethoscope:before{content:"\f04d9"}.mdi-sticker:before{content:"\f1364"}.mdi-sticker-alert:before{content:"\f1365"}.mdi-sticker-alert-outline:before{content:"\f1366"}.mdi-sticker-check:before{content:"\f1367"}.mdi-sticker-check-outline:before{content:"\f1368"}.mdi-sticker-circle-outline:before{content:"\f05d0"}.mdi-sticker-emoji:before{content:"\f0785"}.mdi-sticker-minus:before{content:"\f1369"}.mdi-sticker-minus-outline:before{content:"\f136a"}.mdi-sticker-outline:before{content:"\f136b"}.mdi-sticker-plus:before{content:"\f136c"}.mdi-sticker-plus-outline:before{content:"\f136d"}.mdi-sticker-remove:before{content:"\f136e"}.mdi-sticker-remove-outline:before{content:"\f136f"}.mdi-sticker-text:before{content:"\f178e"}.mdi-sticker-text-outline:before{content:"\f178f"}.mdi-stocking:before{content:"\f04da"}.mdi-stomach:before{content:"\f1093"}.mdi-stool:before{content:"\f195d"}.mdi-stool-outline:before{content:"\f195e"}.mdi-stop:before{content:"\f04db"}.mdi-stop-circle:before{content:"\f0666"}.mdi-stop-circle-outline:before{content:"\f0667"}.mdi-storage-tank:before{content:"\f1a75"}.mdi-storage-tank-outline:before{content:"\f1a76"}.mdi-store:before{content:"\f04dc"}.mdi-store-24-hour:before{content:"\f04dd"}.mdi-store-alert:before{content:"\f18c1"}.mdi-store-alert-outline:before{content:"\f18c2"}.mdi-store-check:before{content:"\f18c3"}.mdi-store-check-outline:before{content:"\f18c4"}.mdi-store-clock:before{content:"\f18c5"}.mdi-store-clock-outline:before{content:"\f18c6"}.mdi-store-cog:before{content:"\f18c7"}.mdi-store-cog-outline:before{content:"\f18c8"}.mdi-store-edit:before{content:"\f18c9"}.mdi-store-edit-outline:before{content:"\f18ca"}.mdi-store-marker:before{content:"\f18cb"}.mdi-store-marker-outline:before{content:"\f18cc"}.mdi-store-minus:before{content:"\f165e"}.mdi-store-minus-outline:before{content:"\f18cd"}.mdi-store-off:before{content:"\f18ce"}.mdi-store-off-outline:before{content:"\f18cf"}.mdi-store-outline:before{content:"\f1361"}.mdi-store-plus:before{content:"\f165f"}.mdi-store-plus-outline:before{content:"\f18d0"}.mdi-store-remove:before{content:"\f1660"}.mdi-store-remove-outline:before{content:"\f18d1"}.mdi-store-search:before{content:"\f18d2"}.mdi-store-search-outline:before{content:"\f18d3"}.mdi-store-settings:before{content:"\f18d4"}.mdi-store-settings-outline:before{content:"\f18d5"}.mdi-storefront:before{content:"\f07c7"}.mdi-storefront-check:before{content:"\f1b7d"}.mdi-storefront-check-outline:before{content:"\f1b7e"}.mdi-storefront-edit:before{content:"\f1b7f"}.mdi-storefront-edit-outline:before{content:"\f1b80"}.mdi-storefront-minus:before{content:"\f1b83"}.mdi-storefront-minus-outline:before{content:"\f1b84"}.mdi-storefront-outline:before{content:"\f10c1"}.mdi-storefront-plus:before{content:"\f1b81"}.mdi-storefront-plus-outline:before{content:"\f1b82"}.mdi-storefront-remove:before{content:"\f1b85"}.mdi-storefront-remove-outline:before{content:"\f1b86"}.mdi-stove:before{content:"\f04de"}.mdi-strategy:before{content:"\f11d6"}.mdi-stretch-to-page:before{content:"\f0f2b"}.mdi-stretch-to-page-outline:before{content:"\f0f2c"}.mdi-string-lights:before{content:"\f12ba"}.mdi-string-lights-off:before{content:"\f12bb"}.mdi-subdirectory-arrow-left:before{content:"\f060c"}.mdi-subdirectory-arrow-right:before{content:"\f060d"}.mdi-submarine:before{content:"\f156c"}.mdi-subtitles:before{content:"\f0a16"}.mdi-subtitles-outline:before{content:"\f0a17"}.mdi-subway:before{content:"\f06ac"}.mdi-subway-alert-variant:before{content:"\f0d9d"}.mdi-subway-variant:before{content:"\f04df"}.mdi-summit:before{content:"\f0786"}.mdi-sun-angle:before{content:"\f1b27"}.mdi-sun-angle-outline:before{content:"\f1b28"}.mdi-sun-clock:before{content:"\f1a77"}.mdi-sun-clock-outline:before{content:"\f1a78"}.mdi-sun-compass:before{content:"\f19a5"}.mdi-sun-snowflake:before{content:"\f1796"}.mdi-sun-snowflake-variant:before{content:"\f1a79"}.mdi-sun-thermometer:before{content:"\f18d6"}.mdi-sun-thermometer-outline:before{content:"\f18d7"}.mdi-sun-wireless:before{content:"\f17fe"}.mdi-sun-wireless-outline:before{content:"\f17ff"}.mdi-sunglasses:before{content:"\f04e0"}.mdi-surfing:before{content:"\f1746"}.mdi-surround-sound:before{content:"\f05c5"}.mdi-surround-sound-2-0:before{content:"\f07f0"}.mdi-surround-sound-2-1:before{content:"\f1729"}.mdi-surround-sound-3-1:before{content:"\f07f1"}.mdi-surround-sound-5-1:before{content:"\f07f2"}.mdi-surround-sound-5-1-2:before{content:"\f172a"}.mdi-surround-sound-7-1:before{content:"\f07f3"}.mdi-svg:before{content:"\f0721"}.mdi-swap-horizontal:before{content:"\f04e1"}.mdi-swap-horizontal-bold:before{content:"\f0bcd"}.mdi-swap-horizontal-circle:before{content:"\f0fe1"}.mdi-swap-horizontal-circle-outline:before{content:"\f0fe2"}.mdi-swap-horizontal-hidden:before{content:"\f1d0e"}.mdi-swap-horizontal-variant:before{content:"\f08c1"}.mdi-swap-vertical:before{content:"\f04e2"}.mdi-swap-vertical-bold:before{content:"\f0bce"}.mdi-swap-vertical-circle:before{content:"\f0fe3"}.mdi-swap-vertical-circle-outline:before{content:"\f0fe4"}.mdi-swap-vertical-variant:before{content:"\f08c2"}.mdi-swim:before{content:"\f04e3"}.mdi-switch:before{content:"\f04e4"}.mdi-sword:before{content:"\f04e5"}.mdi-sword-cross:before{content:"\f0787"}.mdi-syllabary-hangul:before{content:"\f1333"}.mdi-syllabary-hiragana:before{content:"\f1334"}.mdi-syllabary-katakana:before{content:"\f1335"}.mdi-syllabary-katakana-halfwidth:before{content:"\f1336"}.mdi-symbol:before{content:"\f1501"}.mdi-symfony:before{content:"\f0ae6"}.mdi-synagogue:before{content:"\f1b04"}.mdi-synagogue-outline:before{content:"\f1b05"}.mdi-sync:before{content:"\f04e6"}.mdi-sync-alert:before{content:"\f04e7"}.mdi-sync-circle:before{content:"\f1378"}.mdi-sync-off:before{content:"\f04e8"}.mdi-tab:before{content:"\f04e9"}.mdi-tab-minus:before{content:"\f0b4b"}.mdi-tab-plus:before{content:"\f075c"}.mdi-tab-remove:before{content:"\f0b4c"}.mdi-tab-search:before{content:"\f199e"}.mdi-tab-unselected:before{content:"\f04ea"}.mdi-table:before{content:"\f04eb"}.mdi-table-account:before{content:"\f13b9"}.mdi-table-alert:before{content:"\f13ba"}.mdi-table-arrow-down:before{content:"\f13bb"}.mdi-table-arrow-left:before{content:"\f13bc"}.mdi-table-arrow-right:before{content:"\f13bd"}.mdi-table-arrow-up:before{content:"\f13be"}.mdi-table-border:before{content:"\f0a18"}.mdi-table-cancel:before{content:"\f13bf"}.mdi-table-chair:before{content:"\f1061"}.mdi-table-check:before{content:"\f13c0"}.mdi-table-clock:before{content:"\f13c1"}.mdi-table-cog:before{content:"\f13c2"}.mdi-table-column:before{content:"\f0835"}.mdi-table-column-plus-after:before{content:"\f04ec"}.mdi-table-column-plus-before:before{content:"\f04ed"}.mdi-table-column-remove:before{content:"\f04ee"}.mdi-table-column-width:before{content:"\f04ef"}.mdi-table-edit:before{content:"\f04f0"}.mdi-table-eye:before{content:"\f1094"}.mdi-table-eye-off:before{content:"\f13c3"}.mdi-table-filter:before{content:"\f1b8c"}.mdi-table-furniture:before{content:"\f05bc"}.mdi-table-headers-eye:before{content:"\f121d"}.mdi-table-headers-eye-off:before{content:"\f121e"}.mdi-table-heart:before{content:"\f13c4"}.mdi-table-key:before{content:"\f13c5"}.mdi-table-large:before{content:"\f04f1"}.mdi-table-large-plus:before{content:"\f0f87"}.mdi-table-large-remove:before{content:"\f0f88"}.mdi-table-lock:before{content:"\f13c6"}.mdi-table-merge-cells:before{content:"\f09a6"}.mdi-table-minus:before{content:"\f13c7"}.mdi-table-multiple:before{content:"\f13c8"}.mdi-table-network:before{content:"\f13c9"}.mdi-table-of-contents:before{content:"\f0836"}.mdi-table-off:before{content:"\f13ca"}.mdi-table-picnic:before{content:"\f1743"}.mdi-table-pivot:before{content:"\f183c"}.mdi-table-plus:before{content:"\f0a75"}.mdi-table-question:before{content:"\f1b21"}.mdi-table-refresh:before{content:"\f13a0"}.mdi-table-remove:before{content:"\f0a76"}.mdi-table-row:before{content:"\f0837"}.mdi-table-row-height:before{content:"\f04f2"}.mdi-table-row-plus-after:before{content:"\f04f3"}.mdi-table-row-plus-before:before{content:"\f04f4"}.mdi-table-row-remove:before{content:"\f04f5"}.mdi-table-search:before{content:"\f090f"}.mdi-table-settings:before{content:"\f0838"}.mdi-table-split-cell:before{content:"\f142a"}.mdi-table-star:before{content:"\f13cb"}.mdi-table-sync:before{content:"\f13a1"}.mdi-table-tennis:before{content:"\f0e68"}.mdi-tablet:before{content:"\f04f6"}.mdi-tablet-cellphone:before{content:"\f09a7"}.mdi-tablet-dashboard:before{content:"\f0ece"}.mdi-taco:before{content:"\f0762"}.mdi-tag:before{content:"\f04f9"}.mdi-tag-arrow-down:before{content:"\f172b"}.mdi-tag-arrow-down-outline:before{content:"\f172c"}.mdi-tag-arrow-left:before{content:"\f172d"}.mdi-tag-arrow-left-outline:before{content:"\f172e"}.mdi-tag-arrow-right:before{content:"\f172f"}.mdi-tag-arrow-right-outline:before{content:"\f1730"}.mdi-tag-arrow-up:before{content:"\f1731"}.mdi-tag-arrow-up-outline:before{content:"\f1732"}.mdi-tag-check:before{content:"\f1a7a"}.mdi-tag-check-outline:before{content:"\f1a7b"}.mdi-tag-edit:before{content:"\f1c9c"}.mdi-tag-edit-outline:before{content:"\f1c9d"}.mdi-tag-faces:before{content:"\f04fa"}.mdi-tag-heart:before{content:"\f068b"}.mdi-tag-heart-outline:before{content:"\f0bcf"}.mdi-tag-hidden:before{content:"\f1c76"}.mdi-tag-minus:before{content:"\f0910"}.mdi-tag-minus-outline:before{content:"\f121f"}.mdi-tag-multiple:before{content:"\f04fb"}.mdi-tag-multiple-outline:before{content:"\f12f7"}.mdi-tag-off:before{content:"\f1220"}.mdi-tag-off-outline:before{content:"\f1221"}.mdi-tag-outline:before{content:"\f04fc"}.mdi-tag-plus:before{content:"\f0722"}.mdi-tag-plus-outline:before{content:"\f1222"}.mdi-tag-remove:before{content:"\f0723"}.mdi-tag-remove-outline:before{content:"\f1223"}.mdi-tag-search:before{content:"\f1907"}.mdi-tag-search-outline:before{content:"\f1908"}.mdi-tag-text:before{content:"\f1224"}.mdi-tag-text-outline:before{content:"\f04fd"}.mdi-tailwind:before{content:"\f13ff"}.mdi-tally-mark-1:before{content:"\f1abc"}.mdi-tally-mark-2:before{content:"\f1abd"}.mdi-tally-mark-3:before{content:"\f1abe"}.mdi-tally-mark-4:before{content:"\f1abf"}.mdi-tally-mark-5:before{content:"\f1ac0"}.mdi-tangram:before{content:"\f04f8"}.mdi-tank:before{content:"\f0d3a"}.mdi-tanker-truck:before{content:"\f0fe5"}.mdi-tape-drive:before{content:"\f16df"}.mdi-tape-measure:before{content:"\f0b4d"}.mdi-target:before{content:"\f04fe"}.mdi-target-account:before{content:"\f0bd0"}.mdi-target-variant:before{content:"\f0a77"}.mdi-taxi:before{content:"\f04ff"}.mdi-tea:before{content:"\f0d9e"}.mdi-tea-outline:before{content:"\f0d9f"}.mdi-teamviewer:before{content:"\f0500"}.mdi-teddy-bear:before{content:"\f18fb"}.mdi-telescope:before{content:"\f0b4e"}.mdi-television:before{content:"\f0502"}.mdi-television-ambient-light:before{content:"\f1356"}.mdi-television-box:before{content:"\f0839"}.mdi-television-classic:before{content:"\f07f4"}.mdi-television-classic-off:before{content:"\f083a"}.mdi-television-guide:before{content:"\f0503"}.mdi-television-off:before{content:"\f083b"}.mdi-television-pause:before{content:"\f0f89"}.mdi-television-play:before{content:"\f0ecf"}.mdi-television-shimmer:before{content:"\f1110"}.mdi-television-speaker:before{content:"\f1b1b"}.mdi-television-speaker-off:before{content:"\f1b1c"}.mdi-television-stop:before{content:"\f0f8a"}.mdi-temperature-celsius:before{content:"\f0504"}.mdi-temperature-fahrenheit:before{content:"\f0505"}.mdi-temperature-kelvin:before{content:"\f0506"}.mdi-temple-buddhist:before{content:"\f1b06"}.mdi-temple-buddhist-outline:before{content:"\f1b07"}.mdi-temple-hindu:before{content:"\f1b08"}.mdi-temple-hindu-outline:before{content:"\f1b09"}.mdi-tennis:before{content:"\f0da0"}.mdi-tennis-ball:before{content:"\f0507"}.mdi-tennis-ball-outline:before{content:"\f1c5f"}.mdi-tent:before{content:"\f0508"}.mdi-terraform:before{content:"\f1062"}.mdi-terrain:before{content:"\f0509"}.mdi-test-tube:before{content:"\f0668"}.mdi-test-tube-empty:before{content:"\f0911"}.mdi-test-tube-off:before{content:"\f0912"}.mdi-text:before{content:"\f09a8"}.mdi-text-account:before{content:"\f1570"}.mdi-text-box:before{content:"\f021a"}.mdi-text-box-check:before{content:"\f0ea6"}.mdi-text-box-check-outline:before{content:"\f0ea7"}.mdi-text-box-edit:before{content:"\f1a7c"}.mdi-text-box-edit-outline:before{content:"\f1a7d"}.mdi-text-box-minus:before{content:"\f0ea8"}.mdi-text-box-minus-outline:before{content:"\f0ea9"}.mdi-text-box-multiple:before{content:"\f0ab7"}.mdi-text-box-multiple-outline:before{content:"\f0ab8"}.mdi-text-box-outline:before{content:"\f09ed"}.mdi-text-box-plus:before{content:"\f0eaa"}.mdi-text-box-plus-outline:before{content:"\f0eab"}.mdi-text-box-remove:before{content:"\f0eac"}.mdi-text-box-remove-outline:before{content:"\f0ead"}.mdi-text-box-search:before{content:"\f0eae"}.mdi-text-box-search-outline:before{content:"\f0eaf"}.mdi-text-long:before{content:"\f09aa"}.mdi-text-recognition:before{content:"\f113d"}.mdi-text-search:before{content:"\f13b8"}.mdi-text-search-variant:before{content:"\f1a7e"}.mdi-text-shadow:before{content:"\f0669"}.mdi-text-short:before{content:"\f09a9"}.mdi-texture:before{content:"\f050c"}.mdi-texture-box:before{content:"\f0fe6"}.mdi-theater:before{content:"\f050d"}.mdi-theme-light-dark:before{content:"\f050e"}.mdi-thermometer:before{content:"\f050f"}.mdi-thermometer-alert:before{content:"\f0e01"}.mdi-thermometer-auto:before{content:"\f1b0f"}.mdi-thermometer-bluetooth:before{content:"\f1895"}.mdi-thermometer-check:before{content:"\f1a7f"}.mdi-thermometer-chevron-down:before{content:"\f0e02"}.mdi-thermometer-chevron-up:before{content:"\f0e03"}.mdi-thermometer-high:before{content:"\f10c2"}.mdi-thermometer-lines:before{content:"\f0510"}.mdi-thermometer-low:before{content:"\f10c3"}.mdi-thermometer-minus:before{content:"\f0e04"}.mdi-thermometer-off:before{content:"\f1531"}.mdi-thermometer-plus:before{content:"\f0e05"}.mdi-thermometer-probe:before{content:"\f1b2b"}.mdi-thermometer-probe-off:before{content:"\f1b2c"}.mdi-thermometer-water:before{content:"\f1a80"}.mdi-thermostat:before{content:"\f0393"}.mdi-thermostat-auto:before{content:"\f1b17"}.mdi-thermostat-box:before{content:"\f0891"}.mdi-thermostat-box-auto:before{content:"\f1b18"}.mdi-thermostat-cog:before{content:"\f1c80"}.mdi-thought-bubble:before{content:"\f07f6"}.mdi-thought-bubble-outline:before{content:"\f07f7"}.mdi-thumb-down:before{content:"\f0511"}.mdi-thumb-down-outline:before{content:"\f0512"}.mdi-thumb-up:before{content:"\f0513"}.mdi-thumb-up-outline:before{content:"\f0514"}.mdi-thumbs-up-down:before{content:"\f0515"}.mdi-thumbs-up-down-outline:before{content:"\f1914"}.mdi-ticket:before{content:"\f0516"}.mdi-ticket-account:before{content:"\f0517"}.mdi-ticket-confirmation:before{content:"\f0518"}.mdi-ticket-confirmation-outline:before{content:"\f13aa"}.mdi-ticket-outline:before{content:"\f0913"}.mdi-ticket-percent:before{content:"\f0724"}.mdi-ticket-percent-outline:before{content:"\f142b"}.mdi-tie:before{content:"\f0519"}.mdi-tilde:before{content:"\f0725"}.mdi-tilde-off:before{content:"\f18f3"}.mdi-timelapse:before{content:"\f051a"}.mdi-timeline:before{content:"\f0bd1"}.mdi-timeline-alert:before{content:"\f0f95"}.mdi-timeline-alert-outline:before{content:"\f0f98"}.mdi-timeline-check:before{content:"\f1532"}.mdi-timeline-check-outline:before{content:"\f1533"}.mdi-timeline-clock:before{content:"\f11fb"}.mdi-timeline-clock-outline:before{content:"\f11fc"}.mdi-timeline-minus:before{content:"\f1534"}.mdi-timeline-minus-outline:before{content:"\f1535"}.mdi-timeline-outline:before{content:"\f0bd2"}.mdi-timeline-plus:before{content:"\f0f96"}.mdi-timeline-plus-outline:before{content:"\f0f97"}.mdi-timeline-question:before{content:"\f0f99"}.mdi-timeline-question-outline:before{content:"\f0f9a"}.mdi-timeline-remove:before{content:"\f1536"}.mdi-timeline-remove-outline:before{content:"\f1537"}.mdi-timeline-text:before{content:"\f0bd3"}.mdi-timeline-text-outline:before{content:"\f0bd4"}.mdi-timer:before{content:"\f13ab"}.mdi-timer-10:before{content:"\f051c"}.mdi-timer-3:before{content:"\f051d"}.mdi-timer-alert:before{content:"\f1acc"}.mdi-timer-alert-outline:before{content:"\f1acd"}.mdi-timer-cancel:before{content:"\f1ace"}.mdi-timer-cancel-outline:before{content:"\f1acf"}.mdi-timer-check:before{content:"\f1ad0"}.mdi-timer-check-outline:before{content:"\f1ad1"}.mdi-timer-cog:before{content:"\f1925"}.mdi-timer-cog-outline:before{content:"\f1926"}.mdi-timer-edit:before{content:"\f1ad2"}.mdi-timer-edit-outline:before{content:"\f1ad3"}.mdi-timer-lock:before{content:"\f1ad4"}.mdi-timer-lock-open:before{content:"\f1ad5"}.mdi-timer-lock-open-outline:before{content:"\f1ad6"}.mdi-timer-lock-outline:before{content:"\f1ad7"}.mdi-timer-marker:before{content:"\f1ad8"}.mdi-timer-marker-outline:before{content:"\f1ad9"}.mdi-timer-minus:before{content:"\f1ada"}.mdi-timer-minus-outline:before{content:"\f1adb"}.mdi-timer-music:before{content:"\f1adc"}.mdi-timer-music-outline:before{content:"\f1add"}.mdi-timer-off:before{content:"\f13ac"}.mdi-timer-off-outline:before{content:"\f051e"}.mdi-timer-outline:before{content:"\f051b"}.mdi-timer-pause:before{content:"\f1ade"}.mdi-timer-pause-outline:before{content:"\f1adf"}.mdi-timer-play:before{content:"\f1ae0"}.mdi-timer-play-outline:before{content:"\f1ae1"}.mdi-timer-plus:before{content:"\f1ae2"}.mdi-timer-plus-outline:before{content:"\f1ae3"}.mdi-timer-refresh:before{content:"\f1ae4"}.mdi-timer-refresh-outline:before{content:"\f1ae5"}.mdi-timer-remove:before{content:"\f1ae6"}.mdi-timer-remove-outline:before{content:"\f1ae7"}.mdi-timer-sand:before{content:"\f051f"}.mdi-timer-sand-complete:before{content:"\f199f"}.mdi-timer-sand-empty:before{content:"\f06ad"}.mdi-timer-sand-full:before{content:"\f078c"}.mdi-timer-sand-paused:before{content:"\f19a0"}.mdi-timer-settings:before{content:"\f1923"}.mdi-timer-settings-outline:before{content:"\f1924"}.mdi-timer-star:before{content:"\f1ae8"}.mdi-timer-star-outline:before{content:"\f1ae9"}.mdi-timer-stop:before{content:"\f1aea"}.mdi-timer-stop-outline:before{content:"\f1aeb"}.mdi-timer-sync:before{content:"\f1aec"}.mdi-timer-sync-outline:before{content:"\f1aed"}.mdi-timetable:before{content:"\f0520"}.mdi-tire:before{content:"\f1896"}.mdi-toaster:before{content:"\f1063"}.mdi-toaster-off:before{content:"\f11b7"}.mdi-toaster-oven:before{content:"\f0cd3"}.mdi-toggle-switch:before{content:"\f0521"}.mdi-toggle-switch-off:before{content:"\f0522"}.mdi-toggle-switch-off-outline:before{content:"\f0a19"}.mdi-toggle-switch-outline:before{content:"\f0a1a"}.mdi-toggle-switch-variant:before{content:"\f1a25"}.mdi-toggle-switch-variant-off:before{content:"\f1a26"}.mdi-toilet:before{content:"\f09ab"}.mdi-toolbox:before{content:"\f09ac"}.mdi-toolbox-outline:before{content:"\f09ad"}.mdi-tools:before{content:"\f1064"}.mdi-tooltip:before{content:"\f0523"}.mdi-tooltip-account:before{content:"\f000c"}.mdi-tooltip-cellphone:before{content:"\f183b"}.mdi-tooltip-check:before{content:"\f155c"}.mdi-tooltip-check-outline:before{content:"\f155d"}.mdi-tooltip-edit:before{content:"\f0524"}.mdi-tooltip-edit-outline:before{content:"\f12c5"}.mdi-tooltip-image:before{content:"\f0525"}.mdi-tooltip-image-outline:before{content:"\f0bd5"}.mdi-tooltip-minus:before{content:"\f155e"}.mdi-tooltip-minus-outline:before{content:"\f155f"}.mdi-tooltip-outline:before{content:"\f0526"}.mdi-tooltip-plus:before{content:"\f0bd6"}.mdi-tooltip-plus-outline:before{content:"\f0527"}.mdi-tooltip-question:before{content:"\f1bba"}.mdi-tooltip-question-outline:before{content:"\f1bbb"}.mdi-tooltip-remove:before{content:"\f1560"}.mdi-tooltip-remove-outline:before{content:"\f1561"}.mdi-tooltip-text:before{content:"\f0528"}.mdi-tooltip-text-outline:before{content:"\f0bd7"}.mdi-tooth:before{content:"\f08c3"}.mdi-tooth-outline:before{content:"\f0529"}.mdi-toothbrush:before{content:"\f1129"}.mdi-toothbrush-electric:before{content:"\f112c"}.mdi-toothbrush-paste:before{content:"\f112a"}.mdi-torch:before{content:"\f1606"}.mdi-tortoise:before{content:"\f0d3b"}.mdi-toslink:before{content:"\f12b8"}.mdi-touch-text-outline:before{content:"\f1c60"}.mdi-tournament:before{content:"\f09ae"}.mdi-tow-truck:before{content:"\f083c"}.mdi-tower-beach:before{content:"\f0681"}.mdi-tower-fire:before{content:"\f0682"}.mdi-town-hall:before{content:"\f1875"}.mdi-toy-brick:before{content:"\f1288"}.mdi-toy-brick-marker:before{content:"\f1289"}.mdi-toy-brick-marker-outline:before{content:"\f128a"}.mdi-toy-brick-minus:before{content:"\f128b"}.mdi-toy-brick-minus-outline:before{content:"\f128c"}.mdi-toy-brick-outline:before{content:"\f128d"}.mdi-toy-brick-plus:before{content:"\f128e"}.mdi-toy-brick-plus-outline:before{content:"\f128f"}.mdi-toy-brick-remove:before{content:"\f1290"}.mdi-toy-brick-remove-outline:before{content:"\f1291"}.mdi-toy-brick-search:before{content:"\f1292"}.mdi-toy-brick-search-outline:before{content:"\f1293"}.mdi-track-light:before{content:"\f0914"}.mdi-track-light-off:before{content:"\f1b01"}.mdi-trackpad:before{content:"\f07f8"}.mdi-trackpad-lock:before{content:"\f0933"}.mdi-tractor:before{content:"\f0892"}.mdi-tractor-variant:before{content:"\f14c4"}.mdi-trademark:before{content:"\f0a78"}.mdi-traffic-cone:before{content:"\f137c"}.mdi-traffic-light:before{content:"\f052b"}.mdi-traffic-light-outline:before{content:"\f182a"}.mdi-train:before{content:"\f052c"}.mdi-train-bus:before{content:"\f1cc7"}.mdi-train-car:before{content:"\f0bd8"}.mdi-train-car-autorack:before{content:"\f1b2d"}.mdi-train-car-box:before{content:"\f1b2e"}.mdi-train-car-box-full:before{content:"\f1b2f"}.mdi-train-car-box-open:before{content:"\f1b30"}.mdi-train-car-caboose:before{content:"\f1b31"}.mdi-train-car-centerbeam:before{content:"\f1b32"}.mdi-train-car-centerbeam-full:before{content:"\f1b33"}.mdi-train-car-container:before{content:"\f1b34"}.mdi-train-car-flatbed:before{content:"\f1b35"}.mdi-train-car-flatbed-car:before{content:"\f1b36"}.mdi-train-car-flatbed-tank:before{content:"\f1b37"}.mdi-train-car-gondola:before{content:"\f1b38"}.mdi-train-car-gondola-full:before{content:"\f1b39"}.mdi-train-car-hopper:before{content:"\f1b3a"}.mdi-train-car-hopper-covered:before{content:"\f1b3b"}.mdi-train-car-hopper-full:before{content:"\f1b3c"}.mdi-train-car-intermodal:before{content:"\f1b3d"}.mdi-train-car-passenger:before{content:"\f1733"}.mdi-train-car-passenger-door:before{content:"\f1734"}.mdi-train-car-passenger-door-open:before{content:"\f1735"}.mdi-train-car-passenger-variant:before{content:"\f1736"}.mdi-train-car-tank:before{content:"\f1b3e"}.mdi-train-variant:before{content:"\f08c4"}.mdi-tram:before{content:"\f052d"}.mdi-tram-side:before{content:"\f0fe7"}.mdi-transcribe:before{content:"\f052e"}.mdi-transcribe-close:before{content:"\f052f"}.mdi-transfer:before{content:"\f1065"}.mdi-transfer-down:before{content:"\f0da1"}.mdi-transfer-left:before{content:"\f0da2"}.mdi-transfer-right:before{content:"\f0530"}.mdi-transfer-up:before{content:"\f0da3"}.mdi-transit-connection:before{content:"\f0d3c"}.mdi-transit-connection-horizontal:before{content:"\f1546"}.mdi-transit-connection-variant:before{content:"\f0d3d"}.mdi-transit-detour:before{content:"\f0f8b"}.mdi-transit-skip:before{content:"\f1515"}.mdi-transit-transfer:before{content:"\f06ae"}.mdi-transition:before{content:"\f0915"}.mdi-transition-masked:before{content:"\f0916"}.mdi-translate:before{content:"\f05ca"}.mdi-translate-off:before{content:"\f0e06"}.mdi-translate-variant:before{content:"\f1b99"}.mdi-transmission-tower:before{content:"\f0d3e"}.mdi-transmission-tower-export:before{content:"\f192c"}.mdi-transmission-tower-import:before{content:"\f192d"}.mdi-transmission-tower-off:before{content:"\f19dd"}.mdi-trash-can:before{content:"\f0a79"}.mdi-trash-can-outline:before{content:"\f0a7a"}.mdi-tray:before{content:"\f1294"}.mdi-tray-alert:before{content:"\f1295"}.mdi-tray-arrow-down:before{content:"\f0120"}.mdi-tray-arrow-up:before{content:"\f011d"}.mdi-tray-full:before{content:"\f1296"}.mdi-tray-minus:before{content:"\f1297"}.mdi-tray-plus:before{content:"\f1298"}.mdi-tray-remove:before{content:"\f1299"}.mdi-treasure-chest:before{content:"\f0726"}.mdi-treasure-chest-outline:before{content:"\f1c77"}.mdi-tree:before{content:"\f0531"}.mdi-tree-outline:before{content:"\f0e69"}.mdi-trello:before{content:"\f0532"}.mdi-trending-down:before{content:"\f0533"}.mdi-trending-neutral:before{content:"\f0534"}.mdi-trending-up:before{content:"\f0535"}.mdi-triangle:before{content:"\f0536"}.mdi-triangle-down:before{content:"\f1c56"}.mdi-triangle-down-outline:before{content:"\f1c57"}.mdi-triangle-outline:before{content:"\f0537"}.mdi-triangle-small-down:before{content:"\f1a09"}.mdi-triangle-small-up:before{content:"\f1a0a"}.mdi-triangle-wave:before{content:"\f147c"}.mdi-triforce:before{content:"\f0bd9"}.mdi-trophy:before{content:"\f0538"}.mdi-trophy-award:before{content:"\f0539"}.mdi-trophy-broken:before{content:"\f0da4"}.mdi-trophy-outline:before{content:"\f053a"}.mdi-trophy-variant:before{content:"\f053b"}.mdi-trophy-variant-outline:before{content:"\f053c"}.mdi-truck:before{content:"\f053d"}.mdi-truck-alert:before{content:"\f19de"}.mdi-truck-alert-outline:before{content:"\f19df"}.mdi-truck-cargo-container:before{content:"\f18d8"}.mdi-truck-check:before{content:"\f0cd4"}.mdi-truck-check-outline:before{content:"\f129a"}.mdi-truck-delivery:before{content:"\f053e"}.mdi-truck-delivery-outline:before{content:"\f129b"}.mdi-truck-fast:before{content:"\f0788"}.mdi-truck-fast-outline:before{content:"\f129c"}.mdi-truck-flatbed:before{content:"\f1891"}.mdi-truck-minus:before{content:"\f19ae"}.mdi-truck-minus-outline:before{content:"\f19bd"}.mdi-truck-off-road:before{content:"\f1c9e"}.mdi-truck-off-road-off:before{content:"\f1c9f"}.mdi-truck-outline:before{content:"\f129d"}.mdi-truck-plus:before{content:"\f19ad"}.mdi-truck-plus-outline:before{content:"\f19bc"}.mdi-truck-remove:before{content:"\f19af"}.mdi-truck-remove-outline:before{content:"\f19be"}.mdi-truck-snowflake:before{content:"\f19a6"}.mdi-truck-trailer:before{content:"\f0727"}.mdi-trumpet:before{content:"\f1096"}.mdi-tshirt-crew:before{content:"\f0a7b"}.mdi-tshirt-crew-outline:before{content:"\f053f"}.mdi-tshirt-v:before{content:"\f0a7c"}.mdi-tshirt-v-outline:before{content:"\f0540"}.mdi-tsunami:before{content:"\f1a81"}.mdi-tumble-dryer:before{content:"\f0917"}.mdi-tumble-dryer-alert:before{content:"\f11ba"}.mdi-tumble-dryer-off:before{content:"\f11bb"}.mdi-tune:before{content:"\f062e"}.mdi-tune-variant:before{content:"\f1542"}.mdi-tune-vertical:before{content:"\f066a"}.mdi-tune-vertical-variant:before{content:"\f1543"}.mdi-tunnel:before{content:"\f183d"}.mdi-tunnel-outline:before{content:"\f183e"}.mdi-turbine:before{content:"\f1a82"}.mdi-turkey:before{content:"\f171b"}.mdi-turnstile:before{content:"\f0cd5"}.mdi-turnstile-outline:before{content:"\f0cd6"}.mdi-turtle:before{content:"\f0cd7"}.mdi-twitch:before{content:"\f0543"}.mdi-twitter:before{content:"\f0544"}.mdi-two-factor-authentication:before{content:"\f09af"}.mdi-typewriter:before{content:"\f0f2d"}.mdi-ubisoft:before{content:"\f0bda"}.mdi-ubuntu:before{content:"\f0548"}.mdi-ufo:before{content:"\f10c4"}.mdi-ufo-outline:before{content:"\f10c5"}.mdi-ultra-high-definition:before{content:"\f07f9"}.mdi-umbraco:before{content:"\f0549"}.mdi-umbrella:before{content:"\f054a"}.mdi-umbrella-beach:before{content:"\f188a"}.mdi-umbrella-beach-outline:before{content:"\f188b"}.mdi-umbrella-closed:before{content:"\f09b0"}.mdi-umbrella-closed-outline:before{content:"\f13e2"}.mdi-umbrella-closed-variant:before{content:"\f13e1"}.mdi-umbrella-outline:before{content:"\f054b"}.mdi-underwear-outline:before{content:"\f1d0f"}.mdi-undo:before{content:"\f054c"}.mdi-undo-variant:before{content:"\f054d"}.mdi-unfold-less-horizontal:before{content:"\f054e"}.mdi-unfold-less-vertical:before{content:"\f0760"}.mdi-unfold-more-horizontal:before{content:"\f054f"}.mdi-unfold-more-vertical:before{content:"\f0761"}.mdi-ungroup:before{content:"\f0550"}.mdi-unicode:before{content:"\f0ed0"}.mdi-unicorn:before{content:"\f15c2"}.mdi-unicorn-variant:before{content:"\f15c3"}.mdi-unicycle:before{content:"\f15e5"}.mdi-unity:before{content:"\f06af"}.mdi-unreal:before{content:"\f09b1"}.mdi-update:before{content:"\f06b0"}.mdi-upload:before{content:"\f0552"}.mdi-upload-box:before{content:"\f1d10"}.mdi-upload-box-outline:before{content:"\f1d11"}.mdi-upload-circle:before{content:"\f1d12"}.mdi-upload-circle-outline:before{content:"\f1d13"}.mdi-upload-lock:before{content:"\f1373"}.mdi-upload-lock-outline:before{content:"\f1374"}.mdi-upload-multiple:before{content:"\f083d"}.mdi-upload-multiple-outline:before{content:"\f1d14"}.mdi-upload-network:before{content:"\f06f6"}.mdi-upload-network-outline:before{content:"\f0cd8"}.mdi-upload-off:before{content:"\f10c6"}.mdi-upload-off-outline:before{content:"\f10c7"}.mdi-upload-outline:before{content:"\f0e07"}.mdi-usb:before{content:"\f0553"}.mdi-usb-c-port:before{content:"\f1cbf"}.mdi-usb-flash-drive:before{content:"\f129e"}.mdi-usb-flash-drive-outline:before{content:"\f129f"}.mdi-usb-port:before{content:"\f11f0"}.mdi-vacuum:before{content:"\f19a1"}.mdi-vacuum-outline:before{content:"\f19a2"}.mdi-valve:before{content:"\f1066"}.mdi-valve-closed:before{content:"\f1067"}.mdi-valve-open:before{content:"\f1068"}.mdi-van-passenger:before{content:"\f07fa"}.mdi-van-utility:before{content:"\f07fb"}.mdi-vanish:before{content:"\f07fc"}.mdi-vanish-quarter:before{content:"\f1554"}.mdi-vanity-light:before{content:"\f11e1"}.mdi-variable:before{content:"\f0ae7"}.mdi-variable-box:before{content:"\f1111"}.mdi-vector-arrange-above:before{content:"\f0554"}.mdi-vector-arrange-below:before{content:"\f0555"}.mdi-vector-bezier:before{content:"\f0ae8"}.mdi-vector-circle:before{content:"\f0556"}.mdi-vector-circle-variant:before{content:"\f0557"}.mdi-vector-combine:before{content:"\f0558"}.mdi-vector-curve:before{content:"\f0559"}.mdi-vector-difference:before{content:"\f055a"}.mdi-vector-difference-ab:before{content:"\f055b"}.mdi-vector-difference-ba:before{content:"\f055c"}.mdi-vector-ellipse:before{content:"\f0893"}.mdi-vector-intersection:before{content:"\f055d"}.mdi-vector-line:before{content:"\f055e"}.mdi-vector-link:before{content:"\f0fe8"}.mdi-vector-point:before{content:"\f01c4"}.mdi-vector-point-edit:before{content:"\f09e8"}.mdi-vector-point-minus:before{content:"\f1b78"}.mdi-vector-point-plus:before{content:"\f1b79"}.mdi-vector-point-select:before{content:"\f055f"}.mdi-vector-polygon:before{content:"\f0560"}.mdi-vector-polygon-variant:before{content:"\f1856"}.mdi-vector-polyline:before{content:"\f0561"}.mdi-vector-polyline-edit:before{content:"\f1225"}.mdi-vector-polyline-minus:before{content:"\f1226"}.mdi-vector-polyline-plus:before{content:"\f1227"}.mdi-vector-polyline-remove:before{content:"\f1228"}.mdi-vector-radius:before{content:"\f074a"}.mdi-vector-rectangle:before{content:"\f05c6"}.mdi-vector-selection:before{content:"\f0562"}.mdi-vector-square:before{content:"\f0001"}.mdi-vector-square-close:before{content:"\f1857"}.mdi-vector-square-edit:before{content:"\f18d9"}.mdi-vector-square-minus:before{content:"\f18da"}.mdi-vector-square-open:before{content:"\f1858"}.mdi-vector-square-plus:before{content:"\f18db"}.mdi-vector-square-remove:before{content:"\f18dc"}.mdi-vector-triangle:before{content:"\f0563"}.mdi-vector-union:before{content:"\f0564"}.mdi-vhs:before{content:"\f0a1b"}.mdi-vibrate:before{content:"\f0566"}.mdi-vibrate-off:before{content:"\f0cd9"}.mdi-video:before{content:"\f0567"}.mdi-video-2d:before{content:"\f1a1c"}.mdi-video-3d:before{content:"\f07fd"}.mdi-video-3d-off:before{content:"\f13d9"}.mdi-video-3d-variant:before{content:"\f0ed1"}.mdi-video-4k-box:before{content:"\f083e"}.mdi-video-account:before{content:"\f0919"}.mdi-video-box:before{content:"\f00fd"}.mdi-video-box-off:before{content:"\f00fe"}.mdi-video-check:before{content:"\f1069"}.mdi-video-check-outline:before{content:"\f106a"}.mdi-video-high-definition:before{content:"\f152e"}.mdi-video-image:before{content:"\f091a"}.mdi-video-input-antenna:before{content:"\f083f"}.mdi-video-input-component:before{content:"\f0840"}.mdi-video-input-hdmi:before{content:"\f0841"}.mdi-video-input-scart:before{content:"\f0f8c"}.mdi-video-input-svideo:before{content:"\f0842"}.mdi-video-marker:before{content:"\f19a9"}.mdi-video-marker-outline:before{content:"\f19aa"}.mdi-video-minus:before{content:"\f09b2"}.mdi-video-minus-outline:before{content:"\f02ba"}.mdi-video-off:before{content:"\f0568"}.mdi-video-off-outline:before{content:"\f0bdb"}.mdi-video-outline:before{content:"\f0bdc"}.mdi-video-plus:before{content:"\f09b3"}.mdi-video-plus-outline:before{content:"\f01d3"}.mdi-video-stabilization:before{content:"\f091b"}.mdi-video-standard-definition:before{content:"\f1ca0"}.mdi-video-switch:before{content:"\f0569"}.mdi-video-switch-outline:before{content:"\f0790"}.mdi-video-vintage:before{content:"\f0a1c"}.mdi-video-wireless:before{content:"\f0ed2"}.mdi-video-wireless-outline:before{content:"\f0ed3"}.mdi-view-agenda:before{content:"\f056a"}.mdi-view-agenda-outline:before{content:"\f11d8"}.mdi-view-array:before{content:"\f056b"}.mdi-view-array-outline:before{content:"\f1485"}.mdi-view-carousel:before{content:"\f056c"}.mdi-view-carousel-outline:before{content:"\f1486"}.mdi-view-column:before{content:"\f056d"}.mdi-view-column-outline:before{content:"\f1487"}.mdi-view-comfy:before{content:"\f0e6a"}.mdi-view-comfy-outline:before{content:"\f1488"}.mdi-view-compact:before{content:"\f0e6b"}.mdi-view-compact-outline:before{content:"\f0e6c"}.mdi-view-dashboard:before{content:"\f056e"}.mdi-view-dashboard-edit:before{content:"\f1947"}.mdi-view-dashboard-edit-outline:before{content:"\f1948"}.mdi-view-dashboard-outline:before{content:"\f0a1d"}.mdi-view-dashboard-variant:before{content:"\f0843"}.mdi-view-dashboard-variant-outline:before{content:"\f1489"}.mdi-view-day:before{content:"\f056f"}.mdi-view-day-outline:before{content:"\f148a"}.mdi-view-gallery:before{content:"\f1888"}.mdi-view-gallery-outline:before{content:"\f1889"}.mdi-view-grid:before{content:"\f0570"}.mdi-view-grid-compact:before{content:"\f1c61"}.mdi-view-grid-outline:before{content:"\f11d9"}.mdi-view-grid-plus:before{content:"\f0f8d"}.mdi-view-grid-plus-outline:before{content:"\f11da"}.mdi-view-headline:before{content:"\f0571"}.mdi-view-list:before{content:"\f0572"}.mdi-view-list-outline:before{content:"\f148b"}.mdi-view-module:before{content:"\f0573"}.mdi-view-module-outline:before{content:"\f148c"}.mdi-view-parallel:before{content:"\f0728"}.mdi-view-parallel-outline:before{content:"\f148d"}.mdi-view-quilt:before{content:"\f0574"}.mdi-view-quilt-outline:before{content:"\f148e"}.mdi-view-sequential:before{content:"\f0729"}.mdi-view-sequential-outline:before{content:"\f148f"}.mdi-view-split-horizontal:before{content:"\f0bcb"}.mdi-view-split-vertical:before{content:"\f0bcc"}.mdi-view-stream:before{content:"\f0575"}.mdi-view-stream-outline:before{content:"\f1490"}.mdi-view-week:before{content:"\f0576"}.mdi-view-week-outline:before{content:"\f1491"}.mdi-vimeo:before{content:"\f0577"}.mdi-violin:before{content:"\f060f"}.mdi-virtual-reality:before{content:"\f0894"}.mdi-virus:before{content:"\f13b6"}.mdi-virus-off:before{content:"\f18e1"}.mdi-virus-off-outline:before{content:"\f18e2"}.mdi-virus-outline:before{content:"\f13b7"}.mdi-vlc:before{content:"\f057c"}.mdi-voicemail:before{content:"\f057d"}.mdi-volcano:before{content:"\f1a83"}.mdi-volcano-outline:before{content:"\f1a84"}.mdi-volleyball:before{content:"\f09b4"}.mdi-volume-equal:before{content:"\f1b10"}.mdi-volume-high:before{content:"\f057e"}.mdi-volume-low:before{content:"\f057f"}.mdi-volume-medium:before{content:"\f0580"}.mdi-volume-minus:before{content:"\f075e"}.mdi-volume-mute:before{content:"\f075f"}.mdi-volume-off:before{content:"\f0581"}.mdi-volume-plus:before{content:"\f075d"}.mdi-volume-source:before{content:"\f1120"}.mdi-volume-variant-off:before{content:"\f0e08"}.mdi-volume-vibrate:before{content:"\f1121"}.mdi-vote:before{content:"\f0a1f"}.mdi-vote-outline:before{content:"\f0a20"}.mdi-vpn:before{content:"\f0582"}.mdi-vuejs:before{content:"\f0844"}.mdi-vuetify:before{content:"\f0e6d"}.mdi-walk:before{content:"\f0583"}.mdi-wall:before{content:"\f07fe"}.mdi-wall-fire:before{content:"\f1a11"}.mdi-wall-sconce:before{content:"\f091c"}.mdi-wall-sconce-flat:before{content:"\f091d"}.mdi-wall-sconce-flat-outline:before{content:"\f17c9"}.mdi-wall-sconce-flat-variant:before{content:"\f041c"}.mdi-wall-sconce-flat-variant-outline:before{content:"\f17ca"}.mdi-wall-sconce-outline:before{content:"\f17cb"}.mdi-wall-sconce-round:before{content:"\f0748"}.mdi-wall-sconce-round-outline:before{content:"\f17cc"}.mdi-wall-sconce-round-variant:before{content:"\f091e"}.mdi-wall-sconce-round-variant-outline:before{content:"\f17cd"}.mdi-wallet:before{content:"\f0584"}.mdi-wallet-bifold:before{content:"\f1c58"}.mdi-wallet-bifold-outline:before{content:"\f1c59"}.mdi-wallet-giftcard:before{content:"\f0585"}.mdi-wallet-membership:before{content:"\f0586"}.mdi-wallet-outline:before{content:"\f0bdd"}.mdi-wallet-plus:before{content:"\f0f8e"}.mdi-wallet-plus-outline:before{content:"\f0f8f"}.mdi-wallet-travel:before{content:"\f0587"}.mdi-wallpaper:before{content:"\f0e09"}.mdi-wan:before{content:"\f0588"}.mdi-wardrobe:before{content:"\f0f90"}.mdi-wardrobe-outline:before{content:"\f0f91"}.mdi-warehouse:before{content:"\f0f81"}.mdi-washing-machine:before{content:"\f072a"}.mdi-washing-machine-alert:before{content:"\f11bc"}.mdi-washing-machine-off:before{content:"\f11bd"}.mdi-watch:before{content:"\f0589"}.mdi-watch-export:before{content:"\f058a"}.mdi-watch-export-variant:before{content:"\f0895"}.mdi-watch-import:before{content:"\f058b"}.mdi-watch-import-variant:before{content:"\f0896"}.mdi-watch-variant:before{content:"\f0897"}.mdi-watch-vibrate:before{content:"\f06b1"}.mdi-watch-vibrate-off:before{content:"\f0cda"}.mdi-water:before{content:"\f058c"}.mdi-water-alert:before{content:"\f1502"}.mdi-water-alert-outline:before{content:"\f1503"}.mdi-water-boiler:before{content:"\f0f92"}.mdi-water-boiler-alert:before{content:"\f11b3"}.mdi-water-boiler-auto:before{content:"\f1b98"}.mdi-water-boiler-off:before{content:"\f11b4"}.mdi-water-check:before{content:"\f1504"}.mdi-water-check-outline:before{content:"\f1505"}.mdi-water-circle:before{content:"\f1806"}.mdi-water-minus:before{content:"\f1506"}.mdi-water-minus-outline:before{content:"\f1507"}.mdi-water-off:before{content:"\f058d"}.mdi-water-off-outline:before{content:"\f1508"}.mdi-water-opacity:before{content:"\f1855"}.mdi-water-outline:before{content:"\f0e0a"}.mdi-water-percent:before{content:"\f058e"}.mdi-water-percent-alert:before{content:"\f1509"}.mdi-water-plus:before{content:"\f150a"}.mdi-water-plus-outline:before{content:"\f150b"}.mdi-water-polo:before{content:"\f12a0"}.mdi-water-pump:before{content:"\f058f"}.mdi-water-pump-off:before{content:"\f0f93"}.mdi-water-remove:before{content:"\f150c"}.mdi-water-remove-outline:before{content:"\f150d"}.mdi-water-sync:before{content:"\f17c6"}.mdi-water-thermometer:before{content:"\f1a85"}.mdi-water-thermometer-outline:before{content:"\f1a86"}.mdi-water-well:before{content:"\f106b"}.mdi-water-well-outline:before{content:"\f106c"}.mdi-waterfall:before{content:"\f1849"}.mdi-watering-can:before{content:"\f1481"}.mdi-watering-can-outline:before{content:"\f1482"}.mdi-watermark:before{content:"\f0612"}.mdi-wave:before{content:"\f0f2e"}.mdi-wave-arrow-down:before{content:"\f1cb0"}.mdi-wave-arrow-up:before{content:"\f1cb1"}.mdi-wave-undercurrent:before{content:"\f1cc0"}.mdi-waveform:before{content:"\f147d"}.mdi-waves:before{content:"\f078d"}.mdi-waves-arrow-left:before{content:"\f1859"}.mdi-waves-arrow-right:before{content:"\f185a"}.mdi-waves-arrow-up:before{content:"\f185b"}.mdi-waze:before{content:"\f0bde"}.mdi-weather-cloudy:before{content:"\f0590"}.mdi-weather-cloudy-alert:before{content:"\f0f2f"}.mdi-weather-cloudy-arrow-right:before{content:"\f0e6e"}.mdi-weather-cloudy-clock:before{content:"\f18f6"}.mdi-weather-dust:before{content:"\f1b5a"}.mdi-weather-fog:before{content:"\f0591"}.mdi-weather-hail:before{content:"\f0592"}.mdi-weather-hazy:before{content:"\f0f30"}.mdi-weather-hurricane:before{content:"\f0898"}.mdi-weather-hurricane-outline:before{content:"\f1c78"}.mdi-weather-lightning:before{content:"\f0593"}.mdi-weather-lightning-rainy:before{content:"\f067e"}.mdi-weather-moonset:before{content:"\f1d15"}.mdi-weather-moonset-down:before{content:"\f1d16"}.mdi-weather-moonset-up:before{content:"\f1d17"}.mdi-weather-night:before{content:"\f0594"}.mdi-weather-night-partly-cloudy:before{content:"\f0f31"}.mdi-weather-partly-cloudy:before{content:"\f0595"}.mdi-weather-partly-lightning:before{content:"\f0f32"}.mdi-weather-partly-rainy:before{content:"\f0f33"}.mdi-weather-partly-snowy:before{content:"\f0f34"}.mdi-weather-partly-snowy-rainy:before{content:"\f0f35"}.mdi-weather-pouring:before{content:"\f0596"}.mdi-weather-rainy:before{content:"\f0597"}.mdi-weather-snowy:before{content:"\f0598"}.mdi-weather-snowy-heavy:before{content:"\f0f36"}.mdi-weather-snowy-rainy:before{content:"\f067f"}.mdi-weather-sunny:before{content:"\f0599"}.mdi-weather-sunny-alert:before{content:"\f0f37"}.mdi-weather-sunny-off:before{content:"\f14e4"}.mdi-weather-sunset:before{content:"\f059a"}.mdi-weather-sunset-down:before{content:"\f059b"}.mdi-weather-sunset-up:before{content:"\f059c"}.mdi-weather-tornado:before{content:"\f0f38"}.mdi-weather-windy:before{content:"\f059d"}.mdi-weather-windy-variant:before{content:"\f059e"}.mdi-web:before{content:"\f059f"}.mdi-web-box:before{content:"\f0f94"}.mdi-web-cancel:before{content:"\f1790"}.mdi-web-check:before{content:"\f0789"}.mdi-web-clock:before{content:"\f124a"}.mdi-web-minus:before{content:"\f10a0"}.mdi-web-off:before{content:"\f0a8e"}.mdi-web-plus:before{content:"\f0033"}.mdi-web-refresh:before{content:"\f1791"}.mdi-web-remove:before{content:"\f0551"}.mdi-web-sync:before{content:"\f1792"}.mdi-webcam:before{content:"\f05a0"}.mdi-webcam-off:before{content:"\f1737"}.mdi-webhook:before{content:"\f062f"}.mdi-webpack:before{content:"\f072b"}.mdi-webrtc:before{content:"\f1248"}.mdi-wechat:before{content:"\f0611"}.mdi-weight:before{content:"\f05a1"}.mdi-weight-gram:before{content:"\f0d3f"}.mdi-weight-kilogram:before{content:"\f05a2"}.mdi-weight-lifter:before{content:"\f115d"}.mdi-weight-pound:before{content:"\f09b5"}.mdi-whatsapp:before{content:"\f05a3"}.mdi-wheel-barrow:before{content:"\f14f2"}.mdi-wheelchair:before{content:"\f1a87"}.mdi-wheelchair-accessibility:before{content:"\f05a4"}.mdi-whistle:before{content:"\f09b6"}.mdi-whistle-outline:before{content:"\f12bc"}.mdi-white-balance-auto:before{content:"\f05a5"}.mdi-white-balance-incandescent:before{content:"\f05a6"}.mdi-white-balance-iridescent:before{content:"\f05a7"}.mdi-white-balance-sunny:before{content:"\f05a8"}.mdi-widgets:before{content:"\f072c"}.mdi-widgets-outline:before{content:"\f1355"}.mdi-wifi:before{content:"\f05a9"}.mdi-wifi-alert:before{content:"\f16b5"}.mdi-wifi-arrow-down:before{content:"\f16b6"}.mdi-wifi-arrow-left:before{content:"\f16b7"}.mdi-wifi-arrow-left-right:before{content:"\f16b8"}.mdi-wifi-arrow-right:before{content:"\f16b9"}.mdi-wifi-arrow-up:before{content:"\f16ba"}.mdi-wifi-arrow-up-down:before{content:"\f16bb"}.mdi-wifi-cancel:before{content:"\f16bc"}.mdi-wifi-check:before{content:"\f16bd"}.mdi-wifi-cog:before{content:"\f16be"}.mdi-wifi-lock:before{content:"\f16bf"}.mdi-wifi-lock-open:before{content:"\f16c0"}.mdi-wifi-marker:before{content:"\f16c1"}.mdi-wifi-minus:before{content:"\f16c2"}.mdi-wifi-off:before{content:"\f05aa"}.mdi-wifi-plus:before{content:"\f16c3"}.mdi-wifi-refresh:before{content:"\f16c4"}.mdi-wifi-remove:before{content:"\f16c5"}.mdi-wifi-settings:before{content:"\f16c6"}.mdi-wifi-star:before{content:"\f0e0b"}.mdi-wifi-strength-1:before{content:"\f091f"}.mdi-wifi-strength-1-alert:before{content:"\f0920"}.mdi-wifi-strength-1-lock:before{content:"\f0921"}.mdi-wifi-strength-1-lock-open:before{content:"\f16cb"}.mdi-wifi-strength-2:before{content:"\f0922"}.mdi-wifi-strength-2-alert:before{content:"\f0923"}.mdi-wifi-strength-2-lock:before{content:"\f0924"}.mdi-wifi-strength-2-lock-open:before{content:"\f16cc"}.mdi-wifi-strength-3:before{content:"\f0925"}.mdi-wifi-strength-3-alert:before{content:"\f0926"}.mdi-wifi-strength-3-lock:before{content:"\f0927"}.mdi-wifi-strength-3-lock-open:before{content:"\f16cd"}.mdi-wifi-strength-4:before{content:"\f0928"}.mdi-wifi-strength-4-alert:before{content:"\f0929"}.mdi-wifi-strength-4-lock:before{content:"\f092a"}.mdi-wifi-strength-4-lock-open:before{content:"\f16ce"}.mdi-wifi-strength-alert-outline:before{content:"\f092b"}.mdi-wifi-strength-lock-open-outline:before{content:"\f16cf"}.mdi-wifi-strength-lock-outline:before{content:"\f092c"}.mdi-wifi-strength-off:before{content:"\f092d"}.mdi-wifi-strength-off-outline:before{content:"\f092e"}.mdi-wifi-strength-outline:before{content:"\f092f"}.mdi-wifi-sync:before{content:"\f16c7"}.mdi-wikipedia:before{content:"\f05ac"}.mdi-wind-power:before{content:"\f1a88"}.mdi-wind-power-outline:before{content:"\f1a89"}.mdi-wind-turbine:before{content:"\f0da5"}.mdi-wind-turbine-alert:before{content:"\f19ab"}.mdi-wind-turbine-check:before{content:"\f19ac"}.mdi-window-close:before{content:"\f05ad"}.mdi-window-closed:before{content:"\f05ae"}.mdi-window-closed-variant:before{content:"\f11db"}.mdi-window-maximize:before{content:"\f05af"}.mdi-window-minimize:before{content:"\f05b0"}.mdi-window-open:before{content:"\f05b1"}.mdi-window-open-variant:before{content:"\f11dc"}.mdi-window-restore:before{content:"\f05b2"}.mdi-window-shutter:before{content:"\f111c"}.mdi-window-shutter-alert:before{content:"\f111d"}.mdi-window-shutter-auto:before{content:"\f1ba3"}.mdi-window-shutter-cog:before{content:"\f1a8a"}.mdi-window-shutter-open:before{content:"\f111e"}.mdi-window-shutter-settings:before{content:"\f1a8b"}.mdi-windsock:before{content:"\f15fa"}.mdi-wiper:before{content:"\f0ae9"}.mdi-wiper-wash:before{content:"\f0da6"}.mdi-wiper-wash-alert:before{content:"\f18df"}.mdi-wizard-hat:before{content:"\f1477"}.mdi-wordpress:before{content:"\f05b4"}.mdi-wrap:before{content:"\f05b6"}.mdi-wrap-disabled:before{content:"\f0bdf"}.mdi-wrench:before{content:"\f05b7"}.mdi-wrench-check:before{content:"\f1b8f"}.mdi-wrench-check-outline:before{content:"\f1b90"}.mdi-wrench-clock:before{content:"\f19a3"}.mdi-wrench-clock-outline:before{content:"\f1b93"}.mdi-wrench-cog:before{content:"\f1b91"}.mdi-wrench-cog-outline:before{content:"\f1b92"}.mdi-wrench-outline:before{content:"\f0be0"}.mdi-xamarin:before{content:"\f0845"}.mdi-xml:before{content:"\f05c0"}.mdi-xmpp:before{content:"\f07ff"}.mdi-yahoo:before{content:"\f0b4f"}.mdi-yeast:before{content:"\f05c1"}.mdi-yin-yang:before{content:"\f0680"}.mdi-yoga:before{content:"\f117c"}.mdi-youtube:before{content:"\f05c3"}.mdi-youtube-gaming:before{content:"\f0848"}.mdi-youtube-studio:before{content:"\f0847"}.mdi-youtube-subscription:before{content:"\f0d40"}.mdi-youtube-tv:before{content:"\f0448"}.mdi-yurt:before{content:"\f1516"}.mdi-z-wave:before{content:"\f0aea"}.mdi-zend:before{content:"\f0aeb"}.mdi-zigbee:before{content:"\f0d41"}.mdi-zip-box:before{content:"\f05c4"}.mdi-zip-box-outline:before{content:"\f0ffa"}.mdi-zip-disk:before{content:"\f0a23"}.mdi-zodiac-aquarius:before{content:"\f0a7d"}.mdi-zodiac-aries:before{content:"\f0a7e"}.mdi-zodiac-cancer:before{content:"\f0a7f"}.mdi-zodiac-capricorn:before{content:"\f0a80"}.mdi-zodiac-gemini:before{content:"\f0a81"}.mdi-zodiac-leo:before{content:"\f0a82"}.mdi-zodiac-libra:before{content:"\f0a83"}.mdi-zodiac-pisces:before{content:"\f0a84"}.mdi-zodiac-sagittarius:before{content:"\f0a85"}.mdi-zodiac-scorpio:before{content:"\f0a86"}.mdi-zodiac-taurus:before{content:"\f0a87"}.mdi-zodiac-virgo:before{content:"\f0a88"}.mdi-blank:before{content:"\f68c";visibility:hidden}.mdi-18px.mdi-set,.mdi-18px.mdi:before{font-size:18px}.mdi-24px.mdi-set,.mdi-24px.mdi:before{font-size:24px}.mdi-36px.mdi-set,.mdi-36px.mdi:before{font-size:36px}.mdi-48px.mdi-set,.mdi-48px.mdi:before{font-size:48px}.mdi-dark:before{color:#0000008a}.mdi-dark.mdi-inactive:before{color:#00000042}.mdi-light:before{color:#fff}.mdi-light.mdi-inactive:before{color:#ffffff4d}.mdi-rotate-45:before{-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.mdi-rotate-90:before{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.mdi-rotate-135:before{-webkit-transform:rotate(135deg);-ms-transform:rotate(135deg);transform:rotate(135deg)}.mdi-rotate-180:before{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.mdi-rotate-225:before{-webkit-transform:rotate(225deg);-ms-transform:rotate(225deg);transform:rotate(225deg)}.mdi-rotate-270:before{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.mdi-rotate-315:before{-webkit-transform:rotate(315deg);-ms-transform:rotate(315deg);transform:rotate(315deg)}.mdi-flip-h:before{-webkit-transform:scaleX(-1);transform:scaleX(-1);filter:FlipH;-ms-filter:"FlipH"}.mdi-flip-v:before{-webkit-transform:scaleY(-1);transform:scaleY(-1);filter:FlipV;-ms-filter:"FlipV"}.mdi-spin:before{-webkit-animation:mdi-spin 2s infinite linear;animation:mdi-spin 2s infinite linear}@-webkit-keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.flatpickr-calendar{background:transparent;opacity:0;display:none;text-align:center;visibility:hidden;padding:0;-webkit-animation:none;animation:none;direction:ltr;border:0;font-size:14px;line-height:24px;border-radius:5px;position:absolute;width:307.875px;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-touch-action:manipulation;touch-action:manipulation;background:#fff;-webkit-box-shadow:1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,.08);box-shadow:1px 0 #e6e6e6,-1px 0 #e6e6e6,0 1px #e6e6e6,0 -1px #e6e6e6,0 3px 13px #00000014}.flatpickr-calendar.open,.flatpickr-calendar.inline{opacity:1;max-height:640px;visibility:visible}.flatpickr-calendar.open{display:inline-block;z-index:99999}.flatpickr-calendar.animate.open{-webkit-animation:fpFadeInDown .3s cubic-bezier(.23,1,.32,1);animation:fpFadeInDown .3s cubic-bezier(.23,1,.32,1)}.flatpickr-calendar.inline{display:block;position:relative;top:2px}.flatpickr-calendar.static{position:absolute;top:calc(100% + 2px)}.flatpickr-calendar.static.open{z-index:999;display:block}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+1) .flatpickr-day.inRange:nth-child(7n+7){-webkit-box-shadow:none!important;box-shadow:none!important}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1){-webkit-box-shadow:-2px 0 0 #e6e6e6,5px 0 0 #e6e6e6;box-shadow:-2px 0 #e6e6e6,5px 0 #e6e6e6}.flatpickr-calendar .hasWeeks .dayContainer,.flatpickr-calendar .hasTime .dayContainer{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.flatpickr-calendar .hasWeeks .dayContainer{border-left:0}.flatpickr-calendar.hasTime .flatpickr-time{height:40px;border-top:1px solid #e6e6e6}.flatpickr-calendar.noCalendar.hasTime .flatpickr-time{height:auto}.flatpickr-calendar:before,.flatpickr-calendar:after{position:absolute;display:block;pointer-events:none;border:solid transparent;content:"";height:0;width:0;left:22px}.flatpickr-calendar.rightMost:before,.flatpickr-calendar.arrowRight:before,.flatpickr-calendar.rightMost:after,.flatpickr-calendar.arrowRight:after{left:auto;right:22px}.flatpickr-calendar.arrowCenter:before,.flatpickr-calendar.arrowCenter:after{left:50%;right:50%}.flatpickr-calendar:before{border-width:5px;margin:0 -5px}.flatpickr-calendar:after{border-width:4px;margin:0 -4px}.flatpickr-calendar.arrowTop:before,.flatpickr-calendar.arrowTop:after{bottom:100%}.flatpickr-calendar.arrowTop:before{border-bottom-color:#e6e6e6}.flatpickr-calendar.arrowTop:after{border-bottom-color:#fff}.flatpickr-calendar.arrowBottom:before,.flatpickr-calendar.arrowBottom:after{top:100%}.flatpickr-calendar.arrowBottom:before{border-top-color:#e6e6e6}.flatpickr-calendar.arrowBottom:after{border-top-color:#fff}.flatpickr-calendar:focus{outline:0}.flatpickr-wrapper{position:relative;display:inline-block}.flatpickr-months{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.flatpickr-months .flatpickr-month{background:transparent;color:#000000e6;fill:#000000e6;height:34px;line-height:1;text-align:center;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:hidden;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.flatpickr-months .flatpickr-prev-month,.flatpickr-months .flatpickr-next-month{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-decoration:none;cursor:pointer;position:absolute;top:0;height:34px;padding:10px;z-index:3;color:#000000e6;fill:#000000e6}.flatpickr-months .flatpickr-prev-month.flatpickr-disabled,.flatpickr-months .flatpickr-next-month.flatpickr-disabled{display:none}.flatpickr-months .flatpickr-prev-month i,.flatpickr-months .flatpickr-next-month i{position:relative}.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month,.flatpickr-months .flatpickr-next-month.flatpickr-prev-month{left:0}.flatpickr-months .flatpickr-prev-month.flatpickr-next-month,.flatpickr-months .flatpickr-next-month.flatpickr-next-month{right:0}.flatpickr-months .flatpickr-prev-month:hover,.flatpickr-months .flatpickr-next-month:hover{color:#959ea9}.flatpickr-months .flatpickr-prev-month:hover svg,.flatpickr-months .flatpickr-next-month:hover svg{fill:#f64747}.flatpickr-months .flatpickr-prev-month svg,.flatpickr-months .flatpickr-next-month svg{width:14px;height:14px}.flatpickr-months .flatpickr-prev-month svg path,.flatpickr-months .flatpickr-next-month svg path{-webkit-transition:fill .1s;transition:fill .1s;fill:inherit}.numInputWrapper{position:relative;height:auto}.numInputWrapper input,.numInputWrapper span{display:inline-block}.numInputWrapper input{width:100%}.numInputWrapper input::-ms-clear{display:none}.numInputWrapper input::-webkit-outer-spin-button,.numInputWrapper input::-webkit-inner-spin-button{margin:0;-webkit-appearance:none}.numInputWrapper span{position:absolute;right:0;width:14px;padding:0 4px 0 2px;height:50%;line-height:50%;opacity:0;cursor:pointer;border:1px solid rgba(57,57,57,.15);-webkit-box-sizing:border-box;box-sizing:border-box}.numInputWrapper span:hover{background:#0000001a}.numInputWrapper span:active{background:#0003}.numInputWrapper span:after{display:block;content:"";position:absolute}.numInputWrapper span.arrowUp{top:0;border-bottom:0}.numInputWrapper span.arrowUp:after{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:4px solid rgba(57,57,57,.6);top:26%}.numInputWrapper span.arrowDown{top:50%}.numInputWrapper span.arrowDown:after{border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid rgba(57,57,57,.6);top:40%}.numInputWrapper span svg{width:inherit;height:auto}.numInputWrapper span svg path{fill:#00000080}.numInputWrapper:hover{background:#0000000d}.numInputWrapper:hover span{opacity:1}.flatpickr-current-month{font-size:135%;line-height:inherit;font-weight:300;color:inherit;position:absolute;width:75%;left:12.5%;padding:7.48px 0 0;line-height:1;height:34px;display:inline-block;text-align:center;-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}.flatpickr-current-month span.cur-month{font-family:inherit;font-weight:700;color:inherit;display:inline-block;margin-left:.5ch;padding:0}.flatpickr-current-month span.cur-month:hover{background:#0000000d}.flatpickr-current-month .numInputWrapper{width:6ch;width:7ch\fffd;display:inline-block}.flatpickr-current-month .numInputWrapper span.arrowUp:after{border-bottom-color:#000000e6}.flatpickr-current-month .numInputWrapper span.arrowDown:after{border-top-color:#000000e6}.flatpickr-current-month input.cur-year{background:transparent;-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;cursor:text;padding:0 0 0 .5ch;margin:0;display:inline-block;font-size:inherit;font-family:inherit;font-weight:300;line-height:inherit;height:auto;border:0;border-radius:0;vertical-align:initial;-webkit-appearance:textfield;-moz-appearance:textfield;appearance:textfield}.flatpickr-current-month input.cur-year:focus{outline:0}.flatpickr-current-month input.cur-year[disabled],.flatpickr-current-month input.cur-year[disabled]:hover{font-size:100%;color:#00000080;background:transparent;pointer-events:none}.flatpickr-current-month .flatpickr-monthDropdown-months{appearance:menulist;background:transparent;border:none;border-radius:0;box-sizing:border-box;color:inherit;cursor:pointer;font-size:inherit;font-family:inherit;font-weight:300;height:auto;line-height:inherit;margin:-1px 0 0;outline:none;padding:0 0 0 .5ch;position:relative;vertical-align:initial;-webkit-box-sizing:border-box;-webkit-appearance:menulist;-moz-appearance:menulist;width:auto}.flatpickr-current-month .flatpickr-monthDropdown-months:focus,.flatpickr-current-month .flatpickr-monthDropdown-months:active{outline:none}.flatpickr-current-month .flatpickr-monthDropdown-months:hover{background:#0000000d}.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month{background-color:transparent;outline:none;padding:0}.flatpickr-weekdays{background:transparent;text-align:center;overflow:hidden;width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:28px}.flatpickr-weekdays .flatpickr-weekdaycontainer{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}span.flatpickr-weekday{cursor:default;font-size:90%;background:transparent;color:#0000008a;line-height:1;margin:0;text-align:center;display:block;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-weight:bolder}.dayContainer,.flatpickr-weeks{padding:1px 0 0}.flatpickr-days{position:relative;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:start;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;width:307.875px}.flatpickr-days:focus{outline:0}.dayContainer{padding:0;outline:0;text-align:left;width:307.875px;min-width:307.875px;max-width:307.875px;-webkit-box-sizing:border-box;box-sizing:border-box;display:inline-block;display:-ms-flexbox;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-wrap:wrap;-ms-flex-pack:justify;-webkit-justify-content:space-around;justify-content:space-around;-webkit-transform:translate3d(0,0,0);transform:translateZ(0);opacity:1}.dayContainer+.dayContainer{-webkit-box-shadow:-1px 0 0 #e6e6e6;box-shadow:-1px 0 #e6e6e6}.flatpickr-day{background:none;border:1px solid transparent;border-radius:150px;-webkit-box-sizing:border-box;box-sizing:border-box;color:#393939;cursor:pointer;font-weight:400;width:14.2857143%;-webkit-flex-basis:14.2857143%;-ms-flex-preferred-size:14.2857143%;flex-basis:14.2857143%;max-width:39px;height:39px;line-height:39px;margin:0;display:inline-block;position:relative;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center}.flatpickr-day.inRange,.flatpickr-day.prevMonthDay.inRange,.flatpickr-day.nextMonthDay.inRange,.flatpickr-day.today.inRange,.flatpickr-day.prevMonthDay.today.inRange,.flatpickr-day.nextMonthDay.today.inRange,.flatpickr-day:hover,.flatpickr-day.prevMonthDay:hover,.flatpickr-day.nextMonthDay:hover,.flatpickr-day:focus,.flatpickr-day.prevMonthDay:focus,.flatpickr-day.nextMonthDay:focus{cursor:pointer;outline:0;background:#e6e6e6;border-color:#e6e6e6}.flatpickr-day.today{border-color:#959ea9}.flatpickr-day.today:hover,.flatpickr-day.today:focus{border-color:#959ea9;background:#959ea9;color:#fff}.flatpickr-day.selected,.flatpickr-day.startRange,.flatpickr-day.endRange,.flatpickr-day.selected.inRange,.flatpickr-day.startRange.inRange,.flatpickr-day.endRange.inRange,.flatpickr-day.selected:focus,.flatpickr-day.startRange:focus,.flatpickr-day.endRange:focus,.flatpickr-day.selected:hover,.flatpickr-day.startRange:hover,.flatpickr-day.endRange:hover,.flatpickr-day.selected.prevMonthDay,.flatpickr-day.startRange.prevMonthDay,.flatpickr-day.endRange.prevMonthDay,.flatpickr-day.selected.nextMonthDay,.flatpickr-day.startRange.nextMonthDay,.flatpickr-day.endRange.nextMonthDay{background:#569ff7;-webkit-box-shadow:none;box-shadow:none;color:#fff;border-color:#569ff7}.flatpickr-day.selected.startRange,.flatpickr-day.startRange.startRange,.flatpickr-day.endRange.startRange{border-radius:50px 0 0 50px}.flatpickr-day.selected.endRange,.flatpickr-day.startRange.endRange,.flatpickr-day.endRange.endRange{border-radius:0 50px 50px 0}.flatpickr-day.selected.startRange+.endRange:not(:nth-child(7n+1)),.flatpickr-day.startRange.startRange+.endRange:not(:nth-child(7n+1)),.flatpickr-day.endRange.startRange+.endRange:not(:nth-child(7n+1)){-webkit-box-shadow:-10px 0 0 #569ff7;box-shadow:-10px 0 #569ff7}.flatpickr-day.selected.startRange.endRange,.flatpickr-day.startRange.startRange.endRange,.flatpickr-day.endRange.startRange.endRange{border-radius:50px}.flatpickr-day.inRange{border-radius:0;-webkit-box-shadow:-5px 0 0 #e6e6e6,5px 0 0 #e6e6e6;box-shadow:-5px 0 #e6e6e6,5px 0 #e6e6e6}.flatpickr-day.flatpickr-disabled,.flatpickr-day.flatpickr-disabled:hover,.flatpickr-day.prevMonthDay,.flatpickr-day.nextMonthDay,.flatpickr-day.notAllowed,.flatpickr-day.notAllowed.prevMonthDay,.flatpickr-day.notAllowed.nextMonthDay{color:#3939394d;background:transparent;border-color:transparent;cursor:default}.flatpickr-day.flatpickr-disabled,.flatpickr-day.flatpickr-disabled:hover{cursor:not-allowed;color:#3939391a}.flatpickr-day.week.selected{border-radius:0;-webkit-box-shadow:-5px 0 0 #569ff7,5px 0 0 #569ff7;box-shadow:-5px 0 #569ff7,5px 0 #569ff7}.flatpickr-day.hidden{visibility:hidden}.rangeMode .flatpickr-day{margin-top:1px}.flatpickr-weekwrapper{float:left}.flatpickr-weekwrapper .flatpickr-weeks{padding:0 12px;-webkit-box-shadow:1px 0 0 #e6e6e6;box-shadow:1px 0 #e6e6e6}.flatpickr-weekwrapper .flatpickr-weekday{float:none;width:100%;line-height:28px}.flatpickr-weekwrapper span.flatpickr-day,.flatpickr-weekwrapper span.flatpickr-day:hover{display:block;width:100%;max-width:none;color:#3939394d;background:transparent;cursor:default;border:none}.flatpickr-innerContainer{display:block;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden}.flatpickr-rContainer{display:inline-block;padding:0;-webkit-box-sizing:border-box;box-sizing:border-box}.flatpickr-time{text-align:center;outline:0;display:block;height:0;line-height:40px;max-height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.flatpickr-time:after{content:"";display:table;clear:both}.flatpickr-time .numInputWrapper{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;width:40%;height:40px;float:left}.flatpickr-time .numInputWrapper span.arrowUp:after{border-bottom-color:#393939}.flatpickr-time .numInputWrapper span.arrowDown:after{border-top-color:#393939}.flatpickr-time.hasSeconds .numInputWrapper{width:26%}.flatpickr-time.time24hr .numInputWrapper{width:49%}.flatpickr-time input{background:transparent;-webkit-box-shadow:none;box-shadow:none;border:0;border-radius:0;text-align:center;margin:0;padding:0;height:inherit;line-height:inherit;color:#393939;font-size:14px;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:textfield;-moz-appearance:textfield;appearance:textfield}.flatpickr-time input.flatpickr-hour{font-weight:700}.flatpickr-time input.flatpickr-minute,.flatpickr-time input.flatpickr-second{font-weight:400}.flatpickr-time input:focus{outline:0;border:0}.flatpickr-time .flatpickr-time-separator,.flatpickr-time .flatpickr-am-pm{height:inherit;float:left;line-height:inherit;color:#393939;font-weight:700;width:2%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}.flatpickr-time .flatpickr-am-pm{outline:0;width:18%;cursor:pointer;text-align:center;font-weight:400}.flatpickr-time input:hover,.flatpickr-time .flatpickr-am-pm:hover,.flatpickr-time input:focus,.flatpickr-time .flatpickr-am-pm:focus{background:#eee}.flatpickr-input[readonly]{cursor:pointer}@-webkit-keyframes fpFadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}}@keyframes fpFadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}}.grid-stack{position:relative}.grid-stack-rtl{direction:ltr}.grid-stack-rtl>.grid-stack-item{direction:rtl}.grid-stack-placeholder>.placeholder-content{background-color:#0000001a;margin:0;position:absolute;width:auto;z-index:0!important}.grid-stack>.grid-stack-item{position:absolute;padding:0}.grid-stack>.grid-stack-item>.grid-stack-item-content{margin:0;position:absolute;width:auto;overflow-x:hidden;overflow-y:auto}.grid-stack>.grid-stack-item.size-to-content:not(.size-to-content-max)>.grid-stack-item-content{overflow-y:hidden}.grid-stack-item>.ui-resizable-handle{position:absolute;font-size:.1px;display:block;-ms-touch-action:none;touch-action:none}.grid-stack-item.ui-resizable-autohide>.ui-resizable-handle,.grid-stack-item.ui-resizable-disabled>.ui-resizable-handle{display:none}.grid-stack-item>.ui-resizable-ne,.grid-stack-item>.ui-resizable-nw,.grid-stack-item>.ui-resizable-se,.grid-stack-item>.ui-resizable-sw{background-image:url('data:image/svg+xml;utf8,');background-repeat:no-repeat;background-position:center}.grid-stack-item>.ui-resizable-ne{transform:translateY(10px) rotate(45deg)}.grid-stack-item>.ui-resizable-sw{transform:rotate(45deg)}.grid-stack-item>.ui-resizable-nw{transform:translateY(10px) rotate(-45deg)}.grid-stack-item>.ui-resizable-se{transform:rotate(-45deg)}.grid-stack-item>.ui-resizable-nw{cursor:nw-resize;width:20px;height:20px;top:0}.grid-stack-item>.ui-resizable-n{cursor:n-resize;height:10px;top:0;left:25px;right:25px}.grid-stack-item>.ui-resizable-ne{cursor:ne-resize;width:20px;height:20px;top:0}.grid-stack-item>.ui-resizable-e{cursor:e-resize;width:10px;top:15px;bottom:15px}.grid-stack-item>.ui-resizable-se{cursor:se-resize;width:20px;height:20px}.grid-stack-item>.ui-resizable-s{cursor:s-resize;height:10px;left:25px;bottom:0;right:25px}.grid-stack-item>.ui-resizable-sw{cursor:sw-resize;width:20px;height:20px}.grid-stack-item>.ui-resizable-w{cursor:w-resize;width:10px;top:15px;bottom:15px}.grid-stack-item.ui-draggable-dragging>.ui-resizable-handle{display:none!important}.grid-stack-item.ui-draggable-dragging{will-change:left,top;cursor:move}.grid-stack-item.ui-resizable-resizing{will-change:width,height}.ui-draggable-dragging,.ui-resizable-resizing{z-index:10000}.ui-draggable-dragging>.grid-stack-item-content,.ui-resizable-resizing>.grid-stack-item-content{box-shadow:1px 4px 6px #0003;opacity:.8}.grid-stack-animate,.grid-stack-animate .grid-stack-item{transition:left .3s,top .3s,height .3s,width .3s}.grid-stack-animate .grid-stack-item.grid-stack-placeholder,.grid-stack-animate .grid-stack-item.ui-draggable-dragging,.grid-stack-animate .grid-stack-item.ui-resizable-resizing{transition:left 0s,top 0s,height 0s,width 0s}.grid-stack>.grid-stack-item[gs-y="0"]{top:0}.grid-stack>.grid-stack-item[gs-x="0"]{left:0}.gs-12>.grid-stack-item{width:8.333%}.gs-12>.grid-stack-item[gs-x="1"]{left:8.333%}.gs-12>.grid-stack-item[gs-w="2"]{width:16.667%}.gs-12>.grid-stack-item[gs-x="2"]{left:16.667%}.gs-12>.grid-stack-item[gs-w="3"]{width:25%}.gs-12>.grid-stack-item[gs-x="3"]{left:25%}.gs-12>.grid-stack-item[gs-w="4"]{width:33.333%}.gs-12>.grid-stack-item[gs-x="4"]{left:33.333%}.gs-12>.grid-stack-item[gs-w="5"]{width:41.667%}.gs-12>.grid-stack-item[gs-x="5"]{left:41.667%}.gs-12>.grid-stack-item[gs-w="6"]{width:50%}.gs-12>.grid-stack-item[gs-x="6"]{left:50%}.gs-12>.grid-stack-item[gs-w="7"]{width:58.333%}.gs-12>.grid-stack-item[gs-x="7"]{left:58.333%}.gs-12>.grid-stack-item[gs-w="8"]{width:66.667%}.gs-12>.grid-stack-item[gs-x="8"]{left:66.667%}.gs-12>.grid-stack-item[gs-w="9"]{width:75%}.gs-12>.grid-stack-item[gs-x="9"]{left:75%}.gs-12>.grid-stack-item[gs-w="10"]{width:83.333%}.gs-12>.grid-stack-item[gs-x="10"]{left:83.333%}.gs-12>.grid-stack-item[gs-w="11"]{width:91.667%}.gs-12>.grid-stack-item[gs-x="11"]{left:91.667%}.gs-12>.grid-stack-item[gs-w="12"]{width:100%}.gs-1>.grid-stack-item{width:100%}@font-face{font-family:Inter;font-style:normal;font-weight:100;font-display:swap;src:url("./Inter-Thin-TCINWRKS.woff2?v=3.18") format("woff2"),url("./Inter-Thin-MEKOZMGA.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:100;font-display:swap;src:url("./Inter-ThinItalic-WRULPH3M.woff2?v=3.18") format("woff2"),url("./Inter-ThinItalic-ZFOL6JFL.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:200;font-display:swap;src:url("./Inter-ExtraLight-DVW3SATI.woff2?v=3.18") format("woff2"),url("./Inter-ExtraLight-A7NHN7KT.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:200;font-display:swap;src:url("./Inter-ExtraLightItalic-KXZYPKOE.woff2?v=3.18") format("woff2"),url("./Inter-ExtraLightItalic-SKGXKLLA.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:300;font-display:swap;src:url("./Inter-Light-XBRUIIRZ.woff2?v=3.18") format("woff2"),url("./Inter-Light-ARG5Y6MT.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:300;font-display:swap;src:url("./Inter-LightItalic-4OWY6V5R.woff2?v=3.18") format("woff2"),url("./Inter-LightItalic-WFS25IQZ.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url("./Inter-Regular-373XMOLM.woff2?v=3.18") format("woff2"),url("./Inter-Regular-DR3VC6YG.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:400;font-display:swap;src:url("./Inter-Italic-RW2ZALTO.woff2?v=3.18") format("woff2"),url("./Inter-Italic-GHDVOOKA.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url("./Inter-Medium-JV63DMR7.woff2?v=3.18") format("woff2"),url("./Inter-Medium-KH2CGZVE.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:500;font-display:swap;src:url("./Inter-MediumItalic-P7H6BBYU.woff2?v=3.18") format("woff2"),url("./Inter-MediumItalic-PO2E4FIG.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url("./Inter-SemiBold-UMRILHOG.woff2?v=3.18") format("woff2"),url("./Inter-SemiBold-TJ6HPMMH.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:600;font-display:swap;src:url("./Inter-SemiBoldItalic-ABO2DCC7.woff2?v=3.18") format("woff2"),url("./Inter-SemiBoldItalic-DM4JJSFU.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url("./Inter-Bold-6AVOS7AV.woff2?v=3.18") format("woff2"),url("./Inter-Bold-RTIBL5DO.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:700;font-display:swap;src:url("./Inter-BoldItalic-AID2XGS3.woff2?v=3.18") format("woff2"),url("./Inter-BoldItalic-UU7SOVRI.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:800;font-display:swap;src:url("./Inter-ExtraBold-VAPCTO5F.woff2?v=3.18") format("woff2"),url("./Inter-ExtraBold-B5RSZK6R.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:800;font-display:swap;src:url("./Inter-ExtraBoldItalic-W26CJYY7.woff2?v=3.18") format("woff2"),url("./Inter-ExtraBoldItalic-R7DLWYIK.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:normal;font-weight:900;font-display:swap;src:url("./Inter-Black-NU3KAVPI.woff2?v=3.18") format("woff2"),url("./Inter-Black-7VL4YR6G.woff?v=3.18") format("woff")}@font-face{font-family:Inter;font-style:italic;font-weight:900;font-display:swap;src:url("./Inter-BlackItalic-4BVAVUVI.woff2?v=3.18") format("woff2"),url("./Inter-BlackItalic-HJLUEYVW.woff?v=3.18") format("woff")}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url("./Inter-roman.var-EDENVCUS.woff2?v=3.18") format("woff2")}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url("./Inter-italic.var-7LS4VCXC.woff2?v=3.18") format("woff2")}@font-face{font-family:Inter var experimental;font-weight:100 900;font-display:swap;font-style:oblique 0deg 10deg;src:url("./Inter.var-FTIQFLGO.woff2?v=3.18") format("woff2")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:100;src:local("Roboto Mono Thin "),local("Roboto Mono-Thin"),url("./roboto-mono-latin-100-JQJ4Z5FD.woff2") format("woff2"),url("./roboto-mono-latin-100-SMKVIC22.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:100;src:local("Roboto Mono Thin italic"),local("Roboto Mono-Thinitalic"),url("./roboto-mono-latin-100italic-SF7I3UQS.woff2") format("woff2"),url("./roboto-mono-latin-100italic-DARDHGP2.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:200;src:local("Roboto Mono Extra Light "),local("Roboto Mono-Extra Light"),url("./roboto-mono-latin-200-GAB3DPCB.woff2") format("woff2"),url("./roboto-mono-latin-200-GNXGWAU7.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:200;src:local("Roboto Mono Extra Light italic"),local("Roboto Mono-Extra Lightitalic"),url("./roboto-mono-latin-200italic-YC3AN6RQ.woff2") format("woff2"),url("./roboto-mono-latin-200italic-MBEOZD5A.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:300;src:local("Roboto Mono Light "),local("Roboto Mono-Light"),url("./roboto-mono-latin-300-76QG53G7.woff2") format("woff2"),url("./roboto-mono-latin-300-KB4HCXJB.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:300;src:local("Roboto Mono Light italic"),local("Roboto Mono-Lightitalic"),url("./roboto-mono-latin-300italic-ATWQWJG4.woff2") format("woff2"),url("./roboto-mono-latin-300italic-UCTTXECO.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:400;src:local("Roboto Mono Regular "),local("Roboto Mono-Regular"),url("./roboto-mono-latin-400-OKRWGZOX.woff2") format("woff2"),url("./roboto-mono-latin-400-L25YYWY3.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:400;src:local("Roboto Mono Regular italic"),local("Roboto Mono-Regularitalic"),url("./roboto-mono-latin-400italic-UPQRVZWX.woff2") format("woff2"),url("./roboto-mono-latin-400italic-3G7XMJ7A.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:500;src:local("Roboto Mono Medium "),local("Roboto Mono-Medium"),url("./roboto-mono-latin-500-67YXJMLO.woff2") format("woff2"),url("./roboto-mono-latin-500-4ZB2P7GK.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:500;src:local("Roboto Mono Medium italic"),local("Roboto Mono-Mediumitalic"),url("./roboto-mono-latin-500italic-U7WRIR22.woff2") format("woff2"),url("./roboto-mono-latin-500italic-RXGCSZS4.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:600;src:local("Roboto Mono SemiBold "),local("Roboto Mono-SemiBold"),url("./roboto-mono-latin-600-ZRZYGQNG.woff2") format("woff2"),url("./roboto-mono-latin-600-USMEYMTS.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:600;src:local("Roboto Mono SemiBold italic"),local("Roboto Mono-SemiBolditalic"),url("./roboto-mono-latin-600italic-THUILB76.woff2") format("woff2"),url("./roboto-mono-latin-600italic-TLIW3M4P.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:normal;font-display:swap;font-weight:700;src:local("Roboto Mono Bold "),local("Roboto Mono-Bold"),url("./roboto-mono-latin-700-QNNSSZIK.woff2") format("woff2"),url("./roboto-mono-latin-700-PEQVO3WV.woff") format("woff")}@font-face{font-family:Roboto Mono;font-style:italic;font-display:swap;font-weight:700;src:local("Roboto Mono Bold italic"),local("Roboto Mono-Bolditalic"),url("./roboto-mono-latin-700italic-WWLRMKIN.woff2") format("woff2"),url("./roboto-mono-latin-700italic-KTWD2UYU.woff") format("woff")} diff --git a/netbox/project-static/dist/netbox.css b/netbox/project-static/dist/netbox.css index 8bf61408f..82a3edd1d 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: #667382;--tblr-gray-dark: #182433;--tblr-gray-100: #f6f8fb;--tblr-gray-200: #eef1f4;--tblr-gray-300: #dadfe5;--tblr-gray-400: #bbc3cd;--tblr-gray-500: #929dab;--tblr-gray-600: #667382;--tblr-gray-700: #3a4859;--tblr-gray-800: #182433;--tblr-gray-900: #040a11;--tblr-primary: #00857D;--tblr-secondary: #667382;--tblr-success: #2fb344;--tblr-info: #4299e1;--tblr-warning: #f76707;--tblr-danger: #d63939;--tblr-light: #fcfdfe;--tblr-dark: #182433;--tblr-muted: #667382;--tblr-blue: #0054a6;--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-facebook: #1877f2;--tblr-twitter: #1da1f2;--tblr-linkedin: #0a66c2;--tblr-google: #dc4e41;--tblr-youtube: #ff0000;--tblr-vimeo: #1ab7ea;--tblr-dribbble: #ea4c89;--tblr-github: #181717;--tblr-instagram: #e4405f;--tblr-pinterest: #bd081c;--tblr-vk: #6383a8;--tblr-rss: #ffa500;--tblr-flickr: #0063dc;--tblr-bitbucket: #0052cc;--tblr-tabler: #0054a6;--tblr-primary-rgb: 0, 133, 125;--tblr-secondary-rgb: 102, 115, 130;--tblr-success-rgb: 47, 179, 68;--tblr-info-rgb: 66, 153, 225;--tblr-warning-rgb: 247, 103, 7;--tblr-danger-rgb: 214, 57, 57;--tblr-light-rgb: 252, 253, 254;--tblr-dark-rgb: 24, 36, 51;--tblr-muted-rgb: 102, 115, 130;--tblr-blue-rgb: 0, 84, 166;--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-facebook-rgb: 24, 119, 242;--tblr-twitter-rgb: 29, 161, 242;--tblr-linkedin-rgb: 10, 102, 194;--tblr-google-rgb: 220, 78, 65;--tblr-youtube-rgb: 255, 0, 0;--tblr-vimeo-rgb: 26, 183, 234;--tblr-dribbble-rgb: 234, 76, 137;--tblr-github-rgb: 24, 23, 23;--tblr-instagram-rgb: 228, 64, 95;--tblr-pinterest-rgb: 189, 8, 28;--tblr-vk-rgb: 99, 131, 168;--tblr-rss-rgb: 255, 165, 0;--tblr-flickr-rgb: 0, 99, 220;--tblr-bitbucket-rgb: 0, 82, 204;--tblr-tabler-rgb: 0, 84, 166;--tblr-primary-text-emphasis: #003532;--tblr-secondary-text-emphasis: #292e34;--tblr-success-text-emphasis: #13481b;--tblr-info-text-emphasis: #1a3d5a;--tblr-warning-text-emphasis: #632903;--tblr-danger-text-emphasis: #561717;--tblr-light-text-emphasis: #3a4859;--tblr-dark-text-emphasis: #3a4859;--tblr-primary-bg-subtle: #cce7e5;--tblr-secondary-bg-subtle: #e0e3e6;--tblr-success-bg-subtle: #d5f0da;--tblr-info-bg-subtle: #d9ebf9;--tblr-warning-bg-subtle: #fde1cd;--tblr-danger-bg-subtle: #f7d7d7;--tblr-light-bg-subtle: #fbfcfd;--tblr-dark-bg-subtle: #bbc3cd;--tblr-primary-border-subtle: #99cecb;--tblr-secondary-border-subtle: #c2c7cd;--tblr-success-border-subtle: #ace1b4;--tblr-info-border-subtle: #b3d6f3;--tblr-warning-border-subtle: #fcc29c;--tblr-danger-border-subtle: #efb0b0;--tblr-light-border-subtle: #eef1f4;--tblr-dark-border-subtle: #929dab;--tblr-white-rgb: 255, 255, 255;--tblr-black-rgb: 0, 0, 0;--tblr-font-sans-serif: "Inter", system-ui, sans-serif;--tblr-font-monospace: Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--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: #182433;--tblr-body-color-rgb: 24, 36, 51;--tblr-body-bg: #f6f8fb;--tblr-body-bg-rgb: 246, 248, 251;--tblr-emphasis-color: #182433;--tblr-emphasis-color-rgb: 24, 36, 51;--tblr-secondary-color: rgba(24, 36, 51, .75);--tblr-secondary-color-rgb: 24, 36, 51;--tblr-secondary-bg: #eef1f4;--tblr-secondary-bg-rgb: 238, 241, 244;--tblr-tertiary-color: rgba(24, 36, 51, .5);--tblr-tertiary-color-rgb: 24, 36, 51;--tblr-tertiary-bg: #f6f8fb;--tblr-tertiary-bg-rgb: 246, 248, 251;--tblr-heading-color: inherit;--tblr-link-color: #00857D;--tblr-link-color-rgb: 0, 133, 125;--tblr-link-decoration: none;--tblr-link-hover-color: #006a64;--tblr-link-hover-color-rgb: 0, 106, 100;--tblr-link-hover-decoration: underline;--tblr-code-color: var(--tblr-gray-600);--tblr-highlight-color: #182433;--tblr-highlight-bg: #fdeccc;--tblr-border-width: 1px;--tblr-border-style: solid;--tblr-border-color: #dadfe5;--tblr-border-color-translucent: rgba(4, 32, 69, .14);--tblr-border-radius: 4px;--tblr-border-radius-sm: 2px;--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: #fcfdfe;--tblr-body-color-rgb: 252, 253, 254;--tblr-body-bg: #040a11;--tblr-body-bg-rgb: 4, 10, 17;--tblr-emphasis-color: #ffffff;--tblr-emphasis-color-rgb: 255, 255, 255;--tblr-secondary-color: rgba(252, 253, 254, .75);--tblr-secondary-color-rgb: 252, 253, 254;--tblr-secondary-bg: #182433;--tblr-secondary-bg-rgb: 24, 36, 51;--tblr-tertiary-color: rgba(252, 253, 254, .5);--tblr-tertiary-color-rgb: 252, 253, 254;--tblr-tertiary-bg: #0e1722;--tblr-tertiary-bg-rgb: 14, 23, 34;--tblr-primary-text-emphasis: #66b6b1;--tblr-secondary-text-emphasis: #a3abb4;--tblr-success-text-emphasis: #82d18f;--tblr-info-text-emphasis: #8ec2ed;--tblr-warning-text-emphasis: #faa46a;--tblr-danger-text-emphasis: #e68888;--tblr-light-text-emphasis: #f6f8fb;--tblr-dark-text-emphasis: #dadfe5;--tblr-primary-bg-subtle: #001b19;--tblr-secondary-bg-subtle: #14171a;--tblr-success-bg-subtle: #09240e;--tblr-info-bg-subtle: #0d1f2d;--tblr-warning-bg-subtle: #311501;--tblr-danger-bg-subtle: #2b0b0b;--tblr-light-bg-subtle: #182433;--tblr-dark-bg-subtle: #0c121a;--tblr-primary-border-subtle: #00504b;--tblr-secondary-border-subtle: #3d454e;--tblr-success-border-subtle: #1c6b29;--tblr-info-border-subtle: #285c87;--tblr-warning-border-subtle: #943e04;--tblr-danger-border-subtle: #802222;--tblr-light-border-subtle: #3a4859;--tblr-dark-border-subtle: #182433;--tblr-heading-color: inherit;--tblr-link-color: #66b6b1;--tblr-link-hover-color: #85c5c1;--tblr-link-color-rgb: 102, 182, 177;--tblr-link-hover-color-rgb: 133, 197, 193;--tblr-code-color: var(--tblr-gray-300);--tblr-highlight-color: #fcfdfe;--tblr-highlight-bg: #624000;--tblr-border-color: #1f2e41;--tblr-border-color-translucent: rgba(72, 110, 149, .14);--tblr-form-valid-color: #82d18f;--tblr-form-valid-border-color: #82d18f;--tblr-form-invalid-color: #e68888;--tblr-form-invalid-border-color: #e68888}*,*: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:85.714285%}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:85.714285%;color:var(--tblr-light)}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:85.714285%;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:2px}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;font-size:1.5rem;line-height:inherit}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}::-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-size:5rem;font-weight:300;line-height:1.2}.display-2{font-size:4.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}.display-5{font-size:3rem;font-weight:300;line-height:1.2}.display-6{font-size:2rem;font-weight:300;line-height:1.2}.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:85.714285%;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:85.714285%;color:#667382}.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:85.714285%;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: 3rem}.g-6,.gy-6{--tblr-gutter-y: 3rem}.g-7,.gx-7{--tblr-gutter-x: 5rem}.g-7,.gy-7{--tblr-gutter-y: 5rem}.g-8,.gx-8{--tblr-gutter-x: 8rem}.g-8,.gy-8{--tblr-gutter-y: 8rem}@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: 3rem}.g-sm-6,.gy-sm-6{--tblr-gutter-y: 3rem}.g-sm-7,.gx-sm-7{--tblr-gutter-x: 5rem}.g-sm-7,.gy-sm-7{--tblr-gutter-y: 5rem}.g-sm-8,.gx-sm-8{--tblr-gutter-x: 8rem}.g-sm-8,.gy-sm-8{--tblr-gutter-y: 8rem}}@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: 3rem}.g-md-6,.gy-md-6{--tblr-gutter-y: 3rem}.g-md-7,.gx-md-7{--tblr-gutter-x: 5rem}.g-md-7,.gy-md-7{--tblr-gutter-y: 5rem}.g-md-8,.gx-md-8{--tblr-gutter-x: 8rem}.g-md-8,.gy-md-8{--tblr-gutter-y: 8rem}}@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: 3rem}.g-lg-6,.gy-lg-6{--tblr-gutter-y: 3rem}.g-lg-7,.gx-lg-7{--tblr-gutter-x: 5rem}.g-lg-7,.gy-lg-7{--tblr-gutter-y: 5rem}.g-lg-8,.gx-lg-8{--tblr-gutter-x: 8rem}.g-lg-8,.gy-lg-8{--tblr-gutter-y: 8rem}}@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: 3rem}.g-xl-6,.gy-xl-6{--tblr-gutter-y: 3rem}.g-xl-7,.gx-xl-7{--tblr-gutter-x: 5rem}.g-xl-7,.gy-xl-7{--tblr-gutter-y: 5rem}.g-xl-8,.gx-xl-8{--tblr-gutter-x: 8rem}.g-xl-8,.gy-xl-8{--tblr-gutter-y: 8rem}}@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: 3rem}.g-xxl-6,.gy-xxl-6{--tblr-gutter-y: 3rem}.g-xxl-7,.gx-xxl-7{--tblr-gutter-x: 5rem}.g-xxl-7,.gy-xxl-7{--tblr-gutter-y: 5rem}.g-xxl-8,.gx-xxl-8{--tblr-gutter-x: 8rem}.g-xxl-8,.gy-xxl-8{--tblr-gutter-y: 8rem}}.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: rgba(var(--tblr-emphasis-color-rgb), .1);--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)>*>*{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(even)>*{--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(even){--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: #182433;--tblr-table-bg: #cce7e5;--tblr-table-border-color: #a8c0c1;--tblr-table-striped-bg: #c3dddc;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #bad4d3;--tblr-table-active-color: #fcfdfe;--tblr-table-hover-bg: #bfd8d8;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-secondary{--tblr-table-color: #182433;--tblr-table-bg: #e0e3e6;--tblr-table-border-color: #b8bdc2;--tblr-table-striped-bg: #d6d9dd;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #ccd0d4;--tblr-table-active-color: #fcfdfe;--tblr-table-hover-bg: #d1d5d9;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-success{--tblr-table-color: #182433;--tblr-table-bg: #d5f0da;--tblr-table-border-color: #afc7b9;--tblr-table-striped-bg: #cce6d2;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #c2dcc9;--tblr-table-active-color: #182433;--tblr-table-hover-bg: #c7e1cd;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-info{--tblr-table-color: #182433;--tblr-table-bg: #d9ebf9;--tblr-table-border-color: #b2c3d1;--tblr-table-striped-bg: #cfe1ef;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #c6d7e5;--tblr-table-active-color: #182433;--tblr-table-hover-bg: #cbdcea;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-warning{--tblr-table-color: #182433;--tblr-table-bg: #fde1cd;--tblr-table-border-color: #cfbbae;--tblr-table-striped-bg: #f2d8c5;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #e6cebe;--tblr-table-active-color: #182433;--tblr-table-hover-bg: #ecd3c1;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-danger{--tblr-table-color: #182433;--tblr-table-bg: #f7d7d7;--tblr-table-border-color: #cab3b6;--tblr-table-striped-bg: #eccecf;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #e1c5c7;--tblr-table-active-color: #fcfdfe;--tblr-table-hover-bg: #e6cacb;--tblr-table-hover-color: #fcfdfe;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-light{--tblr-table-color: #182433;--tblr-table-bg: #fcfdfe;--tblr-table-border-color: #ced2d5;--tblr-table-striped-bg: #f1f2f4;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #e5e7ea;--tblr-table-active-color: #182433;--tblr-table-hover-bg: #ebedef;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-dark{--tblr-table-color: #fcfdfe;--tblr-table-bg: #182433;--tblr-table-border-color: #464f5c;--tblr-table-striped-bg: #232f3d;--tblr-table-striped-color: #fcfdfe;--tblr-table-active-bg: #2f3a47;--tblr-table-active-color: #fcfdfe;--tblr-table-hover-bg: #293442;--tblr-table-hover-color: #fcfdfe;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.4285714286}.col-form-label-lg{padding-top:calc(.5rem + var(--tblr-border-width));padding-bottom:calc(.5rem + var(--tblr-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.125rem + var(--tblr-border-width));padding-bottom:calc(.125rem + var(--tblr-border-width));font-size:.75rem}.form-text{margin-top:.25rem;font-size:85.714285%;color:var(--tblr-secondary-color)}.form-control{display:block;width:100%;padding:.5625rem .75rem;font-family:var(--tblr-font-sans-serif);font-size:.875rem;font-weight:400;line-height:1.4285714286;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-box-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-box-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.4285714286em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::placeholder{color:#929dab;opacity:1}.form-control:disabled{background-color:var(--tblr-bg-surface-secondary);opacity:1}.form-control::file-selector-button{padding:.5625rem .75rem;margin:-.5625rem -.75rem;margin-inline-end:.75rem;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.4285714286;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.4285714286em + .25rem + calc(var(--tblr-border-width) * 2));padding:.125rem .25rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.form-control-sm::file-selector-button{padding:.125rem .25rem;margin:-.125rem -.25rem;margin-inline-end:.25rem}.form-control-lg{min-height:calc(1.4285714286em + 1rem + calc(var(--tblr-border-width) * 2));padding:.5rem .75rem;font-size:1.25rem;border-radius:var(--tblr-border-radius-lg)}.form-control-lg::file-selector-button{padding:.5rem .75rem;margin:-.5rem -.75rem;margin-inline-end:.75rem}textarea.form-control{min-height:calc(1.4285714286em + 1.125rem + calc(var(--tblr-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.4285714286em + .25rem + calc(var(--tblr-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.4285714286em + 1rem + calc(var(--tblr-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.4285714286em + 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.4285714286em + .25rem + calc(var(--tblr-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.4285714286em + 1rem + 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='%23929dab' 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 2.25rem .5625rem .75rem;font-family:var(--tblr-font-sans-serif);font-size:.875rem;font-weight:400;line-height:1.4285714286;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 .75rem 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-box-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-box-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;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:.125rem;padding-bottom:.125rem;padding-left:.25rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:.75rem;font-size:1.25rem;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='%23fcfdfe' 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='%23dadfe5'/%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}@media (prefers-reduced-motion: reduce){.form-switch .form-check-input{transition:none}}.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='%2380c2be'/%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 #f6f8fb,0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #f6f8fb,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;height:100%;padding:1rem .75rem;overflow:hidden;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 .75rem}.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}.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{color:rgba(var(--tblr-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translate(.15rem)}.form-floating>.form-control:focus~label:after,.form-floating>.form-control:not(:placeholder-shown)~label:after,.form-floating>.form-control-plaintext~label:after,.form-floating>.form-select~label:after{position:absolute;inset:1rem .375rem;z-index:-1;height:1.5em;content:"";background-color:var(--tblr-bg-forms);border-radius:var(--tblr-border-radius)}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--tblr-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translate(.15rem)}.form-floating>.form-control-plaintext~label{border-width:var(--tblr-border-width) 0}.form-floating>:disabled~label,.form-floating>.form-control:disabled~label{color:#667382}.form-floating>:disabled~label:after,.form-floating>.form-control:disabled~label:after{background-color:var(--tblr-bg-surface-secondary)}.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 .75rem;font-size:.875rem;font-weight:400;line-height:1.4285714286;color:var(--tblr-secondary);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:.5rem .75rem;font-size:1.25rem;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:.125rem .25rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.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(var(--tblr-border-width) * -1);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:85.714285%;color:var(--tblr-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:var(--tblr-spacer-2) var(--tblr-spacer-2);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:calc(1.42857em + 1.125rem);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 calc(0.35714em + 0.28125rem) center;background-size:calc(0.71429em + 0.5625rem) calc(0.71429em + 0.5625rem)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:var(--tblr-form-valid-border-color);box-shadow:var(--tblr-box-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:calc(1.42857em + 1.125rem);background-position:top calc(0.35714em + 0.28125rem) right calc(0.35714em + 0.28125rem)}.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:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.71429em + 0.5625rem) calc(0.71429em + 0.5625rem)}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:var(--tblr-form-valid-border-color);box-shadow:var(--tblr-box-shadow-input),0 0 0 .25rem rgba(var(--tblr-success-rgb),.25)}.was-validated .form-control-color:valid,.form-control-color.is-valid{width:calc(4.125rem + 1.42857em)}.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:85.714285%;color:var(--tblr-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:var(--tblr-spacer-2) var(--tblr-spacer-2);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,.is-invalid~.invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:var(--tblr-form-invalid-border-color);padding-right:calc(1.42857em + 1.125rem);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 calc(0.35714em + 0.28125rem) center;background-size:calc(0.71429em + 0.5625rem) calc(0.71429em + 0.5625rem)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:var(--tblr-form-invalid-border-color);box-shadow:var(--tblr-box-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:calc(1.42857em + 1.125rem);background-position:top calc(0.35714em + 0.28125rem) right calc(0.35714em + 0.28125rem)}.was-validated .form-select:invalid,.form-select.is-invalid{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]),.form-select.is-invalid: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:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.71429em + 0.5625rem) calc(0.71429em + 0.5625rem)}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus{border-color:var(--tblr-form-invalid-border-color);box-shadow:var(--tblr-box-shadow-input),0 0 0 .25rem rgba(var(--tblr-danger-rgb),.25)}.was-validated .form-control-color:invalid,.form-control-color.is-invalid{width:calc(4.125rem + 1.42857em)}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:var(--tblr-form-invalid-border-color)}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:var(--tblr-form-invalid-color)}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid: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{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,.was-validated .input-group>.form-select:not(:focus):invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.input-group>.form-floating:not(:focus-within).is-invalid{z-index:4}.btn{--tblr-btn-padding-x: .5rem;--tblr-btn-padding-y: .25rem;--tblr-btn-font-family: var(--tblr-font-sans-serif);--tblr-btn-font-size: .875rem;--tblr-btn-font-weight: var(--tblr-font-weight-medium);--tblr-btn-line-height: 1.4285714286;--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-box-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: #667382;--tblr-btn-disabled-border-color: transparent;--tblr-btn-box-shadow: 0 0 0 #000;--tblr-btn-focus-shadow-rgb: 38, 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: .5rem;--tblr-btn-padding-x: .75rem;--tblr-btn-font-size: 1.25rem;--tblr-btn-border-radius: var(--tblr-border-radius-lg)}.btn-sm,.btn-group-sm>.btn{--tblr-btn-padding-y: .125rem;--tblr-btn-padding-x: .25rem;--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);--tblr-dropdown-box-shadow: var(--tblr-box-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: #667382;--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: #dadfe5;--tblr-dropdown-bg: #182433;--tblr-dropdown-border-color: var(--tblr-border-color-translucent);--tblr-dropdown-box-shadow: ;--tblr-dropdown-link-color: #dadfe5;--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: #929dab;--tblr-dropdown-header-color: #929dab}.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(var(--tblr-border-width) * -1)}.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:.1875rem;padding-left:.1875rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.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(var(--tblr-border-width) * -1)}.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~.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-secondary);--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}@media (prefers-reduced-motion: reduce){.nav-link{transition:none}}.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-basis:0;flex-grow:1;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-body-color);--tblr-navbar-hover-color: rgba(var(--tblr-emphasis-color-rgb), .8);--tblr-navbar-disabled-color: var(--tblr-disabled-color);--tblr-navbar-active-color: var(--tblr-body-color) 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) 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%2824, 36, 51, 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-basis:100%;flex-grow:1;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: 1.25rem;--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);--tblr-card-box-shadow: var(--tblr-shadow-card);--tblr-card-inner-border-radius: calc(var(--tblr-border-radius) - (var(--tblr-border-width)));--tblr-card-cap-padding-y: 1.25rem;--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}}.accordion{--tblr-accordion-color: var(--tblr-body-color);--tblr-accordion-bg: transparent;--tblr-accordion-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out, border-radius .15s ease;--tblr-accordion-border-color: var(--tblr-border-color-translucent);--tblr-accordion-border-width: var(--tblr-border-width);--tblr-accordion-border-radius: var(--tblr-border-radius);--tblr-accordion-inner-border-radius: calc(var(--tblr-border-radius) - (var(--tblr-border-width)));--tblr-accordion-btn-padding-x: 1.25rem;--tblr-accordion-btn-padding-y: 1rem;--tblr-accordion-btn-color: var(--tblr-body-color);--tblr-accordion-btn-bg: transparent;--tblr-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23182433' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--tblr-accordion-btn-icon-width: 1rem;--tblr-accordion-btn-icon-transform: rotate(-180deg);--tblr-accordion-btn-icon-transition: transform .2s ease-in-out;--tblr-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23003532' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--tblr-accordion-btn-focus-box-shadow: 0 0 0 .25rem rgba(var(--tblr-primary-rgb), .25);--tblr-accordion-body-padding-x: 1.25rem;--tblr-accordion-body-padding-y: 1rem;--tblr-accordion-active-color: inherit;--tblr-accordion-active-bg: transparent}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--tblr-accordion-btn-padding-y) var(--tblr-accordion-btn-padding-x);font-size:.875rem;color:var(--tblr-accordion-btn-color);text-align:left;background-color:var(--tblr-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--tblr-accordion-transition)}@media (prefers-reduced-motion: reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--tblr-accordion-active-color);background-color:var(--tblr-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--tblr-accordion-border-width)) 0 var(--tblr-accordion-border-color)}.accordion-button:not(.collapsed):after{background-image:var(--tblr-accordion-btn-active-icon);transform:var(--tblr-accordion-btn-icon-transform)}.accordion-button:after{flex-shrink:0;width:var(--tblr-accordion-btn-icon-width);height:var(--tblr-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--tblr-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--tblr-accordion-btn-icon-width);transition:var(--tblr-accordion-btn-icon-transition)}@media (prefers-reduced-motion: reduce){.accordion-button:after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--tblr-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--tblr-accordion-color);background-color:var(--tblr-accordion-bg);border:var(--tblr-accordion-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 .accordion-button{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 .accordion-button.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{padding:var(--tblr-accordion-body-padding-y) var(--tblr-accordion-body-padding-x)}.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-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}.accordion-flush>.accordion-item>.accordion-collapse{border-radius:0}[data-bs-theme=dark] .accordion-button:after{--tblr-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2366b6b1'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--tblr-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2366b6b1'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.breadcrumb{--tblr-breadcrumb-padding-x: 0;--tblr-breadcrumb-padding-y: 0;--tblr-breadcrumb-margin-bottom: 1rem;--tblr-breadcrumb-bg: ;--tblr-breadcrumb-border-radius: ;--tblr-breadcrumb-divider-color: var(--tblr-secondary);--tblr-breadcrumb-item-padding-x: .5rem;--tblr-breadcrumb-item-active-color: inherit;display:flex;flex-wrap:wrap;padding:var(--tblr-breadcrumb-padding-y) var(--tblr-breadcrumb-padding-x);margin-bottom:var(--tblr-breadcrumb-margin-bottom);font-size:var(--tblr-breadcrumb-font-size);list-style:none;background-color:var(--tblr-breadcrumb-bg);border-radius:var(--tblr-breadcrumb-border-radius)}.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-item.active{color:var(--tblr-breadcrumb-item-active-color)}.pagination{--tblr-pagination-padding-x: .25rem;--tblr-pagination-padding-y: .25rem;--tblr-pagination-font-size: .875rem;--tblr-pagination-color: var(--tblr-secondary);--tblr-pagination-bg: transparent;--tblr-pagination-border-width: 0;--tblr-pagination-border-color: var(--tblr-border-color);--tblr-pagination-border-radius: var(--tblr-border-radius);--tblr-pagination-hover-color: var(--tblr-link-hover-color);--tblr-pagination-hover-bg: var(--tblr-tertiary-bg);--tblr-pagination-hover-border-color: var(--tblr-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-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:-0}.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)}.badge{--tblr-badge-padding-x: .5em;--tblr-badge-padding-y: .25em;--tblr-badge-font-size: 85.714285%;--tblr-badge-font-weight: var(--tblr-font-weight-medium);--tblr-badge-color: var(--tblr-secondary);--tblr-badge-border-radius: var(--tblr-border-radius);display:inline-block;padding:var(--tblr-badge-padding-y) var(--tblr-badge-padding-x);font-size:var(--tblr-badge-font-size);font-weight:var(--tblr-badge-font-weight);line-height:1;color:var(--tblr-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--tblr-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--tblr-alert-bg: transparent;--tblr-alert-padding-x: 1rem;--tblr-alert-padding-y: .75rem;--tblr-alert-margin-bottom: 1rem;--tblr-alert-color: inherit;--tblr-alert-border-color: 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;position:relative;padding:var(--tblr-alert-padding-y) var(--tblr-alert-padding-x);margin-bottom:var(--tblr-alert-margin-bottom);color:var(--tblr-alert-color);background-color:var(--tblr-alert-bg);border:var(--tblr-alert-border);border-radius:var(--tblr-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:var(--tblr-font-weight-bold);color:var(--tblr-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:.9375rem 1rem}.alert-primary{--tblr-alert-color: var(--tblr-primary-text-emphasis);--tblr-alert-bg: var(--tblr-primary-bg-subtle);--tblr-alert-border-color: var(--tblr-primary-border-subtle);--tblr-alert-link-color: var(--tblr-primary-text-emphasis)}.alert-secondary{--tblr-alert-color: var(--tblr-secondary-text-emphasis);--tblr-alert-bg: var(--tblr-secondary-bg-subtle);--tblr-alert-border-color: var(--tblr-secondary-border-subtle);--tblr-alert-link-color: var(--tblr-secondary-text-emphasis)}.alert-success{--tblr-alert-color: var(--tblr-success-text-emphasis);--tblr-alert-bg: var(--tblr-success-bg-subtle);--tblr-alert-border-color: var(--tblr-success-border-subtle);--tblr-alert-link-color: var(--tblr-success-text-emphasis)}.alert-info{--tblr-alert-color: var(--tblr-info-text-emphasis);--tblr-alert-bg: var(--tblr-info-bg-subtle);--tblr-alert-border-color: var(--tblr-info-border-subtle);--tblr-alert-link-color: var(--tblr-info-text-emphasis)}.alert-warning{--tblr-alert-color: var(--tblr-warning-text-emphasis);--tblr-alert-bg: var(--tblr-warning-bg-subtle);--tblr-alert-border-color: var(--tblr-warning-border-subtle);--tblr-alert-link-color: var(--tblr-warning-text-emphasis)}.alert-danger{--tblr-alert-color: var(--tblr-danger-text-emphasis);--tblr-alert-bg: var(--tblr-danger-bg-subtle);--tblr-alert-border-color: var(--tblr-danger-border-subtle);--tblr-alert-link-color: var(--tblr-danger-text-emphasis)}.alert-light{--tblr-alert-color: var(--tblr-light-text-emphasis);--tblr-alert-bg: var(--tblr-light-bg-subtle);--tblr-alert-border-color: var(--tblr-light-border-subtle);--tblr-alert-link-color: var(--tblr-light-text-emphasis)}.alert-dark{--tblr-alert-color: var(--tblr-dark-text-emphasis);--tblr-alert-bg: var(--tblr-dark-bg-subtle);--tblr-alert-border-color: var(--tblr-dark-border-subtle);--tblr-alert-link-color: var(--tblr-dark-text-emphasis)}.alert-muted{--tblr-alert-color: var(--tblr-muted-text-emphasis);--tblr-alert-bg: var(--tblr-muted-bg-subtle);--tblr-alert-border-color: var(--tblr-muted-border-subtle);--tblr-alert-link-color: var(--tblr-muted-text-emphasis)}.alert-blue{--tblr-alert-color: var(--tblr-blue-text-emphasis);--tblr-alert-bg: var(--tblr-blue-bg-subtle);--tblr-alert-border-color: var(--tblr-blue-border-subtle);--tblr-alert-link-color: var(--tblr-blue-text-emphasis)}.alert-azure{--tblr-alert-color: var(--tblr-azure-text-emphasis);--tblr-alert-bg: var(--tblr-azure-bg-subtle);--tblr-alert-border-color: var(--tblr-azure-border-subtle);--tblr-alert-link-color: var(--tblr-azure-text-emphasis)}.alert-indigo{--tblr-alert-color: var(--tblr-indigo-text-emphasis);--tblr-alert-bg: var(--tblr-indigo-bg-subtle);--tblr-alert-border-color: var(--tblr-indigo-border-subtle);--tblr-alert-link-color: var(--tblr-indigo-text-emphasis)}.alert-purple{--tblr-alert-color: var(--tblr-purple-text-emphasis);--tblr-alert-bg: var(--tblr-purple-bg-subtle);--tblr-alert-border-color: var(--tblr-purple-border-subtle);--tblr-alert-link-color: var(--tblr-purple-text-emphasis)}.alert-pink{--tblr-alert-color: var(--tblr-pink-text-emphasis);--tblr-alert-bg: var(--tblr-pink-bg-subtle);--tblr-alert-border-color: var(--tblr-pink-border-subtle);--tblr-alert-link-color: var(--tblr-pink-text-emphasis)}.alert-red{--tblr-alert-color: var(--tblr-red-text-emphasis);--tblr-alert-bg: var(--tblr-red-bg-subtle);--tblr-alert-border-color: var(--tblr-red-border-subtle);--tblr-alert-link-color: var(--tblr-red-text-emphasis)}.alert-orange{--tblr-alert-color: var(--tblr-orange-text-emphasis);--tblr-alert-bg: var(--tblr-orange-bg-subtle);--tblr-alert-border-color: var(--tblr-orange-border-subtle);--tblr-alert-link-color: var(--tblr-orange-text-emphasis)}.alert-yellow{--tblr-alert-color: var(--tblr-yellow-text-emphasis);--tblr-alert-bg: var(--tblr-yellow-bg-subtle);--tblr-alert-border-color: var(--tblr-yellow-border-subtle);--tblr-alert-link-color: var(--tblr-yellow-text-emphasis)}.alert-lime{--tblr-alert-color: var(--tblr-lime-text-emphasis);--tblr-alert-bg: var(--tblr-lime-bg-subtle);--tblr-alert-border-color: var(--tblr-lime-border-subtle);--tblr-alert-link-color: var(--tblr-lime-text-emphasis)}.alert-green{--tblr-alert-color: var(--tblr-green-text-emphasis);--tblr-alert-bg: var(--tblr-green-bg-subtle);--tblr-alert-border-color: var(--tblr-green-border-subtle);--tblr-alert-link-color: var(--tblr-green-text-emphasis)}.alert-teal{--tblr-alert-color: var(--tblr-teal-text-emphasis);--tblr-alert-bg: var(--tblr-teal-bg-subtle);--tblr-alert-border-color: var(--tblr-teal-border-subtle);--tblr-alert-link-color: var(--tblr-teal-text-emphasis)}.alert-cyan{--tblr-alert-color: var(--tblr-cyan-text-emphasis);--tblr-alert-bg: var(--tblr-cyan-bg-subtle);--tblr-alert-border-color: var(--tblr-cyan-border-subtle);--tblr-alert-link-color: var(--tblr-cyan-text-emphasis)}.alert-facebook{--tblr-alert-color: var(--tblr-facebook-text-emphasis);--tblr-alert-bg: var(--tblr-facebook-bg-subtle);--tblr-alert-border-color: var(--tblr-facebook-border-subtle);--tblr-alert-link-color: var(--tblr-facebook-text-emphasis)}.alert-twitter{--tblr-alert-color: var(--tblr-twitter-text-emphasis);--tblr-alert-bg: var(--tblr-twitter-bg-subtle);--tblr-alert-border-color: var(--tblr-twitter-border-subtle);--tblr-alert-link-color: var(--tblr-twitter-text-emphasis)}.alert-linkedin{--tblr-alert-color: var(--tblr-linkedin-text-emphasis);--tblr-alert-bg: var(--tblr-linkedin-bg-subtle);--tblr-alert-border-color: var(--tblr-linkedin-border-subtle);--tblr-alert-link-color: var(--tblr-linkedin-text-emphasis)}.alert-google{--tblr-alert-color: var(--tblr-google-text-emphasis);--tblr-alert-bg: var(--tblr-google-bg-subtle);--tblr-alert-border-color: var(--tblr-google-border-subtle);--tblr-alert-link-color: var(--tblr-google-text-emphasis)}.alert-youtube{--tblr-alert-color: var(--tblr-youtube-text-emphasis);--tblr-alert-bg: var(--tblr-youtube-bg-subtle);--tblr-alert-border-color: var(--tblr-youtube-border-subtle);--tblr-alert-link-color: var(--tblr-youtube-text-emphasis)}.alert-vimeo{--tblr-alert-color: var(--tblr-vimeo-text-emphasis);--tblr-alert-bg: var(--tblr-vimeo-bg-subtle);--tblr-alert-border-color: var(--tblr-vimeo-border-subtle);--tblr-alert-link-color: var(--tblr-vimeo-text-emphasis)}.alert-dribbble{--tblr-alert-color: var(--tblr-dribbble-text-emphasis);--tblr-alert-bg: var(--tblr-dribbble-bg-subtle);--tblr-alert-border-color: var(--tblr-dribbble-border-subtle);--tblr-alert-link-color: var(--tblr-dribbble-text-emphasis)}.alert-github{--tblr-alert-color: var(--tblr-github-text-emphasis);--tblr-alert-bg: var(--tblr-github-bg-subtle);--tblr-alert-border-color: var(--tblr-github-border-subtle);--tblr-alert-link-color: var(--tblr-github-text-emphasis)}.alert-instagram{--tblr-alert-color: var(--tblr-instagram-text-emphasis);--tblr-alert-bg: var(--tblr-instagram-bg-subtle);--tblr-alert-border-color: var(--tblr-instagram-border-subtle);--tblr-alert-link-color: var(--tblr-instagram-text-emphasis)}.alert-pinterest{--tblr-alert-color: var(--tblr-pinterest-text-emphasis);--tblr-alert-bg: var(--tblr-pinterest-bg-subtle);--tblr-alert-border-color: var(--tblr-pinterest-border-subtle);--tblr-alert-link-color: var(--tblr-pinterest-text-emphasis)}.alert-vk{--tblr-alert-color: var(--tblr-vk-text-emphasis);--tblr-alert-bg: var(--tblr-vk-bg-subtle);--tblr-alert-border-color: var(--tblr-vk-border-subtle);--tblr-alert-link-color: var(--tblr-vk-text-emphasis)}.alert-rss{--tblr-alert-color: var(--tblr-rss-text-emphasis);--tblr-alert-bg: var(--tblr-rss-bg-subtle);--tblr-alert-border-color: var(--tblr-rss-border-subtle);--tblr-alert-link-color: var(--tblr-rss-text-emphasis)}.alert-flickr{--tblr-alert-color: var(--tblr-flickr-text-emphasis);--tblr-alert-bg: var(--tblr-flickr-bg-subtle);--tblr-alert-border-color: var(--tblr-flickr-border-subtle);--tblr-alert-link-color: var(--tblr-flickr-text-emphasis)}.alert-bitbucket{--tblr-alert-color: var(--tblr-bitbucket-text-emphasis);--tblr-alert-bg: var(--tblr-bitbucket-bg-subtle);--tblr-alert-border-color: var(--tblr-bitbucket-border-subtle);--tblr-alert-link-color: var(--tblr-bitbucket-text-emphasis)}.alert-tabler{--tblr-alert-color: var(--tblr-tabler-text-emphasis);--tblr-alert-bg: var(--tblr-tabler-bg-subtle);--tblr-alert-border-color: var(--tblr-tabler-border-subtle);--tblr-alert-link-color: var(--tblr-tabler-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:.5rem}}.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: 1.25rem;--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-action{width:100%;color:var(--tblr-list-group-action-color);text-align:inherit}.list-group-item-action:hover,.list-group-item-action: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:active{color:var(--tblr-list-group-action-active-color);background-color:var(--tblr-list-group-action-active-bg)}.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-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)}.list-group-item-facebook{--tblr-list-group-color: var(--tblr-facebook-text-emphasis);--tblr-list-group-bg: var(--tblr-facebook-bg-subtle);--tblr-list-group-border-color: var(--tblr-facebook-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-facebook-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-facebook-border-subtle);--tblr-list-group-active-color: var(--tblr-facebook-bg-subtle);--tblr-list-group-active-bg: var(--tblr-facebook-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-facebook-text-emphasis)}.list-group-item-twitter{--tblr-list-group-color: var(--tblr-twitter-text-emphasis);--tblr-list-group-bg: var(--tblr-twitter-bg-subtle);--tblr-list-group-border-color: var(--tblr-twitter-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-twitter-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-twitter-border-subtle);--tblr-list-group-active-color: var(--tblr-twitter-bg-subtle);--tblr-list-group-active-bg: var(--tblr-twitter-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-twitter-text-emphasis)}.list-group-item-linkedin{--tblr-list-group-color: var(--tblr-linkedin-text-emphasis);--tblr-list-group-bg: var(--tblr-linkedin-bg-subtle);--tblr-list-group-border-color: var(--tblr-linkedin-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-linkedin-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-linkedin-border-subtle);--tblr-list-group-active-color: var(--tblr-linkedin-bg-subtle);--tblr-list-group-active-bg: var(--tblr-linkedin-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-linkedin-text-emphasis)}.list-group-item-google{--tblr-list-group-color: var(--tblr-google-text-emphasis);--tblr-list-group-bg: var(--tblr-google-bg-subtle);--tblr-list-group-border-color: var(--tblr-google-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-google-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-google-border-subtle);--tblr-list-group-active-color: var(--tblr-google-bg-subtle);--tblr-list-group-active-bg: var(--tblr-google-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-google-text-emphasis)}.list-group-item-youtube{--tblr-list-group-color: var(--tblr-youtube-text-emphasis);--tblr-list-group-bg: var(--tblr-youtube-bg-subtle);--tblr-list-group-border-color: var(--tblr-youtube-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-youtube-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-youtube-border-subtle);--tblr-list-group-active-color: var(--tblr-youtube-bg-subtle);--tblr-list-group-active-bg: var(--tblr-youtube-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-youtube-text-emphasis)}.list-group-item-vimeo{--tblr-list-group-color: var(--tblr-vimeo-text-emphasis);--tblr-list-group-bg: var(--tblr-vimeo-bg-subtle);--tblr-list-group-border-color: var(--tblr-vimeo-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-vimeo-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-vimeo-border-subtle);--tblr-list-group-active-color: var(--tblr-vimeo-bg-subtle);--tblr-list-group-active-bg: var(--tblr-vimeo-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-vimeo-text-emphasis)}.list-group-item-dribbble{--tblr-list-group-color: var(--tblr-dribbble-text-emphasis);--tblr-list-group-bg: var(--tblr-dribbble-bg-subtle);--tblr-list-group-border-color: var(--tblr-dribbble-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-dribbble-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-dribbble-border-subtle);--tblr-list-group-active-color: var(--tblr-dribbble-bg-subtle);--tblr-list-group-active-bg: var(--tblr-dribbble-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-dribbble-text-emphasis)}.list-group-item-github{--tblr-list-group-color: var(--tblr-github-text-emphasis);--tblr-list-group-bg: var(--tblr-github-bg-subtle);--tblr-list-group-border-color: var(--tblr-github-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-github-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-github-border-subtle);--tblr-list-group-active-color: var(--tblr-github-bg-subtle);--tblr-list-group-active-bg: var(--tblr-github-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-github-text-emphasis)}.list-group-item-instagram{--tblr-list-group-color: var(--tblr-instagram-text-emphasis);--tblr-list-group-bg: var(--tblr-instagram-bg-subtle);--tblr-list-group-border-color: var(--tblr-instagram-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-instagram-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-instagram-border-subtle);--tblr-list-group-active-color: var(--tblr-instagram-bg-subtle);--tblr-list-group-active-bg: var(--tblr-instagram-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-instagram-text-emphasis)}.list-group-item-pinterest{--tblr-list-group-color: var(--tblr-pinterest-text-emphasis);--tblr-list-group-bg: var(--tblr-pinterest-bg-subtle);--tblr-list-group-border-color: var(--tblr-pinterest-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-pinterest-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-pinterest-border-subtle);--tblr-list-group-active-color: var(--tblr-pinterest-bg-subtle);--tblr-list-group-active-bg: var(--tblr-pinterest-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-pinterest-text-emphasis)}.list-group-item-vk{--tblr-list-group-color: var(--tblr-vk-text-emphasis);--tblr-list-group-bg: var(--tblr-vk-bg-subtle);--tblr-list-group-border-color: var(--tblr-vk-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-vk-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-vk-border-subtle);--tblr-list-group-active-color: var(--tblr-vk-bg-subtle);--tblr-list-group-active-bg: var(--tblr-vk-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-vk-text-emphasis)}.list-group-item-rss{--tblr-list-group-color: var(--tblr-rss-text-emphasis);--tblr-list-group-bg: var(--tblr-rss-bg-subtle);--tblr-list-group-border-color: var(--tblr-rss-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-rss-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-rss-border-subtle);--tblr-list-group-active-color: var(--tblr-rss-bg-subtle);--tblr-list-group-active-bg: var(--tblr-rss-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-rss-text-emphasis)}.list-group-item-flickr{--tblr-list-group-color: var(--tblr-flickr-text-emphasis);--tblr-list-group-bg: var(--tblr-flickr-bg-subtle);--tblr-list-group-border-color: var(--tblr-flickr-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-flickr-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-flickr-border-subtle);--tblr-list-group-active-color: var(--tblr-flickr-bg-subtle);--tblr-list-group-active-bg: var(--tblr-flickr-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-flickr-text-emphasis)}.list-group-item-bitbucket{--tblr-list-group-color: var(--tblr-bitbucket-text-emphasis);--tblr-list-group-bg: var(--tblr-bitbucket-bg-subtle);--tblr-list-group-border-color: var(--tblr-bitbucket-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-bitbucket-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-bitbucket-border-subtle);--tblr-list-group-active-color: var(--tblr-bitbucket-bg-subtle);--tblr-list-group-active-bg: var(--tblr-bitbucket-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-bitbucket-text-emphasis)}.list-group-item-tabler{--tblr-list-group-color: var(--tblr-tabler-text-emphasis);--tblr-list-group-bg: var(--tblr-tabler-bg-subtle);--tblr-list-group-border-color: var(--tblr-tabler-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-tabler-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-tabler-border-subtle);--tblr-list-group-active-color: var(--tblr-tabler-bg-subtle);--tblr-list-group-active-bg: var(--tblr-tabler-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-tabler-text-emphasis)}.btn-close{--tblr-btn-close-color: #182433;--tblr-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23182433'%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.414z'/%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-white-filter: invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em;color:var(--tblr-btn-close-color);background:transparent var(--tblr-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:4px;opacity:var(--tblr-btn-close-opacity)}.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)}.btn-close-white,[data-bs-theme=dark] .btn-close,body[data-bs-theme=dark] [data-bs-theme=light] .btn-close{filter:var(--tblr-btn-close-white-filter)}.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: rgba(var(--tblr-body-bg-rgb), .85);--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-secondary);--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: ;--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{transition:transform .3s ease-out;transform:translateY(-1rem)}@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: #182433;--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:calc(-.5 * var(--tblr-modal-header-padding-y)) calc(-.5 * var(--tblr-modal-header-padding-x)) calc(-.5 * var(--tblr-modal-header-padding-y)) 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-2);--tblr-tooltip-padding-y: var(--tblr-spacer-2);--tblr-tooltip-margin: ;--tblr-tooltip-font-size: .765625rem;--tblr-tooltip-color: var(--tblr-light);--tblr-tooltip-bg: var(--tblr-bg-surface-dark);--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-box-shadow);--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: 1rem;--tblr-popover-body-padding-y: 1rem;--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;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:#fff;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:#fff;text-align:center}.carousel-dark .carousel-control-prev-icon,.carousel-dark .carousel-control-next-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon{filter:invert(1) grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-grow,.spinner-border{display:inline-block;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:#182433}.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:calc(-.5 * var(--tblr-offcanvas-padding-y)) calc(-.5 * var(--tblr-offcanvas-padding-x)) calc(-.5 * var(--tblr-offcanvas-padding-y)) 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,#000000 55%,rgba(0,0,0,.9) 75%,#000000 95%);mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{to{mask-position:-200% 0%}}.clearfix:after{display:block;clear:both;content:""}.text-bg-primary{color:#fcfdfe!important;background-color:RGBA(var(--tblr-primary-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-secondary,.text-bg-gray{color:#fcfdfe!important;background-color:RGBA(var(--tblr-secondary-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-success{color:#fcfdfe!important;background-color:RGBA(var(--tblr-success-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-info{color:#fcfdfe!important;background-color:RGBA(var(--tblr-info-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-warning{color:#fcfdfe!important;background-color:RGBA(var(--tblr-warning-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-danger{color:#fcfdfe!important;background-color:RGBA(var(--tblr-danger-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-light,.text-bg-white{color:#182433!important;background-color:RGBA(var(--tblr-light-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-dark,.text-bg-black{color:#fcfdfe!important;background-color:RGBA(var(--tblr-dark-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-muted{color:#fcfdfe!important;background-color:RGBA(var(--tblr-muted-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-blue{color:#fcfdfe!important;background-color:RGBA(var(--tblr-blue-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-azure{color:#fcfdfe!important;background-color:RGBA(var(--tblr-azure-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-indigo{color:#fcfdfe!important;background-color:RGBA(var(--tblr-indigo-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-purple{color:#fcfdfe!important;background-color:RGBA(var(--tblr-purple-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-pink{color:#fcfdfe!important;background-color:RGBA(var(--tblr-pink-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-red{color:#fcfdfe!important;background-color:RGBA(var(--tblr-red-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-orange{color:#fcfdfe!important;background-color:RGBA(var(--tblr-orange-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-yellow{color:#fcfdfe!important;background-color:RGBA(var(--tblr-yellow-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-lime{color:#fcfdfe!important;background-color:RGBA(var(--tblr-lime-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-green{color:#fcfdfe!important;background-color:RGBA(var(--tblr-green-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-teal{color:#fcfdfe!important;background-color:RGBA(var(--tblr-teal-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-cyan{color:#fcfdfe!important;background-color:RGBA(var(--tblr-cyan-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-facebook{color:#fcfdfe!important;background-color:RGBA(var(--tblr-facebook-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-twitter{color:#fcfdfe!important;background-color:RGBA(var(--tblr-twitter-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-linkedin{color:#fcfdfe!important;background-color:RGBA(var(--tblr-linkedin-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-google{color:#fcfdfe!important;background-color:RGBA(var(--tblr-google-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-youtube{color:#fcfdfe!important;background-color:RGBA(var(--tblr-youtube-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-vimeo{color:#fcfdfe!important;background-color:RGBA(var(--tblr-vimeo-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-dribbble{color:#fcfdfe!important;background-color:RGBA(var(--tblr-dribbble-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-github{color:#fcfdfe!important;background-color:RGBA(var(--tblr-github-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-instagram{color:#fcfdfe!important;background-color:RGBA(var(--tblr-instagram-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-pinterest{color:#fcfdfe!important;background-color:RGBA(var(--tblr-pinterest-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-vk{color:#fcfdfe!important;background-color:RGBA(var(--tblr-vk-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-rss{color:#fcfdfe!important;background-color:RGBA(var(--tblr-rss-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-flickr{color:#fcfdfe!important;background-color:RGBA(var(--tblr-flickr-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-bitbucket{color:#fcfdfe!important;background-color:RGBA(var(--tblr-bitbucket-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-tabler{color:#fcfdfe!important;background-color:RGBA(var(--tblr-tabler-rgb),var(--tblr-bg-opacity, 1))!important}.link-primary{color:RGBA(var(--tblr-primary-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-primary-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-secondary{color:RGBA(var(--tblr-secondary-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-secondary-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-secondary:hover,.link-secondary:focus{color:RGBA(82,92,104,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(82,92,104,var(--tblr-link-underline-opacity, 1))!important}.link-success{color:RGBA(var(--tblr-success-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-success-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-info{color:RGBA(var(--tblr-info-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-info-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-warning{color:RGBA(var(--tblr-warning-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-warning-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-warning:hover,.link-warning: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}.link-danger{color:RGBA(var(--tblr-danger-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-danger-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-light{color:RGBA(var(--tblr-light-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-light-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-light:hover,.link-light:focus{color:RGBA(253,253,254,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(253,253,254,var(--tblr-link-underline-opacity, 1))!important}.link-dark{color:RGBA(var(--tblr-dark-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-dark-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-dark:hover,.link-dark:focus{color:RGBA(19,29,41,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(19,29,41,var(--tblr-link-underline-opacity, 1))!important}.link-muted{color:RGBA(var(--tblr-muted-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-muted-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-muted:hover,.link-muted:focus{color:RGBA(82,92,104,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(82,92,104,var(--tblr-link-underline-opacity, 1))!important}.link-blue{color:RGBA(var(--tblr-blue-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-blue-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-blue:hover,.link-blue:focus{color:RGBA(0,67,133,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(0,67,133,var(--tblr-link-underline-opacity, 1))!important}.link-azure{color:RGBA(var(--tblr-azure-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-azure-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-indigo{color:RGBA(var(--tblr-indigo-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-indigo-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-purple{color:RGBA(var(--tblr-purple-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-purple-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-pink{color:RGBA(var(--tblr-pink-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-pink-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-red{color:RGBA(var(--tblr-red-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-red-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-orange{color:RGBA(var(--tblr-orange-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-orange-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-yellow{color:RGBA(var(--tblr-yellow-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-yellow-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-lime{color:RGBA(var(--tblr-lime-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-lime-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-green{color:RGBA(var(--tblr-green-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-green-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-teal{color:RGBA(var(--tblr-teal-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-teal-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-cyan{color:RGBA(var(--tblr-cyan-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-cyan-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-facebook{color:RGBA(var(--tblr-facebook-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-facebook-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-facebook:hover,.link-facebook:focus{color:RGBA(19,95,194,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(19,95,194,var(--tblr-link-underline-opacity, 1))!important}.link-twitter{color:RGBA(var(--tblr-twitter-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-twitter-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-twitter:hover,.link-twitter:focus{color:RGBA(23,129,194,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(23,129,194,var(--tblr-link-underline-opacity, 1))!important}.link-linkedin{color:RGBA(var(--tblr-linkedin-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-linkedin-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-linkedin:hover,.link-linkedin:focus{color:RGBA(8,82,155,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(8,82,155,var(--tblr-link-underline-opacity, 1))!important}.link-google{color:RGBA(var(--tblr-google-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-google-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-google:hover,.link-google:focus{color:RGBA(176,62,52,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(176,62,52,var(--tblr-link-underline-opacity, 1))!important}.link-youtube{color:RGBA(var(--tblr-youtube-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-youtube-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-youtube:hover,.link-youtube:focus{color:RGBA(204,0,0,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(204,0,0,var(--tblr-link-underline-opacity, 1))!important}.link-vimeo{color:RGBA(var(--tblr-vimeo-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-vimeo-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-vimeo:hover,.link-vimeo:focus{color:RGBA(21,146,187,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(21,146,187,var(--tblr-link-underline-opacity, 1))!important}.link-dribbble{color:RGBA(var(--tblr-dribbble-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-dribbble-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-dribbble:hover,.link-dribbble:focus{color:RGBA(187,61,110,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(187,61,110,var(--tblr-link-underline-opacity, 1))!important}.link-github{color:RGBA(var(--tblr-github-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-github-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-github:hover,.link-github:focus{color:RGBA(19,18,18,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(19,18,18,var(--tblr-link-underline-opacity, 1))!important}.link-instagram{color:RGBA(var(--tblr-instagram-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-instagram-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-instagram:hover,.link-instagram:focus{color:RGBA(182,51,76,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(182,51,76,var(--tblr-link-underline-opacity, 1))!important}.link-pinterest{color:RGBA(var(--tblr-pinterest-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-pinterest-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-pinterest:hover,.link-pinterest:focus{color:RGBA(151,6,22,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(151,6,22,var(--tblr-link-underline-opacity, 1))!important}.link-vk{color:RGBA(var(--tblr-vk-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-vk-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-vk:hover,.link-vk:focus{color:RGBA(79,105,134,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(79,105,134,var(--tblr-link-underline-opacity, 1))!important}.link-rss{color:RGBA(var(--tblr-rss-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-rss-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-rss:hover,.link-rss:focus{color:RGBA(204,132,0,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(204,132,0,var(--tblr-link-underline-opacity, 1))!important}.link-flickr{color:RGBA(var(--tblr-flickr-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-flickr-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-flickr:hover,.link-flickr:focus{color:RGBA(0,79,176,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(0,79,176,var(--tblr-link-underline-opacity, 1))!important}.link-bitbucket{color:RGBA(var(--tblr-bitbucket-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-bitbucket-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-bitbucket:hover,.link-bitbucket:focus{color:RGBA(0,66,163,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(0,66,163,var(--tblr-link-underline-opacity, 1))!important}.link-tabler{color:RGBA(var(--tblr-tabler-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-tabler-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-tabler:hover,.link-tabler:focus{color:RGBA(0,67,133,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(0,67,133,var(--tblr-link-underline-opacity, 1))!important}.link-body-emphasis{color:RGBA(var(--tblr-emphasis-color-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-emphasis-color-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-body-emphasis:hover,.link-body-emphasis:focus{color:RGBA(var(--tblr-emphasis-color-rgb),var(--tblr-link-opacity, .75))!important;text-decoration-color:RGBA(var(--tblr-emphasis-color-rgb),var(--tblr-link-underline-opacity, .75))!important}.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)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;text-decoration-color:rgba(var(--tblr-link-color-rgb),var(--tblr-link-opacity, .5));text-underline-offset:.25em;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media (prefers-reduced-motion: reduce){.icon-link>.bi{transition:none}}.icon-link-hover:hover>.bi,.icon-link-hover:focus-visible>.bi{transform:var(--tblr-icon-link-transform, translate3d(.25em, 0, 0))}.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-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%}.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}}.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}.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}.stretched-link:after{position:absolute;inset:0;z-index:1;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}.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}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!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))}.focus-ring-facebook{--tblr-focus-ring-color: rgba(var(--tblr-facebook-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-twitter{--tblr-focus-ring-color: rgba(var(--tblr-twitter-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-linkedin{--tblr-focus-ring-color: rgba(var(--tblr-linkedin-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-google{--tblr-focus-ring-color: rgba(var(--tblr-google-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-youtube{--tblr-focus-ring-color: rgba(var(--tblr-youtube-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-vimeo{--tblr-focus-ring-color: rgba(var(--tblr-vimeo-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-dribbble{--tblr-focus-ring-color: rgba(var(--tblr-dribbble-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-github{--tblr-focus-ring-color: rgba(var(--tblr-github-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-instagram{--tblr-focus-ring-color: rgba(var(--tblr-instagram-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-pinterest{--tblr-focus-ring-color: rgba(var(--tblr-pinterest-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-vk{--tblr-focus-ring-color: rgba(var(--tblr-vk-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-rss{--tblr-focus-ring-color: rgba(var(--tblr-rss-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-flickr{--tblr-focus-ring-color: rgba(var(--tblr-flickr-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-bitbucket{--tblr-focus-ring-color: rgba(var(--tblr-bitbucket-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-tabler{--tblr-focus-ring-color: rgba(var(--tblr-tabler-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) rgba(4,32,69,.14)!important}.border-wide{border:2px var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-0{border:0!important}.border-top{border-top:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-top-wide{border-top:2px var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-end-wide{border-right:2px var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-bottom-wide{border-bottom:2px var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-start-wide{border-left:2px var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-start-0{border-left:0!important}.border-primary{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-primary-rgb),var(--tblr-border-opacity))!important}.border-secondary{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-secondary-rgb),var(--tblr-border-opacity))!important}.border-success{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-success-rgb),var(--tblr-border-opacity))!important}.border-info{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-info-rgb),var(--tblr-border-opacity))!important}.border-warning{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-warning-rgb),var(--tblr-border-opacity))!important}.border-danger{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-danger-rgb),var(--tblr-border-opacity))!important}.border-light{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-light-rgb),var(--tblr-border-opacity))!important}.border-dark{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-dark-rgb),var(--tblr-border-opacity))!important}.border-muted{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-muted-rgb),var(--tblr-border-opacity))!important}.border-blue{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-blue-rgb),var(--tblr-border-opacity))!important}.border-azure{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-azure-rgb),var(--tblr-border-opacity))!important}.border-indigo{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-indigo-rgb),var(--tblr-border-opacity))!important}.border-purple{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-purple-rgb),var(--tblr-border-opacity))!important}.border-pink{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-pink-rgb),var(--tblr-border-opacity))!important}.border-red{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-red-rgb),var(--tblr-border-opacity))!important}.border-orange{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-orange-rgb),var(--tblr-border-opacity))!important}.border-yellow{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-yellow-rgb),var(--tblr-border-opacity))!important}.border-lime{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-lime-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-teal{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-teal-rgb),var(--tblr-border-opacity))!important}.border-cyan{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-cyan-rgb),var(--tblr-border-opacity))!important}.border-facebook{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-facebook-rgb),var(--tblr-border-opacity))!important}.border-twitter{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-twitter-rgb),var(--tblr-border-opacity))!important}.border-linkedin{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-linkedin-rgb),var(--tblr-border-opacity))!important}.border-google{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-google-rgb),var(--tblr-border-opacity))!important}.border-youtube{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-youtube-rgb),var(--tblr-border-opacity))!important}.border-vimeo{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-vimeo-rgb),var(--tblr-border-opacity))!important}.border-dribbble{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-dribbble-rgb),var(--tblr-border-opacity))!important}.border-github{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-github-rgb),var(--tblr-border-opacity))!important}.border-instagram{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-instagram-rgb),var(--tblr-border-opacity))!important}.border-pinterest{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-pinterest-rgb),var(--tblr-border-opacity))!important}.border-vk{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-vk-rgb),var(--tblr-border-opacity))!important}.border-rss{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-rss-rgb),var(--tblr-border-opacity))!important}.border-flickr{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-flickr-rgb),var(--tblr-border-opacity))!important}.border-bitbucket{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-border-opacity))!important}.border-tabler{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-tabler-rgb),var(--tblr-border-opacity))!important}.border-black{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-black-rgb),var(--tblr-border-opacity))!important}.border-white{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-white-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-0{width:0!important}.w-1{width:.25rem!important}.w-2{width:.5rem!important}.w-3{width:1rem!important}.w-4{width:1.5rem!important}.w-5{width:2rem!important}.w-6{width:3rem!important}.w-7{width:5rem!important}.w-8{width:8rem!important}.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}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-0{height:0!important}.h-1{height:.25rem!important}.h-2{height:.5rem!important}.h-3{height:1rem!important}.h-4{height:1.5rem!important}.h-5{height:2rem!important}.h-6{height:3rem!important}.h-7{height:5rem!important}.h-8{height:8rem!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}.h-auto{height:auto!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:3rem!important}.m-7{margin:5rem!important}.m-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-7{margin-right:5rem!important;margin-left:5rem!important}.mx-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-7{margin-top:5rem!important}.mt-8{margin-top:8rem!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:3rem!important}.me-7{margin-right:5rem!important}.me-8{margin-right:8rem!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:3rem!important}.mb-7{margin-bottom:5rem!important}.mb-8{margin-bottom:8rem!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:3rem!important}.ms-7{margin-left:5rem!important}.ms-8{margin-left:8rem!important}.ms-auto{margin-left:auto!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:3rem!important}.p-7{padding:5rem!important}.p-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-7{padding-right:5rem!important;padding-left:5rem!important}.px-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-7{padding-top:5rem!important}.pt-8{padding-top:8rem!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:3rem!important}.pe-7{padding-right:5rem!important}.pe-8{padding-right:8rem!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:3rem!important}.pb-7{padding-bottom:5rem!important}.pb-8{padding-bottom:8rem!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:3rem!important}.ps-7{padding-left:5rem!important}.ps-8{padding-left:8rem!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:3rem!important}.gap-7{gap:5rem!important}.gap-8{gap:8rem!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:3rem!important}.row-gap-7{row-gap:5rem!important}.row-gap-8{row-gap:8rem!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:3rem!important}.column-gap-7{column-gap:5rem!important}.column-gap-8{column-gap:8rem!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-facebook{--tblr-text-opacity: 1;color:rgba(var(--tblr-facebook-rgb),var(--tblr-text-opacity))!important}.text-twitter{--tblr-text-opacity: 1;color:rgba(var(--tblr-twitter-rgb),var(--tblr-text-opacity))!important}.text-linkedin{--tblr-text-opacity: 1;color:rgba(var(--tblr-linkedin-rgb),var(--tblr-text-opacity))!important}.text-google{--tblr-text-opacity: 1;color:rgba(var(--tblr-google-rgb),var(--tblr-text-opacity))!important}.text-youtube{--tblr-text-opacity: 1;color:rgba(var(--tblr-youtube-rgb),var(--tblr-text-opacity))!important}.text-vimeo{--tblr-text-opacity: 1;color:rgba(var(--tblr-vimeo-rgb),var(--tblr-text-opacity))!important}.text-dribbble{--tblr-text-opacity: 1;color:rgba(var(--tblr-dribbble-rgb),var(--tblr-text-opacity))!important}.text-github{--tblr-text-opacity: 1;color:rgba(var(--tblr-github-rgb),var(--tblr-text-opacity))!important}.text-instagram{--tblr-text-opacity: 1;color:rgba(var(--tblr-instagram-rgb),var(--tblr-text-opacity))!important}.text-pinterest{--tblr-text-opacity: 1;color:rgba(var(--tblr-pinterest-rgb),var(--tblr-text-opacity))!important}.text-vk{--tblr-text-opacity: 1;color:rgba(var(--tblr-vk-rgb),var(--tblr-text-opacity))!important}.text-rss{--tblr-text-opacity: 1;color:rgba(var(--tblr-rss-rgb),var(--tblr-text-opacity))!important}.text-flickr{--tblr-text-opacity: 1;color:rgba(var(--tblr-flickr-rgb),var(--tblr-text-opacity))!important}.text-bitbucket{--tblr-text-opacity: 1;color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-text-opacity))!important}.text-tabler{--tblr-text-opacity: 1;color:rgba(var(--tblr-tabler-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-facebook{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-facebook-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-twitter{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-twitter-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-linkedin{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-linkedin-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-google{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-google-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-youtube{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-youtube-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-vimeo{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-vimeo-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-dribbble{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-dribbble-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-github{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-github-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-instagram{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-instagram-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-pinterest{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-pinterest-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-vk{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-vk-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-rss{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-rss-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-flickr{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-flickr-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-bitbucket{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-tabler{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-tabler-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-facebook{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-facebook-rgb),var(--tblr-bg-opacity))!important}.bg-twitter{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-twitter-rgb),var(--tblr-bg-opacity))!important}.bg-linkedin{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-linkedin-rgb),var(--tblr-bg-opacity))!important}.bg-google{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-google-rgb),var(--tblr-bg-opacity))!important}.bg-youtube{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-youtube-rgb),var(--tblr-bg-opacity))!important}.bg-vimeo{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-vimeo-rgb),var(--tblr-bg-opacity))!important}.bg-dribbble{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-dribbble-rgb),var(--tblr-bg-opacity))!important}.bg-github{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-github-rgb),var(--tblr-bg-opacity))!important}.bg-instagram{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-instagram-rgb),var(--tblr-bg-opacity))!important}.bg-pinterest{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-pinterest-rgb),var(--tblr-bg-opacity))!important}.bg-vk{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-vk-rgb),var(--tblr-bg-opacity))!important}.bg-rss{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-rss-rgb),var(--tblr-bg-opacity))!important}.bg-flickr{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-flickr-rgb),var(--tblr-bg-opacity))!important}.bg-bitbucket{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-bg-opacity))!important}.bg-tabler{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-tabler-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}.tracking-tight{letter-spacing:-.05em!important}.tracking-normal{letter-spacing:0!important}.tracking-wide{letter-spacing:.05em!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}.border-x{border-left:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.14)!important;border-right:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-x-wide{border-left:2px var(--tblr-border-style) rgba(4,32,69,.14)!important;border-right:2px var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-x-0{border-left:0!important;border-right:0!important}.border-y{border-top:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.14)!important;border-bottom:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.14)!important}.border-y-wide{border-top:2px var(--tblr-border-style) rgba(4,32,69,.14)!important;border-bottom:2px var(--tblr-border-style) rgba(4,32,69,.14)!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}@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:3rem!important}.m-sm-7{margin:5rem!important}.m-sm-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-sm-7{margin-right:5rem!important;margin-left:5rem!important}.mx-sm-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-sm-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-sm-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-sm-7{margin-top:5rem!important}.mt-sm-8{margin-top:8rem!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:3rem!important}.me-sm-7{margin-right:5rem!important}.me-sm-8{margin-right:8rem!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:3rem!important}.mb-sm-7{margin-bottom:5rem!important}.mb-sm-8{margin-bottom:8rem!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:3rem!important}.ms-sm-7{margin-left:5rem!important}.ms-sm-8{margin-left:8rem!important}.ms-sm-auto{margin-left:auto!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:3rem!important}.p-sm-7{padding:5rem!important}.p-sm-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-sm-7{padding-right:5rem!important;padding-left:5rem!important}.px-sm-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-sm-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-sm-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-sm-7{padding-top:5rem!important}.pt-sm-8{padding-top:8rem!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:3rem!important}.pe-sm-7{padding-right:5rem!important}.pe-sm-8{padding-right:8rem!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:3rem!important}.pb-sm-7{padding-bottom:5rem!important}.pb-sm-8{padding-bottom:8rem!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:3rem!important}.ps-sm-7{padding-left:5rem!important}.ps-sm-8{padding-left:8rem!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:3rem!important}.gap-sm-7{gap:5rem!important}.gap-sm-8{gap:8rem!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:3rem!important}.row-gap-sm-7{row-gap:5rem!important}.row-gap-sm-8{row-gap:8rem!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:3rem!important}.column-gap-sm-7{column-gap:5rem!important}.column-gap-sm-8{column-gap:8rem!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:3rem!important}.m-md-7{margin:5rem!important}.m-md-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-md-7{margin-right:5rem!important;margin-left:5rem!important}.mx-md-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-md-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-md-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-md-7{margin-top:5rem!important}.mt-md-8{margin-top:8rem!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:3rem!important}.me-md-7{margin-right:5rem!important}.me-md-8{margin-right:8rem!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:3rem!important}.mb-md-7{margin-bottom:5rem!important}.mb-md-8{margin-bottom:8rem!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:3rem!important}.ms-md-7{margin-left:5rem!important}.ms-md-8{margin-left:8rem!important}.ms-md-auto{margin-left:auto!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:3rem!important}.p-md-7{padding:5rem!important}.p-md-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-md-7{padding-right:5rem!important;padding-left:5rem!important}.px-md-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-md-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-md-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-md-7{padding-top:5rem!important}.pt-md-8{padding-top:8rem!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:3rem!important}.pe-md-7{padding-right:5rem!important}.pe-md-8{padding-right:8rem!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:3rem!important}.pb-md-7{padding-bottom:5rem!important}.pb-md-8{padding-bottom:8rem!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:3rem!important}.ps-md-7{padding-left:5rem!important}.ps-md-8{padding-left:8rem!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:3rem!important}.gap-md-7{gap:5rem!important}.gap-md-8{gap:8rem!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:3rem!important}.row-gap-md-7{row-gap:5rem!important}.row-gap-md-8{row-gap:8rem!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:3rem!important}.column-gap-md-7{column-gap:5rem!important}.column-gap-md-8{column-gap:8rem!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:3rem!important}.m-lg-7{margin:5rem!important}.m-lg-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-lg-7{margin-right:5rem!important;margin-left:5rem!important}.mx-lg-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-lg-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-lg-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-lg-7{margin-top:5rem!important}.mt-lg-8{margin-top:8rem!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:3rem!important}.me-lg-7{margin-right:5rem!important}.me-lg-8{margin-right:8rem!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:3rem!important}.mb-lg-7{margin-bottom:5rem!important}.mb-lg-8{margin-bottom:8rem!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:3rem!important}.ms-lg-7{margin-left:5rem!important}.ms-lg-8{margin-left:8rem!important}.ms-lg-auto{margin-left:auto!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:3rem!important}.p-lg-7{padding:5rem!important}.p-lg-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-lg-7{padding-right:5rem!important;padding-left:5rem!important}.px-lg-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-lg-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-lg-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-lg-7{padding-top:5rem!important}.pt-lg-8{padding-top:8rem!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:3rem!important}.pe-lg-7{padding-right:5rem!important}.pe-lg-8{padding-right:8rem!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:3rem!important}.pb-lg-7{padding-bottom:5rem!important}.pb-lg-8{padding-bottom:8rem!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:3rem!important}.ps-lg-7{padding-left:5rem!important}.ps-lg-8{padding-left:8rem!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:3rem!important}.gap-lg-7{gap:5rem!important}.gap-lg-8{gap:8rem!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:3rem!important}.row-gap-lg-7{row-gap:5rem!important}.row-gap-lg-8{row-gap:8rem!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:3rem!important}.column-gap-lg-7{column-gap:5rem!important}.column-gap-lg-8{column-gap:8rem!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:3rem!important}.m-xl-7{margin:5rem!important}.m-xl-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-xl-7{margin-right:5rem!important;margin-left:5rem!important}.mx-xl-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-xl-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-xl-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-xl-7{margin-top:5rem!important}.mt-xl-8{margin-top:8rem!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:3rem!important}.me-xl-7{margin-right:5rem!important}.me-xl-8{margin-right:8rem!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:3rem!important}.mb-xl-7{margin-bottom:5rem!important}.mb-xl-8{margin-bottom:8rem!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:3rem!important}.ms-xl-7{margin-left:5rem!important}.ms-xl-8{margin-left:8rem!important}.ms-xl-auto{margin-left:auto!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:3rem!important}.p-xl-7{padding:5rem!important}.p-xl-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-xl-7{padding-right:5rem!important;padding-left:5rem!important}.px-xl-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-xl-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-xl-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-xl-7{padding-top:5rem!important}.pt-xl-8{padding-top:8rem!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:3rem!important}.pe-xl-7{padding-right:5rem!important}.pe-xl-8{padding-right:8rem!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:3rem!important}.pb-xl-7{padding-bottom:5rem!important}.pb-xl-8{padding-bottom:8rem!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:3rem!important}.ps-xl-7{padding-left:5rem!important}.ps-xl-8{padding-left:8rem!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:3rem!important}.gap-xl-7{gap:5rem!important}.gap-xl-8{gap:8rem!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:3rem!important}.row-gap-xl-7{row-gap:5rem!important}.row-gap-xl-8{row-gap:8rem!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:3rem!important}.column-gap-xl-7{column-gap:5rem!important}.column-gap-xl-8{column-gap:8rem!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:3rem!important}.m-xxl-7{margin:5rem!important}.m-xxl-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-xxl-7{margin-right:5rem!important;margin-left:5rem!important}.mx-xxl-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-xxl-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-xxl-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-xxl-7{margin-top:5rem!important}.mt-xxl-8{margin-top:8rem!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:3rem!important}.me-xxl-7{margin-right:5rem!important}.me-xxl-8{margin-right:8rem!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:3rem!important}.mb-xxl-7{margin-bottom:5rem!important}.mb-xxl-8{margin-bottom:8rem!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:3rem!important}.ms-xxl-7{margin-left:5rem!important}.ms-xxl-8{margin-left:8rem!important}.ms-xxl-auto{margin-left:auto!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:3rem!important}.p-xxl-7{padding:5rem!important}.p-xxl-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-xxl-7{padding-right:5rem!important;padding-left:5rem!important}.px-xxl-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-xxl-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-xxl-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-xxl-7{padding-top:5rem!important}.pt-xxl-8{padding-top:8rem!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:3rem!important}.pe-xxl-7{padding-right:5rem!important}.pe-xxl-8{padding-right:8rem!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:3rem!important}.pb-xxl-7{padding-bottom:5rem!important}.pb-xxl-8{padding-bottom:8rem!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:3rem!important}.ps-xxl-7{padding-left:5rem!important}.ps-xxl-8{padding-left:8rem!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:3rem!important}.gap-xxl-7{gap:5rem!important}.gap-xxl-8{gap:8rem!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:3rem!important}.row-gap-xxl-7{row-gap:5rem!important}.row-gap-xxl-8{row-gap:8rem!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:3rem!important}.column-gap-xxl-7{column-gap:5rem!important}.column-gap-xxl-8{column-gap:8rem!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{font-size:16px;height:100%}:root,:host,[data-bs-theme=light]{--tblr-primary: #00857D;--tblr-primary-rgb: 0, 133, 125;--tblr-primary-fg: var(--tblr-light);--tblr-primary-darken: #007871;--tblr-primary-lt: #e6f3f2;--tblr-primary-lt-rgb: 230, 243, 242;--tblr-secondary: #667382;--tblr-secondary-rgb: 102, 115, 130;--tblr-secondary-fg: var(--tblr-light);--tblr-secondary-darken: #5c6875;--tblr-secondary-lt: #f0f1f3;--tblr-secondary-lt-rgb: 240, 241, 243;--tblr-success: #2fb344;--tblr-success-rgb: 47, 179, 68;--tblr-success-fg: var(--tblr-light);--tblr-success-darken: #2aa13d;--tblr-success-lt: #eaf7ec;--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: #3b8acb;--tblr-info-lt: #ecf5fc;--tblr-info-lt-rgb: 236, 245, 252;--tblr-warning: #f76707;--tblr-warning-rgb: 247, 103, 7;--tblr-warning-fg: var(--tblr-light);--tblr-warning-darken: #de5d06;--tblr-warning-lt: #fef0e6;--tblr-warning-lt-rgb: 254, 240, 230;--tblr-danger: #d63939;--tblr-danger-rgb: 214, 57, 57;--tblr-danger-fg: var(--tblr-light);--tblr-danger-darken: #c13333;--tblr-danger-lt: #fbebeb;--tblr-danger-lt-rgb: 251, 235, 235;--tblr-light: #fcfdfe;--tblr-light-rgb: 252, 253, 254;--tblr-light-fg: var(--tblr-dark);--tblr-light-darken: #e3e4e5;--tblr-light-lt: white;--tblr-light-lt-rgb: 255, 255, 255;--tblr-dark: #182433;--tblr-dark-rgb: 24, 36, 51;--tblr-dark-fg: var(--tblr-light);--tblr-dark-darken: #16202e;--tblr-dark-lt: #e8e9eb;--tblr-dark-lt-rgb: 232, 233, 235;--tblr-muted: #667382;--tblr-muted-rgb: 102, 115, 130;--tblr-muted-fg: var(--tblr-light);--tblr-muted-darken: #5c6875;--tblr-muted-lt: #f0f1f3;--tblr-muted-lt-rgb: 240, 241, 243;--tblr-blue: #0054a6;--tblr-blue-rgb: 0, 84, 166;--tblr-blue-fg: var(--tblr-light);--tblr-blue-darken: #004c95;--tblr-blue-lt: #e6eef6;--tblr-blue-lt-rgb: 230, 238, 246;--tblr-azure: #4299e1;--tblr-azure-rgb: 66, 153, 225;--tblr-azure-fg: var(--tblr-light);--tblr-azure-darken: #3b8acb;--tblr-azure-lt: #ecf5fc;--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: #3b59d4;--tblr-indigo-lt: #eceffd;--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: #9d38b5;--tblr-purple-lt: #f7ecfa;--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: #c12e61;--tblr-pink-lt: #fbebf0;--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: #c13333;--tblr-red-lt: #fbebeb;--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: #de5d06;--tblr-orange-lt: #fef0e6;--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: #dd8f00;--tblr-yellow-lt: #fef5e6;--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: #68a614;--tblr-lime-lt: #f1f8e8;--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: #2aa13d;--tblr-green-lt: #eaf7ec;--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: #0b956c;--tblr-teal-lt: #e7f6f2;--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: #1592a6;--tblr-cyan-lt: #e8f6f8;--tblr-cyan-lt-rgb: 232, 246, 248;--tblr-facebook: #1877f2;--tblr-facebook-rgb: 24, 119, 242;--tblr-facebook-fg: var(--tblr-light);--tblr-facebook-darken: #166bda;--tblr-facebook-lt: #e8f1fe;--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: #1a91da;--tblr-twitter-lt: #e8f6fe;--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: #095caf;--tblr-linkedin-lt: #e7f0f9;--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: #c6463b;--tblr-google-lt: #fcedec;--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: #e60000;--tblr-youtube-lt: #ffe6e6;--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: #17a5d3;--tblr-vimeo-lt: #e8f8fd;--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: #d3447b;--tblr-dribbble-lt: #fdedf3;--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: #161515;--tblr-github-lt: #e8e8e8;--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: #cd3a56;--tblr-instagram-lt: #fcecef;--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: #aa0719;--tblr-pinterest-lt: #f8e6e8;--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: #597697;--tblr-vk-lt: #eff3f6;--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: #e69500;--tblr-rss-lt: #fff6e6;--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: #0059c6;--tblr-flickr-lt: #e6effc;--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: #004ab8;--tblr-bitbucket-lt: #e6eefa;--tblr-bitbucket-lt-rgb: 230, 238, 250;--tblr-tabler: #0054a6;--tblr-tabler-rgb: 0, 84, 166;--tblr-tabler-fg: var(--tblr-light);--tblr-tabler-darken: #004c95;--tblr-tabler-lt: #e6eef6;--tblr-tabler-lt-rgb: 230, 238, 246;--tblr-gray-50: #fcfdfe;--tblr-gray-50-rgb: 252, 253, 254;--tblr-gray-50-fg: var(--tblr-dark);--tblr-gray-50-darken: #e3e4e5;--tblr-gray-50-lt: white;--tblr-gray-50-lt-rgb: 255, 255, 255;--tblr-gray-100: #f6f8fb;--tblr-gray-100-rgb: 246, 248, 251;--tblr-gray-100-fg: var(--tblr-dark);--tblr-gray-100-darken: #dddfe2;--tblr-gray-100-lt: #fefeff;--tblr-gray-100-lt-rgb: 254, 254, 255;--tblr-gray-200: #eef1f4;--tblr-gray-200-rgb: 238, 241, 244;--tblr-gray-200-fg: var(--tblr-dark);--tblr-gray-200-darken: #d6d9dc;--tblr-gray-200-lt: #fdfefe;--tblr-gray-200-lt-rgb: 253, 254, 254;--tblr-gray-300: #dadfe5;--tblr-gray-300-rgb: 218, 223, 229;--tblr-gray-300-fg: var(--tblr-dark);--tblr-gray-300-darken: #c4c9ce;--tblr-gray-300-lt: #fbfcfc;--tblr-gray-300-lt-rgb: 251, 252, 252;--tblr-gray-400: #bbc3cd;--tblr-gray-400-rgb: 187, 195, 205;--tblr-gray-400-fg: var(--tblr-light);--tblr-gray-400-darken: #a8b0b9;--tblr-gray-400-lt: #f8f9fa;--tblr-gray-400-lt-rgb: 248, 249, 250;--tblr-gray-500: #929dab;--tblr-gray-500-rgb: 146, 157, 171;--tblr-gray-500-fg: var(--tblr-light);--tblr-gray-500-darken: #838d9a;--tblr-gray-500-lt: #f4f5f7;--tblr-gray-500-lt-rgb: 244, 245, 247;--tblr-gray-600: #667382;--tblr-gray-600-rgb: 102, 115, 130;--tblr-gray-600-fg: var(--tblr-light);--tblr-gray-600-darken: #5c6875;--tblr-gray-600-lt: #f0f1f3;--tblr-gray-600-lt-rgb: 240, 241, 243;--tblr-gray-700: #3a4859;--tblr-gray-700-rgb: 58, 72, 89;--tblr-gray-700-fg: var(--tblr-light);--tblr-gray-700-darken: #344150;--tblr-gray-700-lt: #ebedee;--tblr-gray-700-lt-rgb: 235, 237, 238;--tblr-gray-800: #182433;--tblr-gray-800-rgb: 24, 36, 51;--tblr-gray-800-fg: var(--tblr-light);--tblr-gray-800-darken: #16202e;--tblr-gray-800-lt: #e8e9eb;--tblr-gray-800-lt-rgb: 232, 233, 235;--tblr-gray-900: #040a11;--tblr-gray-900-rgb: 4, 10, 17;--tblr-gray-900-fg: var(--tblr-light);--tblr-gray-900-darken: #04090f;--tblr-gray-900-lt: #e6e7e7;--tblr-gray-900-lt-rgb: 230, 231, 231;--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: 3rem;--tblr-spacer-7: 5rem;--tblr-spacer-8: 8rem;--tblr-spacer: 1rem;--tblr-bg-surface: var(--tblr-white);--tblr-bg-surface-secondary: var(--tblr-gray-100);--tblr-bg-surface-tertiary: var(--tblr-gray-50);--tblr-bg-surface-dark: var(--tblr-dark);--tblr-bg-forms: var(--tblr-bg-surface);--tblr-border-color: #dadfe5;--tblr-border-color-translucent: rgba(4, 32, 69, .14);--tblr-border-dark-color: #bbc3cd;--tblr-border-dark-color-translucent: rgba(4, 32, 69, .27);--tblr-border-active-color: #b6bcc3;--tblr-icon-color: var(--tblr-gray-500);--tblr-active-bg: rgba(var(--tblr-primary-rgb), .04);--tblr-disabled-bg: var(--tblr-bg-surface-secondary);--tblr-disabled-color: var(--tblr-gray-300);--tblr-code-color: var(--tblr-gray-600);--tblr-code-bg: var(--tblr-bg-surface-secondary);--tblr-dark-mode-border-color: #1f2e41;--tblr-dark-mode-border-color-translucent: rgba(72, 110, 149, .14);--tblr-dark-mode-border-color-active: #2c415d;--tblr-dark-mode-border-dark-color: #1f2e41;--tblr-page-padding: var(--tblr-spacer-3);--tblr-page-padding-y: var(--tblr-spacer-4);--tblr-font-weight-light: 300;--tblr-font-weight-normal: 400;--tblr-font-weight-medium: 500;--tblr-font-weight-bold: 600;--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-box-shadow: rgba(var(--tblr-body-color-rgb), .04) 0 2px 4px 0;--tblr-box-shadow-border: inset 0 0 0 1px var(--tblr-border-color-translucent);--tblr-box-shadow-transparent: 0 0 0 0 transparent;--tblr-box-shadow-input: 0 1px 1px rgba(var(--tblr-body-color-rgb), .06);--tblr-box-shadow-card: 0 0 4px rgba(var(--tblr-body-color-rgb), .04);--tblr-box-shadow-card-hover: rgba(var(--tblr-body-color-rgb), .16) 0 2px 16px 0;--tblr-box-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)}@media (max-width: 991.98px){:root,:host,[data-bs-theme=light]{--tblr-page-padding: var(--tblr-spacer-2)}}@keyframes pulse{0%{opacity:1;transform:scale3d(.8,.8,.8)}50%{transform:scale(1);opacity:1}to{opacity:1;transform:scale3d(.8,.8,.8)}}@keyframes tada{0%{transform:scale(1)}10%,5%{transform:scale3d(.9,.9,.9) rotate(-5deg)}15%,25%,35%,45%{transform:scale3d(1.1,1.1,1.1) rotate(5deg)}20%,30%,40%{transform:scale3d(1.1,1.1,1.1) rotate(-5deg)}50%{transform:scale(1)}}@keyframes rotate-360{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes blink{0%{opacity:0}50%{opacity:1}to{opacity:0}}body{letter-spacing:0;touch-action:manipulation;text-rendering:optimizeLegibility;font-feature-settings:"liga" 0;position:relative;min-height:100%;height:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media print{body{background:transparent}}*{scrollbar-color:rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.16) 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 rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.16)}*::-webkit-scrollbar-track{background:transparent}*:hover::-webkit-scrollbar-thumb{box-shadow:inset 0 0 0 1rem rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.32)}*::-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:#182433 linear-gradient(to right,rgba(255,255,255,.1),transparent) fixed;padding:1rem;--tblr-theme-boxed-border-radius: 4px}}.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, .06);--tblr-navbar-color: var(--tblr-body-color);--tblr-navbar-border-color: var(--tblr-border-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:2rem;min-height:2rem;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%)}.navbar-nav{margin:0;padding:0}@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.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: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{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{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.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: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{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{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.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: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{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{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.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: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{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{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.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: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{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{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.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: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{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{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;position:relative}@media (prefers-reduced-motion: reduce){.navbar-toggler-icon{transition:none}}.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{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{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{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.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{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{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.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{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{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.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{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{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.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{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{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.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;overflow-y:scroll;padding:0}@media (prefers-reduced-motion: reduce){.navbar-vertical.navbar-expand{transition:none}}.navbar-vertical.navbar-expand.navbar-right{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{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.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 .container{margin-top:auto;margin-bottom:auto}.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)}.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,rgba(0,0,0,0) 0%,rgba(0,0,0,.6) 100%)}.page-header{display:flex;flex-wrap:wrap;min-height:2.25rem;flex-direction:column;justify-content:center}.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:.625rem;font-weight:var(--tblr-font-weight-bold);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-h4);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:#fff;padding:2rem 0;color:var(--tblr-secondary);margin-top:auto}.footer-transparent{background-color:transparent;border-top:0}body:not(.theme-dark):not([data-bs-theme=dark]) .hide-theme-light{display:none!important}body.theme-dark .hide-theme-dark,body[data-bs-theme=dark] .hide-theme-dark{display:none!important}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{--tblr-body-color: #fcfdfe;--tblr-body-color-rgb: 252, 253, 254;--tblr-muted: #3a4859;--tblr-body-bg: #151f2c;--tblr-body-bg-rgb: 21, 31, 44;--tblr-emphasis-color: #ffffff;--tblr-emphasis-color-rgb: 255, 255, 255;--tblr-bg-forms: #151f2c;--tblr-bg-surface: #182433;--tblr-bg-surface-dark: #151f2c;--tblr-bg-surface-secondary: #1b293a;--tblr-bg-surface-tertiary: #151f2c;--tblr-link-color: #00aea3;--tblr-link-hover-color: #00857D;--tblr-active-bg: #1b293a;--tblr-disabled-color: var(--tblr-gray-700);--tblr-border-color: var(--tblr-dark-mode-border-color);--tblr-border-color-translucent: var( --tblr-dark-mode-border-color-translucent );--tblr-border-dark-color: var(--tblr-dark-mode-border-dark-color);--tblr-border-color-active: var( --tblr-dark-mode-border-color-active );--tblr-btn-color: #151f2c;--tblr-code-color: var(--tblr-body-color);--tblr-code-bg: #1f2e41;--tblr-primary-lt: #162e3a;--tblr-primary-lt-rgb: 22, 46, 58;--tblr-secondary-lt: #202c3b;--tblr-secondary-lt-rgb: 32, 44, 59;--tblr-success-lt: #1a3235;--tblr-success-lt-rgb: 26, 50, 53;--tblr-info-lt: #1c3044;--tblr-info-lt-rgb: 28, 48, 68;--tblr-warning-lt: #2e2b2f;--tblr-warning-lt-rgb: 46, 43, 47;--tblr-danger-lt: #2b2634;--tblr-danger-lt-rgb: 43, 38, 52;--tblr-light-lt: #2f3a47;--tblr-light-lt-rgb: 47, 58, 71;--tblr-dark-lt: #182433;--tblr-dark-lt-rgb: 24, 36, 51;--tblr-muted-lt: #202c3b;--tblr-muted-lt-rgb: 32, 44, 59;--tblr-blue-lt: #16293f;--tblr-blue-lt-rgb: 22, 41, 63;--tblr-azure-lt: #1c3044;--tblr-azure-lt-rgb: 28, 48, 68;--tblr-indigo-lt: #1c2a45;--tblr-indigo-lt-rgb: 28, 42, 69;--tblr-purple-lt: #272742;--tblr-purple-lt-rgb: 39, 39, 66;--tblr-pink-lt: #2b2639;--tblr-pink-lt-rgb: 43, 38, 57;--tblr-red-lt: #2b2634;--tblr-red-lt-rgb: 43, 38, 52;--tblr-orange-lt: #2e2b2f;--tblr-orange-lt-rgb: 46, 43, 47;--tblr-yellow-lt: #2e302e;--tblr-yellow-lt-rgb: 46, 48, 46;--tblr-lime-lt: #213330;--tblr-lime-lt-rgb: 33, 51, 48;--tblr-green-lt: #1a3235;--tblr-green-lt-rgb: 26, 50, 53;--tblr-teal-lt: #17313a;--tblr-teal-lt-rgb: 23, 49, 58;--tblr-cyan-lt: #183140;--tblr-cyan-lt-rgb: 24, 49, 64;--tblr-facebook-lt: #182c46;--tblr-facebook-lt-rgb: 24, 44, 70;--tblr-twitter-lt: #193146;--tblr-twitter-lt-rgb: 25, 49, 70;--tblr-linkedin-lt: #172b41;--tblr-linkedin-lt-rgb: 23, 43, 65;--tblr-google-lt: #2c2834;--tblr-google-lt-rgb: 44, 40, 52;--tblr-youtube-lt: #2f202e;--tblr-youtube-lt-rgb: 47, 32, 46;--tblr-vimeo-lt: #183345;--tblr-vimeo-lt-rgb: 24, 51, 69;--tblr-dribbble-lt: #2d283c;--tblr-dribbble-lt-rgb: 45, 40, 60;--tblr-github-lt: #182330;--tblr-github-lt-rgb: 24, 35, 48;--tblr-instagram-lt: #2c2737;--tblr-instagram-lt-rgb: 44, 39, 55;--tblr-pinterest-lt: #292131;--tblr-pinterest-lt-rgb: 41, 33, 49;--tblr-vk-lt: #202e3f;--tblr-vk-lt-rgb: 32, 46, 63;--tblr-rss-lt: #2f312e;--tblr-rss-lt-rgb: 47, 49, 46;--tblr-flickr-lt: #162a44;--tblr-flickr-lt-rgb: 22, 42, 68;--tblr-bitbucket-lt: #162942;--tblr-bitbucket-lt-rgb: 22, 41, 66;--tblr-tabler-lt: #16293f;--tblr-tabler-lt-rgb: 22, 41, 63}[data-bs-theme=dark] .navbar-brand-autodark .navbar-brand-image{filter:brightness(0) invert(1)}.accordion{--tblr-accordion-color: var(--tblr-body-color)}.accordion-button:focus:not(:focus-visible){outline:none;box-shadow:none}.accordion-button:after{opacity:.7}.accordion-button:not(.collapsed){font-weight:var(--tblr-font-weight-bold);border-bottom-color:transparent;box-shadow:none}.accordion-button:not(.collapsed):after{opacity:1}.alert{--tblr-alert-color: var(--tblr-secondary);--tblr-alert-bg: var(--tblr-surface);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);border-left:.25rem var(--tblr-border-style) var(--tblr-alert-color);box-shadow:#1824330a 0 2px 4px}.alert>:last-child{margin-bottom:0}.alert-important{border-color:transparent;background:var(--tblr-alert-color);color:#fff}.alert-important .alert-icon,.alert-important .alert-link,.alert-important .alert-title,.alert-important .alert-link:hover{color:inherit}.alert-important .btn-close{filter:var(--tblr-btn-close-white-filter)}.alert-link,.alert-link:hover{color:var(--tblr-alert-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)}.alert-facebook{--tblr-alert-color: var(--tblr-facebook)}.alert-twitter{--tblr-alert-color: var(--tblr-twitter)}.alert-linkedin{--tblr-alert-color: var(--tblr-linkedin)}.alert-google{--tblr-alert-color: var(--tblr-google)}.alert-youtube{--tblr-alert-color: var(--tblr-youtube)}.alert-vimeo{--tblr-alert-color: var(--tblr-vimeo)}.alert-dribbble{--tblr-alert-color: var(--tblr-dribbble)}.alert-github{--tblr-alert-color: var(--tblr-github)}.alert-instagram{--tblr-alert-color: var(--tblr-instagram)}.alert-pinterest{--tblr-alert-color: var(--tblr-pinterest)}.alert-vk{--tblr-alert-color: var(--tblr-vk)}.alert-rss{--tblr-alert-color: var(--tblr-rss)}.alert-flickr{--tblr-alert-color: var(--tblr-flickr)}.alert-bitbucket{--tblr-alert-color: var(--tblr-bitbucket)}.alert-tabler{--tblr-alert-color: var(--tblr-tabler)}.alert-icon{color:var(--tblr-alert-color);width:1.5rem!important;height:1.5rem!important;margin:-.125rem 1rem -.125rem 0}.alert-title{font-size:.875rem;line-height:1.25rem;font-weight:var(--tblr-font-weight-bold);margin-bottom:.25rem;color:var(--tblr-alert-color)}.avatar{--tblr-avatar-size: 2.5rem;--tblr-avatar-status-size: .75rem;--tblr-avatar-bg: var(--tblr-bg-surface-secondary);--tblr-avatar-box-shadow: var(--tblr-box-shadow-border);--tblr-avatar-font-size: 1rem;--tblr-avatar-icon-size: 1.5rem;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)}.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}.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: .75rem}.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: 1rem}.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.25rem}.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}.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}.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}.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}.avatar-2xl .badge:empty{width:1rem;height:1rem}.avatar-list{--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-cap-bg, var(--tblr-card-bg, var(--tblr-bg-surface)))}.avatar-upload{width:4rem;height:4rem;border:var(--tblr-border-width) dashed var(--tblr-border-color);background:var(--tblr-bg-forms);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))}.badge{justify-content:center;align-items:center;background:var(--tblr-bg-surface-secondary);overflow:hidden;user-select:none;border:var(--tblr-border-width) var(--tblr-border-style) transparent;min-width:1.35714285em;font-weight:var(--tblr-font-weight-bold);letter-spacing:.04em;vertical-align:bottom}a.badge{color:var(--tblr-bg-surface)}.badge .avatar{box-sizing:content-box;width:1.25rem;height:1.25rem;margin:0 .5rem 0 -.5rem}.badge .icon{width:1em;height:1em;font-size:1rem;stroke-width:2}.badge:empty,.badge-empty{display:inline-block;width:.5rem;height:.5rem;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}.breadcrumb{--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);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{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-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-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-color-active);--tblr-btn-box-shadow: var(--tblr-box-shadow-input);--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)}.btn .icon{width:var(--tblr-btn-icon-size);height:var(--tblr-btn-icon-size);min-width: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:inherit}.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{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:#00857d;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);--tblr-btn-bg: var(--tblr-primary);--tblr-btn-hover-color: var(--tblr-primary-fg);--tblr-btn-hover-bg: rgba(var(--tblr-primary-rgb), .8);--tblr-btn-active-color: var(--tblr-primary-fg);--tblr-btn-active-bg: rgba(var(--tblr-primary-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-primary);--tblr-btn-disabled-color: var(--tblr-primary-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-primary);--tblr-btn-disabled-border-color: var(--tblr-primary)}.btn-secondary{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-secondary-fg);--tblr-btn-bg: var(--tblr-secondary);--tblr-btn-hover-color: var(--tblr-secondary-fg);--tblr-btn-hover-bg: rgba(var(--tblr-secondary-rgb), .8);--tblr-btn-active-color: var(--tblr-secondary-fg);--tblr-btn-active-bg: rgba(var(--tblr-secondary-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-secondary);--tblr-btn-disabled-color: var(--tblr-secondary-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-secondary{--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-disabled-color: var(--tblr-secondary);--tblr-btn-disabled-border-color: var(--tblr-secondary)}.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);--tblr-btn-bg: var(--tblr-success);--tblr-btn-hover-color: var(--tblr-success-fg);--tblr-btn-hover-bg: rgba(var(--tblr-success-rgb), .8);--tblr-btn-active-color: var(--tblr-success-fg);--tblr-btn-active-bg: rgba(var(--tblr-success-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-success);--tblr-btn-disabled-color: var(--tblr-success-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-success);--tblr-btn-disabled-border-color: var(--tblr-success)}.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);--tblr-btn-bg: var(--tblr-info);--tblr-btn-hover-color: var(--tblr-info-fg);--tblr-btn-hover-bg: rgba(var(--tblr-info-rgb), .8);--tblr-btn-active-color: var(--tblr-info-fg);--tblr-btn-active-bg: rgba(var(--tblr-info-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-info);--tblr-btn-disabled-color: var(--tblr-info-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-info);--tblr-btn-disabled-border-color: var(--tblr-info)}.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);--tblr-btn-bg: var(--tblr-warning);--tblr-btn-hover-color: var(--tblr-warning-fg);--tblr-btn-hover-bg: rgba(var(--tblr-warning-rgb), .8);--tblr-btn-active-color: var(--tblr-warning-fg);--tblr-btn-active-bg: rgba(var(--tblr-warning-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-warning);--tblr-btn-disabled-color: var(--tblr-warning-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-warning);--tblr-btn-disabled-border-color: var(--tblr-warning)}.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);--tblr-btn-bg: var(--tblr-danger);--tblr-btn-hover-color: var(--tblr-danger-fg);--tblr-btn-hover-bg: rgba(var(--tblr-danger-rgb), .8);--tblr-btn-active-color: var(--tblr-danger-fg);--tblr-btn-active-bg: rgba(var(--tblr-danger-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-danger);--tblr-btn-disabled-color: var(--tblr-danger-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-danger);--tblr-btn-disabled-border-color: var(--tblr-danger)}.btn-light{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-light-fg);--tblr-btn-bg: var(--tblr-light);--tblr-btn-hover-color: var(--tblr-light-fg);--tblr-btn-hover-bg: rgba(var(--tblr-light-rgb), .8);--tblr-btn-active-color: var(--tblr-light-fg);--tblr-btn-active-bg: rgba(var(--tblr-light-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-light);--tblr-btn-disabled-color: var(--tblr-light-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-light{--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-disabled-color: var(--tblr-light);--tblr-btn-disabled-border-color: var(--tblr-light)}.btn-dark{--tblr-btn-border-color: var(--tblr-dark-mode-border-color);--tblr-btn-hover-border-color: var(--tblr-dark-mode-border-color-active);--tblr-btn-active-border-color: var(--tblr-dark-mode-border-color-active);--tblr-btn-color: var(--tblr-dark-fg);--tblr-btn-bg: var(--tblr-dark);--tblr-btn-hover-color: var(--tblr-dark-fg);--tblr-btn-hover-bg: rgba(var(--tblr-dark-rgb), .8);--tblr-btn-active-color: var(--tblr-dark-fg);--tblr-btn-active-bg: rgba(var(--tblr-dark-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-dark);--tblr-btn-disabled-color: var(--tblr-dark-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-dark{--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-disabled-color: var(--tblr-dark);--tblr-btn-disabled-border-color: var(--tblr-dark)}.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);--tblr-btn-bg: var(--tblr-muted);--tblr-btn-hover-color: var(--tblr-muted-fg);--tblr-btn-hover-bg: rgba(var(--tblr-muted-rgb), .8);--tblr-btn-active-color: var(--tblr-muted-fg);--tblr-btn-active-bg: rgba(var(--tblr-muted-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-muted);--tblr-btn-disabled-color: var(--tblr-muted-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-muted);--tblr-btn-disabled-border-color: var(--tblr-muted)}.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);--tblr-btn-bg: var(--tblr-blue);--tblr-btn-hover-color: var(--tblr-blue-fg);--tblr-btn-hover-bg: rgba(var(--tblr-blue-rgb), .8);--tblr-btn-active-color: var(--tblr-blue-fg);--tblr-btn-active-bg: rgba(var(--tblr-blue-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-blue);--tblr-btn-disabled-color: var(--tblr-blue-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-blue);--tblr-btn-disabled-border-color: var(--tblr-blue)}.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);--tblr-btn-bg: var(--tblr-azure);--tblr-btn-hover-color: var(--tblr-azure-fg);--tblr-btn-hover-bg: rgba(var(--tblr-azure-rgb), .8);--tblr-btn-active-color: var(--tblr-azure-fg);--tblr-btn-active-bg: rgba(var(--tblr-azure-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-azure);--tblr-btn-disabled-color: var(--tblr-azure-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-azure);--tblr-btn-disabled-border-color: var(--tblr-azure)}.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);--tblr-btn-bg: var(--tblr-indigo);--tblr-btn-hover-color: var(--tblr-indigo-fg);--tblr-btn-hover-bg: rgba(var(--tblr-indigo-rgb), .8);--tblr-btn-active-color: var(--tblr-indigo-fg);--tblr-btn-active-bg: rgba(var(--tblr-indigo-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-indigo);--tblr-btn-disabled-color: var(--tblr-indigo-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-indigo);--tblr-btn-disabled-border-color: var(--tblr-indigo)}.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);--tblr-btn-bg: var(--tblr-purple);--tblr-btn-hover-color: var(--tblr-purple-fg);--tblr-btn-hover-bg: rgba(var(--tblr-purple-rgb), .8);--tblr-btn-active-color: var(--tblr-purple-fg);--tblr-btn-active-bg: rgba(var(--tblr-purple-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-purple);--tblr-btn-disabled-color: var(--tblr-purple-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-purple);--tblr-btn-disabled-border-color: var(--tblr-purple)}.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);--tblr-btn-bg: var(--tblr-pink);--tblr-btn-hover-color: var(--tblr-pink-fg);--tblr-btn-hover-bg: rgba(var(--tblr-pink-rgb), .8);--tblr-btn-active-color: var(--tblr-pink-fg);--tblr-btn-active-bg: rgba(var(--tblr-pink-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-pink);--tblr-btn-disabled-color: var(--tblr-pink-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-pink);--tblr-btn-disabled-border-color: var(--tblr-pink)}.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);--tblr-btn-bg: var(--tblr-red);--tblr-btn-hover-color: var(--tblr-red-fg);--tblr-btn-hover-bg: rgba(var(--tblr-red-rgb), .8);--tblr-btn-active-color: var(--tblr-red-fg);--tblr-btn-active-bg: rgba(var(--tblr-red-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-red);--tblr-btn-disabled-color: var(--tblr-red-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-red);--tblr-btn-disabled-border-color: var(--tblr-red)}.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);--tblr-btn-bg: var(--tblr-orange);--tblr-btn-hover-color: var(--tblr-orange-fg);--tblr-btn-hover-bg: rgba(var(--tblr-orange-rgb), .8);--tblr-btn-active-color: var(--tblr-orange-fg);--tblr-btn-active-bg: rgba(var(--tblr-orange-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-orange);--tblr-btn-disabled-color: var(--tblr-orange-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-orange);--tblr-btn-disabled-border-color: var(--tblr-orange)}.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);--tblr-btn-bg: var(--tblr-yellow);--tblr-btn-hover-color: var(--tblr-yellow-fg);--tblr-btn-hover-bg: rgba(var(--tblr-yellow-rgb), .8);--tblr-btn-active-color: var(--tblr-yellow-fg);--tblr-btn-active-bg: rgba(var(--tblr-yellow-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-yellow);--tblr-btn-disabled-color: var(--tblr-yellow-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-yellow);--tblr-btn-disabled-border-color: var(--tblr-yellow)}.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);--tblr-btn-bg: var(--tblr-lime);--tblr-btn-hover-color: var(--tblr-lime-fg);--tblr-btn-hover-bg: rgba(var(--tblr-lime-rgb), .8);--tblr-btn-active-color: var(--tblr-lime-fg);--tblr-btn-active-bg: rgba(var(--tblr-lime-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-lime);--tblr-btn-disabled-color: var(--tblr-lime-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-lime);--tblr-btn-disabled-border-color: var(--tblr-lime)}.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);--tblr-btn-bg: var(--tblr-green);--tblr-btn-hover-color: var(--tblr-green-fg);--tblr-btn-hover-bg: rgba(var(--tblr-green-rgb), .8);--tblr-btn-active-color: var(--tblr-green-fg);--tblr-btn-active-bg: rgba(var(--tblr-green-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-green);--tblr-btn-disabled-color: var(--tblr-green-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-green);--tblr-btn-disabled-border-color: var(--tblr-green)}.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);--tblr-btn-bg: var(--tblr-teal);--tblr-btn-hover-color: var(--tblr-teal-fg);--tblr-btn-hover-bg: rgba(var(--tblr-teal-rgb), .8);--tblr-btn-active-color: var(--tblr-teal-fg);--tblr-btn-active-bg: rgba(var(--tblr-teal-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-teal);--tblr-btn-disabled-color: var(--tblr-teal-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-teal);--tblr-btn-disabled-border-color: var(--tblr-teal)}.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);--tblr-btn-bg: var(--tblr-cyan);--tblr-btn-hover-color: var(--tblr-cyan-fg);--tblr-btn-hover-bg: rgba(var(--tblr-cyan-rgb), .8);--tblr-btn-active-color: var(--tblr-cyan-fg);--tblr-btn-active-bg: rgba(var(--tblr-cyan-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-cyan);--tblr-btn-disabled-color: var(--tblr-cyan-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-cyan);--tblr-btn-disabled-border-color: var(--tblr-cyan)}.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);--tblr-btn-bg: var(--tblr-facebook);--tblr-btn-hover-color: var(--tblr-facebook-fg);--tblr-btn-hover-bg: rgba(var(--tblr-facebook-rgb), .8);--tblr-btn-active-color: var(--tblr-facebook-fg);--tblr-btn-active-bg: rgba(var(--tblr-facebook-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-facebook);--tblr-btn-disabled-color: var(--tblr-facebook-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-facebook);--tblr-btn-disabled-border-color: var(--tblr-facebook)}.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);--tblr-btn-bg: var(--tblr-twitter);--tblr-btn-hover-color: var(--tblr-twitter-fg);--tblr-btn-hover-bg: rgba(var(--tblr-twitter-rgb), .8);--tblr-btn-active-color: var(--tblr-twitter-fg);--tblr-btn-active-bg: rgba(var(--tblr-twitter-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-twitter);--tblr-btn-disabled-color: var(--tblr-twitter-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-twitter);--tblr-btn-disabled-border-color: var(--tblr-twitter)}.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);--tblr-btn-bg: var(--tblr-linkedin);--tblr-btn-hover-color: var(--tblr-linkedin-fg);--tblr-btn-hover-bg: rgba(var(--tblr-linkedin-rgb), .8);--tblr-btn-active-color: var(--tblr-linkedin-fg);--tblr-btn-active-bg: rgba(var(--tblr-linkedin-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-linkedin);--tblr-btn-disabled-color: var(--tblr-linkedin-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-linkedin);--tblr-btn-disabled-border-color: var(--tblr-linkedin)}.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);--tblr-btn-bg: var(--tblr-google);--tblr-btn-hover-color: var(--tblr-google-fg);--tblr-btn-hover-bg: rgba(var(--tblr-google-rgb), .8);--tblr-btn-active-color: var(--tblr-google-fg);--tblr-btn-active-bg: rgba(var(--tblr-google-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-google);--tblr-btn-disabled-color: var(--tblr-google-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-google);--tblr-btn-disabled-border-color: var(--tblr-google)}.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);--tblr-btn-bg: var(--tblr-youtube);--tblr-btn-hover-color: var(--tblr-youtube-fg);--tblr-btn-hover-bg: rgba(var(--tblr-youtube-rgb), .8);--tblr-btn-active-color: var(--tblr-youtube-fg);--tblr-btn-active-bg: rgba(var(--tblr-youtube-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-youtube);--tblr-btn-disabled-color: var(--tblr-youtube-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-youtube);--tblr-btn-disabled-border-color: var(--tblr-youtube)}.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);--tblr-btn-bg: var(--tblr-vimeo);--tblr-btn-hover-color: var(--tblr-vimeo-fg);--tblr-btn-hover-bg: rgba(var(--tblr-vimeo-rgb), .8);--tblr-btn-active-color: var(--tblr-vimeo-fg);--tblr-btn-active-bg: rgba(var(--tblr-vimeo-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-vimeo);--tblr-btn-disabled-color: var(--tblr-vimeo-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-vimeo);--tblr-btn-disabled-border-color: var(--tblr-vimeo)}.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);--tblr-btn-bg: var(--tblr-dribbble);--tblr-btn-hover-color: var(--tblr-dribbble-fg);--tblr-btn-hover-bg: rgba(var(--tblr-dribbble-rgb), .8);--tblr-btn-active-color: var(--tblr-dribbble-fg);--tblr-btn-active-bg: rgba(var(--tblr-dribbble-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-dribbble);--tblr-btn-disabled-color: var(--tblr-dribbble-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-dribbble);--tblr-btn-disabled-border-color: var(--tblr-dribbble)}.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);--tblr-btn-bg: var(--tblr-github);--tblr-btn-hover-color: var(--tblr-github-fg);--tblr-btn-hover-bg: rgba(var(--tblr-github-rgb), .8);--tblr-btn-active-color: var(--tblr-github-fg);--tblr-btn-active-bg: rgba(var(--tblr-github-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-github);--tblr-btn-disabled-color: var(--tblr-github-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-github);--tblr-btn-disabled-border-color: var(--tblr-github)}.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);--tblr-btn-bg: var(--tblr-instagram);--tblr-btn-hover-color: var(--tblr-instagram-fg);--tblr-btn-hover-bg: rgba(var(--tblr-instagram-rgb), .8);--tblr-btn-active-color: var(--tblr-instagram-fg);--tblr-btn-active-bg: rgba(var(--tblr-instagram-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-instagram);--tblr-btn-disabled-color: var(--tblr-instagram-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-instagram);--tblr-btn-disabled-border-color: var(--tblr-instagram)}.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);--tblr-btn-bg: var(--tblr-pinterest);--tblr-btn-hover-color: var(--tblr-pinterest-fg);--tblr-btn-hover-bg: rgba(var(--tblr-pinterest-rgb), .8);--tblr-btn-active-color: var(--tblr-pinterest-fg);--tblr-btn-active-bg: rgba(var(--tblr-pinterest-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-pinterest);--tblr-btn-disabled-color: var(--tblr-pinterest-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-pinterest);--tblr-btn-disabled-border-color: var(--tblr-pinterest)}.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);--tblr-btn-bg: var(--tblr-vk);--tblr-btn-hover-color: var(--tblr-vk-fg);--tblr-btn-hover-bg: rgba(var(--tblr-vk-rgb), .8);--tblr-btn-active-color: var(--tblr-vk-fg);--tblr-btn-active-bg: rgba(var(--tblr-vk-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-vk);--tblr-btn-disabled-color: var(--tblr-vk-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-vk);--tblr-btn-disabled-border-color: var(--tblr-vk)}.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);--tblr-btn-bg: var(--tblr-rss);--tblr-btn-hover-color: var(--tblr-rss-fg);--tblr-btn-hover-bg: rgba(var(--tblr-rss-rgb), .8);--tblr-btn-active-color: var(--tblr-rss-fg);--tblr-btn-active-bg: rgba(var(--tblr-rss-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-rss);--tblr-btn-disabled-color: var(--tblr-rss-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-rss);--tblr-btn-disabled-border-color: var(--tblr-rss)}.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);--tblr-btn-bg: var(--tblr-flickr);--tblr-btn-hover-color: var(--tblr-flickr-fg);--tblr-btn-hover-bg: rgba(var(--tblr-flickr-rgb), .8);--tblr-btn-active-color: var(--tblr-flickr-fg);--tblr-btn-active-bg: rgba(var(--tblr-flickr-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-flickr);--tblr-btn-disabled-color: var(--tblr-flickr-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-flickr);--tblr-btn-disabled-border-color: var(--tblr-flickr)}.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);--tblr-btn-bg: var(--tblr-bitbucket);--tblr-btn-hover-color: var(--tblr-bitbucket-fg);--tblr-btn-hover-bg: rgba(var(--tblr-bitbucket-rgb), .8);--tblr-btn-active-color: var(--tblr-bitbucket-fg);--tblr-btn-active-bg: rgba(var(--tblr-bitbucket-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-bitbucket);--tblr-btn-disabled-color: var(--tblr-bitbucket-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-bitbucket);--tblr-btn-disabled-border-color: var(--tblr-bitbucket)}.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);--tblr-btn-bg: var(--tblr-tabler);--tblr-btn-hover-color: var(--tblr-tabler-fg);--tblr-btn-hover-bg: rgba(var(--tblr-tabler-rgb), .8);--tblr-btn-active-color: var(--tblr-tabler-fg);--tblr-btn-active-bg: rgba(var(--tblr-tabler-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-tabler);--tblr-btn-disabled-color: var(--tblr-tabler-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-tabler);--tblr-btn-disabled-border-color: var(--tblr-tabler)}.btn-ghost-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-ghost-secondary{--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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-light{--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-ghost-dark{--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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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.5;--tblr-btn-icon-size: .75rem}.btn-lg,.btn-group-lg>.btn{--tblr-btn-line-height: 1.5;--tblr-btn-icon-size: 2rem}.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{min-width:calc(var(--tblr-btn-line-height) * var(--tblr-btn-font-size) + var(--tblr-btn-padding-y) * 2 + var(--tblr-btn-border-width) * 2);min-height:calc(var(--tblr-btn-line-height) * var(--tblr-btn-font-size) + var(--tblr-btn-padding-y) * 2 + var(--tblr-btn-border-width) * 2);padding-left:0;padding-right:0}.btn-icon .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:1.5rem;right:1.5rem;border-radius:100rem}.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{padding:0;border:0;color:var(--tblr-secondary);display:inline-flex;width:2rem;height:2rem;align-items:center;justify-content:center;border-radius:var(--tblr-border-radius);background:transparent}.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)}.btn-action.show{color:var(--tblr-primary)}.btn-action .icon{margin:0;width:1.25rem;height:1.25rem;font-size:1.25rem;stroke-width:1}.btn-actions{display:flex}.btn-group,.btn-group-vertical{box-shadow:var(--tblr-box-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 0;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,rgba(24,36,51,.9),rgba(24,36,51,0))}.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 0}.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:4px;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) - (var(--tblr-border-width)));border-bottom-left-radius:calc(var(--tblr-border-radius) - (var(--tblr-border-width)))}.card-img,.card-img-end{border-top-right-radius:calc(var(--tblr-border-radius) - (var(--tblr-border-width)));border-bottom-right-radius:calc(var(--tblr-border-radius) - (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,rgba(0,0,0,0) 0%,rgba(0,0,0,.6) 100%)}.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:1.25rem;text-align:center;transition:background .3s;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)}@media (prefers-reduced-motion: reduce){.card-btn{transition:none}}.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:1.25rem;background:#666666 no-repeat center/cover}.card-cover:before{position:absolute;inset:0;content:"";background:rgba(24,36,51,.48)}.card-cover:first-child,.card-cover:first-child:before{border-radius:4px 4px 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}.card-header-pills{flex:1;margin-top:-.5rem;margin-bottom:-.5rem}.card-rotate-left{transform:rotate(-1.5deg)}.card-rotate-right{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) var(--tblr-border-radius)}.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{cursor:pointer}.btn-close:focus{outline:none}.dropdown-menu{user-select:none}.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}.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:.625rem;font-weight:var(--tblr-font-weight-bold);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(-0.25rem - 1px);margin-left:-.25rem}.dropend .dropdown-toggle:after{margin-left:auto}.dropdown-menu-card{padding:0}.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:.625rem;font-weight:var(--tblr-font-weight-bold);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-img img{height:8rem;width:auto}.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:45rem}.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:3rem}.space-x-6{display:flex;gap:3rem}.space-y-7{display:flex;flex-direction:column;gap:5rem}.space-x-7{display:flex;gap:5rem}.space-y-8{display:flex;flex-direction:column;gap:8rem}.space-x-8{display:flex;gap:8rem}.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:3rem!important}.divide-y-6>:not(template):not(:last-child){padding-bottom:3rem!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:3rem!important}.divide-x-6>:not(template):not(:last-child){padding-right:3rem!important}.divide-y-7>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-7>:not(template):not(:first-child){padding-top:5rem!important}.divide-y-7>:not(template):not(:last-child){padding-bottom:5rem!important}.divide-x-7>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-7>:not(template):not(:first-child){padding-left:5rem!important}.divide-x-7>:not(template):not(:last-child){padding-right:5rem!important}.divide-y-8>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-8>:not(template):not(:first-child){padding-top:8rem!important}.divide-y-8>:not(template):not(:last-child){padding-bottom:8rem!important}.divide-x-8>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-8>:not(template):not(:first-child){padding-left:8rem!important}.divide-x-8>:not(template):not(:last-child){padding-right:8rem!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-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%}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}.form-select:-moz-focusring{color:var(--tblr-body-color)}.form-control:-webkit-autofill{box-shadow:0 0 0 1000px var(--tblr-body-bg) 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-body-bg);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius)}.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-box-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}.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:3px;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='%23182433' 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{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:3px}.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:2px;border-top-right-radius:2px}.form-imagecheck-image:last-child{border-bottom-right-radius:2px;border-bottom-left-radius:2px}.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.4285714286em + 1.125rem + calc(var(--tblr-border-width) * 2));margin:0;padding:.5625rem .75rem;font-size:.875rem;line-height:1.4285714286;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:3px;box-shadow:var(--tblr-box-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-box-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;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-box-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-secondary);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:active,.list-group-item:focus,.list-group-item:hover{background-color:rgba(var(--tblr-secondary-rgb),.08)}.list-group-item.disabled,.list-group-item:disabled{color:#929dab;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-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-item{padding:1rem 0}.list-separated-item:first-child{padding-top:0}.list-separated-item:last-child{padding-bottom:0}.list-separated-item+.list-separated-item{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.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:#0054a6;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{position:absolute;top:0;right:0;width:3.5rem;height:3.5rem;margin:0;padding:0;z-index:10}.modal-body{scrollbar-color:rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.16) 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 rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.16)}.modal-body::-webkit-scrollbar-track{background:transparent}.modal-body:hover::-webkit-scrollbar-thumb{box-shadow:inset 0 0 0 1rem rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.32)}.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-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)}.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 -var(--tblr-border-width);border:0;border-bottom:2px var(--tblr-border-style) transparent;color:var(--tblr-secondary)}.nav-bordered .nav-link.active,.nav-bordered .nav-item.show .nav-link{color:var(--tblr-primary);border-color:var(--tblr-primary)}.nav-link{display:flex;transition:color .3s;align-items:center}@media (prefers-reduced-motion: reduce){.nav-link{transition:none}}.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:var(--tblr-icon-color)}.nav-link-icon svg{display:block;height:100%}.nav-fill .nav-item .nav-link{justify-content:center}.stars{display:inline-flex;color:#bbc3cd;font-size:.75rem}.stars .star:not(:first-child){margin-left:.25rem}.pagination{user-select:none}.page-link{min-width:1.75rem;border-radius:var(--tblr-border-radius)}.page-item{text-align:center}.page-item:not(.active) .page-link:hover{background:transparent}.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)}@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))}.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:1.7142857143}.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>blockquote{font-size:1rem;margin:1.5rem 0;padding:.5rem 1.5rem}.markdown>img{border-radius:var(--tblr-border-radius)}.placeholder:not(.btn):not([class*=bg-]){background-color:currentColor!important}.placeholder:not(.avatar):not([class*=card-img-]){border-radius:var(--tblr-border-radius)}.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-azure{--tblr-steps-color: var(--tblr-azure)}.steps-indigo{--tblr-steps-color: var(--tblr-indigo)}.steps-purple{--tblr-steps-color: var(--tblr-purple)}.steps-pink{--tblr-steps-color: var(--tblr-pink)}.steps-red{--tblr-steps-color: var(--tblr-red)}.steps-orange{--tblr-steps-color: var(--tblr-orange)}.steps-yellow{--tblr-steps-color: var(--tblr-yellow)}.steps-lime{--tblr-steps-color: var(--tblr-lime)}.steps-green{--tblr-steps-color: var(--tblr-green)}.steps-teal{--tblr-steps-color: var(--tblr-teal)}.steps-cyan{--tblr-steps-color: var(--tblr-cyan)}.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: #667382;--tblr-status-color-rgb: 102, 115, 130;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: #667382;--tblr-status-color-rgb: 102, 115, 130}.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: #f76707;--tblr-status-color-rgb: 247, 103, 7}.status-danger{--tblr-status-color: #d63939;--tblr-status-color-rgb: 214, 57, 57}.status-light{--tblr-status-color: #fcfdfe;--tblr-status-color-rgb: 252, 253, 254}.status-dark{--tblr-status-color: #182433;--tblr-status-color-rgb: 24, 36, 51}.status-muted{--tblr-status-color: #667382;--tblr-status-color-rgb: 102, 115, 130}.status-blue{--tblr-status-color: #0054a6;--tblr-status-color-rgb: 0, 84, 166}.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-facebook{--tblr-status-color: #1877f2;--tblr-status-color-rgb: 24, 119, 242}.status-twitter{--tblr-status-color: #1da1f2;--tblr-status-color-rgb: 29, 161, 242}.status-linkedin{--tblr-status-color: #0a66c2;--tblr-status-color-rgb: 10, 102, 194}.status-google{--tblr-status-color: #dc4e41;--tblr-status-color-rgb: 220, 78, 65}.status-youtube{--tblr-status-color: #ff0000;--tblr-status-color-rgb: 255, 0, 0}.status-vimeo{--tblr-status-color: #1ab7ea;--tblr-status-color-rgb: 26, 183, 234}.status-dribbble{--tblr-status-color: #ea4c89;--tblr-status-color-rgb: 234, 76, 137}.status-github{--tblr-status-color: #181717;--tblr-status-color-rgb: 24, 23, 23}.status-instagram{--tblr-status-color: #e4405f;--tblr-status-color-rgb: 228, 64, 95}.status-pinterest{--tblr-status-color: #bd081c;--tblr-status-color-rgb: 189, 8, 28}.status-vk{--tblr-status-color: #6383a8;--tblr-status-color-rgb: 99, 131, 168}.status-rss{--tblr-status-color: #ffa500;--tblr-status-color-rgb: 255, 165, 0}.status-flickr{--tblr-status-color: #0063dc;--tblr-status-color-rgb: 0, 99, 220}.status-bitbucket{--tblr-status-color: #0052cc;--tblr-status-color-rgb: 0, 82, 204}.status-tabler{--tblr-status-color: #0054a6;--tblr-status-color-rgb: 0, 84, 166}.status-dot{--tblr-status-dot-color: var(--tblr-status-color, #667382);--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, #667382);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-right,.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-right .switch-icon-a,.switch-icon-slide-right .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-right .switch-icon-a,.switch-icon-slide-right .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-right .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-right .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-right.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-right.active .switch-icon-b,.switch-icon-slide-down.active .switch-icon-b{transform:translateY(0)}.switch-icon-slide-left .switch-icon-a{transform:translate(0)}.switch-icon-slide-left .switch-icon-b{transform:translate(100%)}.switch-icon-slide-left.active .switch-icon-a{transform:translate(-100%)}.switch-icon-slide-left.active .switch-icon-b,.switch-icon-slide-right .switch-icon-a{transform:translate(0)}.switch-icon-slide-right .switch-icon-b{transform:translate(-100%)}.switch-icon-slide-right.active .switch-icon-a{transform:translate(100%)}.switch-icon-slide-right.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:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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;margin:-.5rem;padding:.5rem}@media (prefers-reduced-motion: reduce){.table-sort{transition:none}}.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}.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-box-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{background:#ffffff;border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);box-shadow:#1824330a 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: #667382}.toast-success{--tblr-toast-color: #2fb344}.toast-info{--tblr-toast-color: #4299e1}.toast-warning{--tblr-toast-color: #f76707}.toast-danger{--tblr-toast-color: #d63939}.toast-light{--tblr-toast-color: #fcfdfe}.toast-dark{--tblr-toast-color: #182433}.toast-muted{--tblr-toast-color: #667382}.toast-blue{--tblr-toast-color: #0054a6}.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}.toast-facebook{--tblr-toast-color: #1877f2}.toast-twitter{--tblr-toast-color: #1da1f2}.toast-linkedin{--tblr-toast-color: #0a66c2}.toast-google{--tblr-toast-color: #dc4e41}.toast-youtube{--tblr-toast-color: #ff0000}.toast-vimeo{--tblr-toast-color: #1ab7ea}.toast-dribbble{--tblr-toast-color: #ea4c89}.toast-github{--tblr-toast-color: #181717}.toast-instagram{--tblr-toast-color: #e4405f}.toast-pinterest{--tblr-toast-color: #bd081c}.toast-vk{--tblr-toast-color: #6383a8}.toast-rss{--tblr-toast-color: #ffa500}.toast-flickr{--tblr-toast-color: #0063dc}.toast-bitbucket{--tblr-toast-color: #0052cc}.toast-tabler{--tblr-toast-color: #0054a6}.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-gray-200);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:.625rem;font-weight:var(--tblr-font-weight-bold);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{content:none}.hr-text.hr-text-left>*:first-child{padding-right:.5rem;padding-left:.5rem}.hr-text.hr-text-right:before{content:""}.hr-text.hr-text-right:after{content:none}.hr-text.hr-text-right>*:first-child{padding-right:0;padding-left:.5rem}.card>.hr-text{margin:0}.hr-text-spaceless{margin:-.5rem 0}.lead{line-height:1.4}a{text-decoration-skip-ink:auto}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)}strong,.strong,b{font-weight:var(--tblr-font-weight-bold)}blockquote{padding-left: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{padding:1rem;background:var(--tblr-bg-surface-dark);color:var(--tblr-light);border-radius:var(--tblr-border-radius)}pre code{background:transparent}code{background:var(--tblr-code-bg);padding:2px 4px;border-radius:var(--tblr-border-radius)}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%}.list-unstyled{margin-left:0}::selection{background-color:rgba(var(--tblr-primary-rgb),.16)}[class^=link-].disabled,[class*=" link-"].disabled{color:var(--tblr-disabled-color);pointer-events:none}.subheader{font-size:.625rem;font-weight:var(--tblr-font-weight-bold);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary)}.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) rgba(4,32,69,.14)}.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}.bg-white-overlay{color:#fff;background-color:#fcfdfe3d}.bg-dark-overlay{color:#fff;background-color:#1824333d}.bg-cover{background-repeat:no-repeat;background-size:cover;background-position:center}.bg-primary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-primary-rgb),var(--tblr-bg-opacity))!important}.bg-primary-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-primary-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-primary-lt-rgb),var(--tblr-bg-opacity))!important}.border-primary{border-color:#00857d!important}.bg-secondary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-secondary-rgb),var(--tblr-bg-opacity))!important}.bg-secondary-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-secondary-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-secondary-lt-rgb),var(--tblr-bg-opacity))!important}.border-secondary{border-color:#667382!important}.bg-success{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-success-rgb),var(--tblr-bg-opacity))!important}.bg-success-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-success-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-success-lt-rgb),var(--tblr-bg-opacity))!important}.border-success{border-color:#2fb344!important}.bg-info{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-info-rgb),var(--tblr-bg-opacity))!important}.bg-info-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-info-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-info-lt-rgb),var(--tblr-bg-opacity))!important}.border-info{border-color:#4299e1!important}.bg-warning{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-warning-rgb),var(--tblr-bg-opacity))!important}.bg-warning-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-warning-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-warning-lt-rgb),var(--tblr-bg-opacity))!important}.border-warning{border-color:#f76707!important}.bg-danger{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-danger-rgb),var(--tblr-bg-opacity))!important}.bg-danger-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-danger-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-danger-lt-rgb),var(--tblr-bg-opacity))!important}.border-danger{border-color:#d63939!important}.bg-light{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-light-rgb),var(--tblr-bg-opacity))!important}.bg-light-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-light-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-light-lt-rgb),var(--tblr-bg-opacity))!important}.border-light{border-color:#fcfdfe!important}.bg-dark{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-dark-rgb),var(--tblr-bg-opacity))!important}.bg-dark-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-dark-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-dark-lt-rgb),var(--tblr-bg-opacity))!important}.border-dark{border-color:#182433!important}.bg-muted{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-muted-rgb),var(--tblr-bg-opacity))!important}.bg-muted-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-muted-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-muted-lt-rgb),var(--tblr-bg-opacity))!important}.border-muted{border-color:#667382!important}.bg-blue{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-blue-rgb),var(--tblr-bg-opacity))!important}.bg-blue-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-blue-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-blue-lt-rgb),var(--tblr-bg-opacity))!important}.border-blue{border-color:#0054a6!important}.bg-azure{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-azure-rgb),var(--tblr-bg-opacity))!important}.bg-azure-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-azure-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-azure-lt-rgb),var(--tblr-bg-opacity))!important}.border-azure{border-color:#4299e1!important}.bg-indigo{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-indigo-rgb),var(--tblr-bg-opacity))!important}.bg-indigo-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-indigo-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-indigo-lt-rgb),var(--tblr-bg-opacity))!important}.border-indigo{border-color:#4263eb!important}.bg-purple{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-purple-rgb),var(--tblr-bg-opacity))!important}.bg-purple-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-purple-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-purple-lt-rgb),var(--tblr-bg-opacity))!important}.border-purple{border-color:#ae3ec9!important}.bg-pink{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-pink-rgb),var(--tblr-bg-opacity))!important}.bg-pink-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-pink-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-pink-lt-rgb),var(--tblr-bg-opacity))!important}.border-pink{border-color:#d6336c!important}.bg-red{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-red-rgb),var(--tblr-bg-opacity))!important}.bg-red-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-red-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-red-lt-rgb),var(--tblr-bg-opacity))!important}.border-red{border-color:#d63939!important}.bg-orange{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-orange-rgb),var(--tblr-bg-opacity))!important}.bg-orange-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-orange-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-orange-lt-rgb),var(--tblr-bg-opacity))!important}.border-orange{border-color:#f76707!important}.bg-yellow{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-yellow-rgb),var(--tblr-bg-opacity))!important}.bg-yellow-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-yellow-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-yellow-lt-rgb),var(--tblr-bg-opacity))!important}.border-yellow{border-color:#f59f00!important}.bg-lime{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-lime-rgb),var(--tblr-bg-opacity))!important}.bg-lime-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-lime-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-lime-lt-rgb),var(--tblr-bg-opacity))!important}.border-lime{border-color:#74b816!important}.bg-green{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-green-rgb),var(--tblr-bg-opacity))!important}.bg-green-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-green-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-green-lt-rgb),var(--tblr-bg-opacity))!important}.border-green{border-color:#2fb344!important}.bg-teal{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-teal-rgb),var(--tblr-bg-opacity))!important}.bg-teal-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-teal-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-teal-lt-rgb),var(--tblr-bg-opacity))!important}.border-teal{border-color:#0ca678!important}.bg-cyan{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-cyan-rgb),var(--tblr-bg-opacity))!important}.bg-cyan-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-cyan-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-cyan-lt-rgb),var(--tblr-bg-opacity))!important}.border-cyan{border-color:#17a2b8!important}.bg-facebook{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-facebook-rgb),var(--tblr-bg-opacity))!important}.bg-facebook-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-facebook-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-facebook-lt-rgb),var(--tblr-bg-opacity))!important}.border-facebook{border-color:#1877f2!important}.bg-twitter{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-twitter-rgb),var(--tblr-bg-opacity))!important}.bg-twitter-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-twitter-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-twitter-lt-rgb),var(--tblr-bg-opacity))!important}.border-twitter{border-color:#1da1f2!important}.bg-linkedin{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-linkedin-rgb),var(--tblr-bg-opacity))!important}.bg-linkedin-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-linkedin-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-linkedin-lt-rgb),var(--tblr-bg-opacity))!important}.border-linkedin{border-color:#0a66c2!important}.bg-google{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-google-rgb),var(--tblr-bg-opacity))!important}.bg-google-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-google-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-google-lt-rgb),var(--tblr-bg-opacity))!important}.border-google{border-color:#dc4e41!important}.bg-youtube{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-youtube-rgb),var(--tblr-bg-opacity))!important}.bg-youtube-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-youtube-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-youtube-lt-rgb),var(--tblr-bg-opacity))!important}.border-youtube{border-color:red!important}.bg-vimeo{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-vimeo-rgb),var(--tblr-bg-opacity))!important}.bg-vimeo-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-vimeo-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-vimeo-lt-rgb),var(--tblr-bg-opacity))!important}.border-vimeo{border-color:#1ab7ea!important}.bg-dribbble{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-dribbble-rgb),var(--tblr-bg-opacity))!important}.bg-dribbble-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-dribbble-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-dribbble-lt-rgb),var(--tblr-bg-opacity))!important}.border-dribbble{border-color:#ea4c89!important}.bg-github{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-github-rgb),var(--tblr-bg-opacity))!important}.bg-github-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-github-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-github-lt-rgb),var(--tblr-bg-opacity))!important}.border-github{border-color:#181717!important}.bg-instagram{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-instagram-rgb),var(--tblr-bg-opacity))!important}.bg-instagram-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-instagram-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-instagram-lt-rgb),var(--tblr-bg-opacity))!important}.border-instagram{border-color:#e4405f!important}.bg-pinterest{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-pinterest-rgb),var(--tblr-bg-opacity))!important}.bg-pinterest-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-pinterest-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-pinterest-lt-rgb),var(--tblr-bg-opacity))!important}.border-pinterest{border-color:#bd081c!important}.bg-vk{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-vk-rgb),var(--tblr-bg-opacity))!important}.bg-vk-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-vk-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-vk-lt-rgb),var(--tblr-bg-opacity))!important}.border-vk{border-color:#6383a8!important}.bg-rss{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-rss-rgb),var(--tblr-bg-opacity))!important}.bg-rss-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-rss-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-rss-lt-rgb),var(--tblr-bg-opacity))!important}.border-rss{border-color:orange!important}.bg-flickr{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-flickr-rgb),var(--tblr-bg-opacity))!important}.bg-flickr-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-flickr-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-flickr-lt-rgb),var(--tblr-bg-opacity))!important}.border-flickr{border-color:#0063dc!important}.bg-bitbucket{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-bg-opacity))!important}.bg-bitbucket-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-bitbucket-lt-rgb),var(--tblr-bg-opacity))!important}.border-bitbucket{border-color:#0052cc!important}.bg-tabler{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-tabler-rgb),var(--tblr-bg-opacity))!important}.bg-tabler-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-tabler-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-tabler-lt-rgb),var(--tblr-bg-opacity))!important}.border-tabler{border-color:#0054a6!important}.bg-white{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-white-rgb),var(--tblr-bg-opacity))!important}.bg-white-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-white-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-white-lt-rgb),var(--tblr-bg-opacity))!important}.border-white{border-color:#fff!important}.text-primary{--tblr-text-opacity: 1;color:rgba(var(--tblr-primary-rgb),var(--tblr-text-opacity))!important}.text-primary-fg{color:var(--tblr-primary-fg)!important}.text-secondary{--tblr-text-opacity: 1;color:rgba(var(--tblr-secondary-rgb),var(--tblr-text-opacity))!important}.text-secondary-fg{color:var(--tblr-secondary-fg)!important}.text-success{--tblr-text-opacity: 1;color:rgba(var(--tblr-success-rgb),var(--tblr-text-opacity))!important}.text-success-fg{color:var(--tblr-success-fg)!important}.text-info{--tblr-text-opacity: 1;color:rgba(var(--tblr-info-rgb),var(--tblr-text-opacity))!important}.text-info-fg{color:var(--tblr-info-fg)!important}.text-warning{--tblr-text-opacity: 1;color:rgba(var(--tblr-warning-rgb),var(--tblr-text-opacity))!important}.text-warning-fg{color:var(--tblr-warning-fg)!important}.text-danger{--tblr-text-opacity: 1;color:rgba(var(--tblr-danger-rgb),var(--tblr-text-opacity))!important}.text-danger-fg{color:var(--tblr-danger-fg)!important}.text-light{--tblr-text-opacity: 1;color:rgba(var(--tblr-light-rgb),var(--tblr-text-opacity))!important}.text-light-fg{color:var(--tblr-light-fg)!important}.text-dark{--tblr-text-opacity: 1;color:rgba(var(--tblr-dark-rgb),var(--tblr-text-opacity))!important}.text-dark-fg{color:var(--tblr-dark-fg)!important}.text-muted{--tblr-text-opacity: 1;color:rgba(var(--tblr-muted-rgb),var(--tblr-text-opacity))!important}.text-muted-fg{color:var(--tblr-muted-fg)!important}.text-blue{--tblr-text-opacity: 1;color:rgba(var(--tblr-blue-rgb),var(--tblr-text-opacity))!important}.text-blue-fg{color:var(--tblr-blue-fg)!important}.text-azure{--tblr-text-opacity: 1;color:rgba(var(--tblr-azure-rgb),var(--tblr-text-opacity))!important}.text-azure-fg{color:var(--tblr-azure-fg)!important}.text-indigo{--tblr-text-opacity: 1;color:rgba(var(--tblr-indigo-rgb),var(--tblr-text-opacity))!important}.text-indigo-fg{color:var(--tblr-indigo-fg)!important}.text-purple{--tblr-text-opacity: 1;color:rgba(var(--tblr-purple-rgb),var(--tblr-text-opacity))!important}.text-purple-fg{color:var(--tblr-purple-fg)!important}.text-pink{--tblr-text-opacity: 1;color:rgba(var(--tblr-pink-rgb),var(--tblr-text-opacity))!important}.text-pink-fg{color:var(--tblr-pink-fg)!important}.text-red{--tblr-text-opacity: 1;color:rgba(var(--tblr-red-rgb),var(--tblr-text-opacity))!important}.text-red-fg{color:var(--tblr-red-fg)!important}.text-orange{--tblr-text-opacity: 1;color:rgba(var(--tblr-orange-rgb),var(--tblr-text-opacity))!important}.text-orange-fg{color:var(--tblr-orange-fg)!important}.text-yellow{--tblr-text-opacity: 1;color:rgba(var(--tblr-yellow-rgb),var(--tblr-text-opacity))!important}.text-yellow-fg{color:var(--tblr-yellow-fg)!important}.text-lime{--tblr-text-opacity: 1;color:rgba(var(--tblr-lime-rgb),var(--tblr-text-opacity))!important}.text-lime-fg{color:var(--tblr-lime-fg)!important}.text-green{--tblr-text-opacity: 1;color:rgba(var(--tblr-green-rgb),var(--tblr-text-opacity))!important}.text-green-fg{color:var(--tblr-green-fg)!important}.text-teal{--tblr-text-opacity: 1;color:rgba(var(--tblr-teal-rgb),var(--tblr-text-opacity))!important}.text-teal-fg{color:var(--tblr-teal-fg)!important}.text-cyan{--tblr-text-opacity: 1;color:rgba(var(--tblr-cyan-rgb),var(--tblr-text-opacity))!important}.text-cyan-fg{color:var(--tblr-cyan-fg)!important}.text-facebook{--tblr-text-opacity: 1;color:rgba(var(--tblr-facebook-rgb),var(--tblr-text-opacity))!important}.text-facebook-fg{color:var(--tblr-facebook-fg)!important}.text-twitter{--tblr-text-opacity: 1;color:rgba(var(--tblr-twitter-rgb),var(--tblr-text-opacity))!important}.text-twitter-fg{color:var(--tblr-twitter-fg)!important}.text-linkedin{--tblr-text-opacity: 1;color:rgba(var(--tblr-linkedin-rgb),var(--tblr-text-opacity))!important}.text-linkedin-fg{color:var(--tblr-linkedin-fg)!important}.text-google{--tblr-text-opacity: 1;color:rgba(var(--tblr-google-rgb),var(--tblr-text-opacity))!important}.text-google-fg{color:var(--tblr-google-fg)!important}.text-youtube{--tblr-text-opacity: 1;color:rgba(var(--tblr-youtube-rgb),var(--tblr-text-opacity))!important}.text-youtube-fg{color:var(--tblr-youtube-fg)!important}.text-vimeo{--tblr-text-opacity: 1;color:rgba(var(--tblr-vimeo-rgb),var(--tblr-text-opacity))!important}.text-vimeo-fg{color:var(--tblr-vimeo-fg)!important}.text-dribbble{--tblr-text-opacity: 1;color:rgba(var(--tblr-dribbble-rgb),var(--tblr-text-opacity))!important}.text-dribbble-fg{color:var(--tblr-dribbble-fg)!important}.text-github{--tblr-text-opacity: 1;color:rgba(var(--tblr-github-rgb),var(--tblr-text-opacity))!important}.text-github-fg{color:var(--tblr-github-fg)!important}.text-instagram{--tblr-text-opacity: 1;color:rgba(var(--tblr-instagram-rgb),var(--tblr-text-opacity))!important}.text-instagram-fg{color:var(--tblr-instagram-fg)!important}.text-pinterest{--tblr-text-opacity: 1;color:rgba(var(--tblr-pinterest-rgb),var(--tblr-text-opacity))!important}.text-pinterest-fg{color:var(--tblr-pinterest-fg)!important}.text-vk{--tblr-text-opacity: 1;color:rgba(var(--tblr-vk-rgb),var(--tblr-text-opacity))!important}.text-vk-fg{color:var(--tblr-vk-fg)!important}.text-rss{--tblr-text-opacity: 1;color:rgba(var(--tblr-rss-rgb),var(--tblr-text-opacity))!important}.text-rss-fg{color:var(--tblr-rss-fg)!important}.text-flickr{--tblr-text-opacity: 1;color:rgba(var(--tblr-flickr-rgb),var(--tblr-text-opacity))!important}.text-flickr-fg{color:var(--tblr-flickr-fg)!important}.text-bitbucket{--tblr-text-opacity: 1;color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-text-opacity))!important}.text-bitbucket-fg{color:var(--tblr-bitbucket-fg)!important}.text-tabler{--tblr-text-opacity: 1;color:rgba(var(--tblr-tabler-rgb),var(--tblr-text-opacity))!important}.text-tabler-fg{color:var(--tblr-tabler-fg)!important}.bg-gray-50{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-50-rgb),var(--tblr-bg-opacity))!important}.text-gray-50-fg{color:#182433!important}.bg-gray-100{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-100-rgb),var(--tblr-bg-opacity))!important}.text-gray-100-fg{color:#182433!important}.bg-gray-200{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-200-rgb),var(--tblr-bg-opacity))!important}.text-gray-200-fg{color:#182433!important}.bg-gray-300{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-300-rgb),var(--tblr-bg-opacity))!important}.text-gray-300-fg{color:#182433!important}.bg-gray-400{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-400-rgb),var(--tblr-bg-opacity))!important}.text-gray-400-fg{color:#fcfdfe!important}.bg-gray-500{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-500-rgb),var(--tblr-bg-opacity))!important}.text-gray-500-fg{color:#fcfdfe!important}.bg-gray-600{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-600-rgb),var(--tblr-bg-opacity))!important}.text-gray-600-fg{color:#fcfdfe!important}.bg-gray-700{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-700-rgb),var(--tblr-bg-opacity))!important}.text-gray-700-fg{color:#fcfdfe!important}.bg-gray-800{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-800-rgb),var(--tblr-bg-opacity))!important}.text-gray-800-fg{color:#fcfdfe!important}.bg-gray-900{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-900-rgb),var(--tblr-bg-opacity))!important}.text-gray-900-fg{color:#fcfdfe!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:3rem!important}.h-6{height:3rem!important}.w-7{width:5rem!important}.h-7{height:5rem!important}.w-8{width:8rem!important}.h-8{height:8rem!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:.35!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:.7!important}.opacity-75{opacity:.75!important}.opacity-80{opacity:.8!important}.opacity-85{opacity:.85!important}.opacity-90{opacity:.9!important}.opacity-95{opacity:.95!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 0!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}.ts-control{border:1px solid var(--tblr-border-color);padding:.5625rem .75rem;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(0.5625rem - 1px + -0) .75rem calc(0.5625rem - 4px + -0)}.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:#182433;border:0 solid #dadfe5}.ts-wrapper.multi .ts-control>div.active{background:#00857D;color:#fff;border:0 solid rgba(0,0,0,0)}.ts-wrapper.multi.disabled .ts-control>div,.ts-wrapper.multi.disabled .ts-control>div.active{color:#727272;background:white;border:0 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(0.75rem + 5px);right:auto}.ts-control.rtl .ts-control>input{margin:0 4px 0 -2px!important}.disabled .ts-control{opacity:.5;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:#fff;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:rgba(255,237,40,.4);border-radius:1px}.ts-dropdown .option,.ts-dropdown .optgroup-header,.ts-dropdown .no-results,.ts-dropdown .create{padding:3px .75rem}.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:#667382;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:#18243380}.ts-dropdown .spinner{display:inline-block;width:30px;height:30px;margin:3px .75rem}.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(0.75rem - 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),.75rem)}.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 .75rem;border-bottom:1px solid #d0d0d0;background:color-mix(#fff,#d0d0d0,85%);border-radius:var(--tblr-border-radius) var(--tblr-border-radius) 0 0}.ts-wrapper .dropdown-header-close{position:absolute;right:.75rem;top:50%;color:#182433;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-box-shadow-input)}.plugin-dropdown_input .dropdown-input{border:1px solid #d0d0d0;border-width:0 0 1px;display:block;padding:.5625rem .75rem;box-shadow:none;width:100%;background:transparent}.plugin-dropdown_input.focus .ts-dropdown .dropdown-input{border-color:#80c2be;outline:0;box-shadow:var(--tblr-box-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:rgba(0,0,0,.05)}.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 #dadfe5;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 #dadfe5;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: 0;--ts-pr-caret: 0;--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:#182433;font-family:inherit;font-size:inherit;line-height:1.4285714286}.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:#fff;border:1px solid var(--tblr-border-color-translucent);border-radius:4px;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) 0;overflow:hidden;border-top:1px solid var(--tblr-border-color-translucent);margin-left:-.75rem;margin-right:-.75rem}.ts-dropdown .create{padding-left:.75rem}.ts-dropdown-content{padding:5px 0}.ts-control{box-shadow:var(--tblr-box-shadow-input);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;display:flex;align-items:center}@media (prefers-reduced-motion: reduce){.ts-control{transition:none}}.focus .ts-control{border-color:#80c2be;outline:0;box-shadow:var(--tblr-box-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.ts-control .item{display:flex;align-items:center}.ts-wrapper.is-invalid,.was-validated .invalid,.was-validated :invalid+.ts-wrapper{border-color:var(--tblr-form-invalid-color)}.ts-wrapper.is-invalid: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 calc(0.35714em + 0.28125rem) center;background-size:calc(0.71429em + 0.5625rem) calc(0.71429em + 0.5625rem);background-repeat:no-repeat}.ts-wrapper.is-invalid.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='%23929dab' 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 .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.71429em + 0.5625rem) calc(0.71429em + 0.5625rem);background-repeat:no-repeat}.ts-wrapper.is-invalid.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 calc(0.35714em + 0.28125rem) center;background-size:calc(0.71429em + 0.5625rem) calc(0.71429em + 0.5625rem);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='%23929dab' 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 .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.71429em + 0.5625rem) calc(0.71429em + 0.5625rem);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.4285714286em + 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.4285714286em + .25rem + 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;padding-bottom:0}.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.4285714286em + .25rem + calc(var(--tblr-border-width) * 2)) - 1.4285714286 * .75rem - 4px) / 2)!important}.ts-wrapper.multi.has-items .ts-control{padding-left:calc(0.75rem - 5px);--ts-pr-min:calc(0.75rem - 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.4285714286em + 1rem + 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:1.25rem}.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='%23929dab' 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 .75rem center;background-size:16px 12px}.ts-wrapper.form-select,.ts-wrapper.single{--ts-pr-caret: 2.25rem}.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}:root{--ts-pr-clear-button: 0rem;--ts-pr-caret: 0rem}.ts-input,.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-box-shadow-input)}.ts-wrapper.is-invalid .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-box-shadow-dropdown)}.ts-dropdown .option{padding:.5rem .75rem}.ts-control,.ts-control input{color:var(--tblr-body-color)}.ts-control input::placeholder{color:#929dab}.ts-wrapper.multi .ts-control>div{background:var(--tblr-bg-surface-secondary);border:1px solid var(--tblr-border-color);color:var(--tblr-body-color)}html{scroll-behavior:auto!important}.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}pre{background-color:transparent;color:inherit}.alert{background:var(--tblr-bg-surface)}.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:4px}.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}.page-body .card .card-header{background:var(--tblr-bg-surface-secondary)!important}[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: #bbc3cd;--tblr-primary: #00F2D4;--tblr-primary-fg: #001423;--tblr-primary-rgb: 0,242,212;--tblr-btn-active-color: #001423}body[data-bs-theme=dark],body[data-bs-theme=dark] body[data-bs-theme=light]{background-color:#001423}body[data-bs-theme=dark] ::selection,body[data-bs-theme=dark] body[data-bs-theme=light] ::selection{background-color:rgba(var(--tblr-primary-rgb),.48)}body[data-bs-theme=dark] .btn-primary,body[data-bs-theme=dark] .bg-primary .card-title,body[data-bs-theme=dark] .bg-primary a,body[data-bs-theme=dark] .bg-primary i,body[data-bs-theme=dark] .text-bg-primary{color:#001423!important}body[data-bs-theme=dark] .card{background:#001423!important}body[data-bs-theme=dark] .navbar,body[data-bs-theme=dark] .page-header{background-color:#001423}body[data-bs-theme=dark] .page,body[data-bs-theme=dark] .page-tabs .nav-tabs .nav-link.active{background-color:#081b2a!important}body[data-bs-theme=dark] .page-link.active,body[data-bs-theme=dark] .active>.page-link{color:#001423}body[data-bs-theme=dark] .text-bg-primary{color:#001423!important}body[data-bs-theme=dark] .text-muted{color:var(--tblr-secondary-color)!important}body[data-bs-theme=dark] .text-secondary{color:#bbc3cd!important}body[data-bs-theme=dark] .footer .text-primary{color:#fff!important}body[data-bs-theme=dark] .toast{color:var(--tblr-body-color)}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}.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}.page{background-color:var(--tblr-bg-surface-secondary)}.page-header{background-color:var(--tblr-bg-surface);min-height:0}@media (min-width: 992px){.navbar-vertical.navbar-expand-lg{padding-bottom:2rem}}.navbar-vertical.navbar-expand-lg .navbar-collapse .nav-link-icon{color:var(--tblr-nav-link-color)!important}.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{background-color:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:hover a{text-decoration:none}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:hover .btn-group{visibility:visible}.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.active a{color:#001423}.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,rgba(0,0,0,0) 0%,rgba(0,0,0,.3) 100%);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}}body[data-bs-theme=light] .navbar-vertical.navbar-expand-lg{background:linear-gradient(180deg,rgba(0,133,125,0) 0%,rgba(0,133,125,.1) 100%),#FFF}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg{background:linear-gradient(180deg,rgba(0,242,212,0) 0%,rgba(0,242,212,.1) 100%),#001423}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .nav-item.dropdown.active:after{border-color:#00f2d4!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item a{color:#fff!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item.active{background-color:#ffffff0f!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item.active a{color:#fff!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item:hover{background-color:#ffffff0f!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item .nav-link-title{color:#fff!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .text-secondary{color:#00f2d4!important}body[data-bs-theme=dark] .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.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)}body[data-bs-theme=dark] .table thead th,body[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}pre.change-data>span{display:block;padding-right:1rem;padding-left:1rem}pre.change-data>span.added{background-color:#2fb344}pre.change-data>span.removed{background-color:#d63939}pre.change-diff{border-color:transparent}pre.change-diff.change-added{background-color:#2fb344}pre.change-diff.change-removed{background-color:#d63939}pre.block{padding:1rem;border:1px solid #dadfe5;border-radius:4px}.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:#0054a626}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:#d6393926}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}td>.rendered-markdown{max-height:200px;overflow-y:scroll}td>.rendered-markdown p:last-of-type{margin-bottom:0}.markdown-widget .preview{border:1px solid #dadfe5;border-radius:4px;min-height:200px}span.color-label{display:inline-block;width:5rem;height:1rem;padding:.25em .5em;border:1px solid #303030;border-radius:4px}.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}.column-filter{position:static;display:inline}.column-filter.dropdown>.dropdown-toggle>.mdi-filter-settings{font-size:.625rem}.column-filter.dropdown>.dropdown-menu{max-width:300px}.column-filter.dropdown-toggle:after{content:none}.netbox-edition{letter-spacing:.15rem}.btn-float-group,.btn-float-group-right,.btn-float-group-left{position:sticky;bottom:10px;z-index:2}.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}tr[data-read=True] td{background-color:var(--tblr-bg-surface-secondary);color:var(--tblr-secondary-color)} +@charset "UTF-8";:root,[data-bs-theme=light]{--tblr-black: #000000;--tblr-white: #ffffff;--tblr-gray: #49566c;--tblr-gray-dark: #182433;--tblr-gray-100: #eef3f6;--tblr-gray-200: #dce1e7;--tblr-gray-300: #b8c4d4;--tblr-gray-400: #8a97ab;--tblr-gray-500: #6c7a91;--tblr-gray-600: #49566c;--tblr-gray-700: #3a4859;--tblr-gray-800: #182433;--tblr-gray-900: #040a11;--tblr-primary: #00857D;--tblr-secondary: #6c7a91;--tblr-success: #2fb344;--tblr-info: #4299e1;--tblr-warning: #f76707;--tblr-danger: #d63939;--tblr-light: #f6f8fb;--tblr-dark: #182433;--tblr-muted: #6c7a91;--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-x: #000000;--tblr-facebook: #1877f2;--tblr-twitter: #1da1f2;--tblr-linkedin: #0a66c2;--tblr-google: #dc4e41;--tblr-youtube: #ff0000;--tblr-vimeo: #1ab7ea;--tblr-dribbble: #ea4c89;--tblr-github: #181717;--tblr-instagram: #e4405f;--tblr-pinterest: #bd081c;--tblr-vk: #6383a8;--tblr-rss: #ffa500;--tblr-flickr: #0063dc;--tblr-bitbucket: #0052cc;--tblr-tabler: #066fd1;--tblr-primary-rgb: 0, 133, 125;--tblr-secondary-rgb: 108, 122, 145;--tblr-success-rgb: 47, 179, 68;--tblr-info-rgb: 66, 153, 225;--tblr-warning-rgb: 247, 103, 7;--tblr-danger-rgb: 214, 57, 57;--tblr-light-rgb: 246, 248, 251;--tblr-dark-rgb: 24, 36, 51;--tblr-muted-rgb: 108, 122, 145;--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-x-rgb: 0, 0, 0;--tblr-facebook-rgb: 24, 119, 242;--tblr-twitter-rgb: 29, 161, 242;--tblr-linkedin-rgb: 10, 102, 194;--tblr-google-rgb: 220, 78, 65;--tblr-youtube-rgb: 255, 0, 0;--tblr-vimeo-rgb: 26, 183, 234;--tblr-dribbble-rgb: 234, 76, 137;--tblr-github-rgb: 24, 23, 23;--tblr-instagram-rgb: 228, 64, 95;--tblr-pinterest-rgb: 189, 8, 28;--tblr-vk-rgb: 99, 131, 168;--tblr-rss-rgb: 255, 165, 0;--tblr-flickr-rgb: 0, 99, 220;--tblr-bitbucket-rgb: 0, 82, 204;--tblr-tabler-rgb: 6, 111, 209;--tblr-primary-text-emphasis: #003532;--tblr-secondary-text-emphasis: #2b313a;--tblr-success-text-emphasis: #13481b;--tblr-info-text-emphasis: #1a3d5a;--tblr-warning-text-emphasis: #632903;--tblr-danger-text-emphasis: #561717;--tblr-light-text-emphasis: #3a4859;--tblr-dark-text-emphasis: #3a4859;--tblr-primary-bg-subtle: #cce7e5;--tblr-secondary-bg-subtle: #e2e4e9;--tblr-success-bg-subtle: #d5f0da;--tblr-info-bg-subtle: #d9ebf9;--tblr-warning-bg-subtle: #fde1cd;--tblr-danger-bg-subtle: #f7d7d7;--tblr-light-bg-subtle: #f7f9fb;--tblr-dark-bg-subtle: #8a97ab;--tblr-primary-border-subtle: #99cecb;--tblr-secondary-border-subtle: #c4cad3;--tblr-success-border-subtle: #ace1b4;--tblr-info-border-subtle: #b3d6f3;--tblr-warning-border-subtle: #fcc29c;--tblr-danger-border-subtle: #efb0b0;--tblr-light-border-subtle: #dce1e7;--tblr-dark-border-subtle: #6c7a91;--tblr-white-rgb: 255, 255, 255;--tblr-black-rgb: 0, 0, 0;--tblr-font-sans-serif: "Inter", system-ui, sans-serif;--tblr-font-monospace: Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--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: #182433;--tblr-body-color-rgb: 24, 36, 51;--tblr-body-bg: #f6f8fb;--tblr-body-bg-rgb: 246, 248, 251;--tblr-emphasis-color: #3a4859;--tblr-emphasis-color-rgb: 58, 72, 89;--tblr-secondary-color: rgba(24, 36, 51, .75);--tblr-secondary-color-rgb: 24, 36, 51;--tblr-secondary-bg: #dce1e7;--tblr-secondary-bg-rgb: 220, 225, 231;--tblr-tertiary-color: rgba(24, 36, 51, .5);--tblr-tertiary-color-rgb: 24, 36, 51;--tblr-tertiary-bg: #eef3f6;--tblr-tertiary-bg-rgb: 238, 243, 246;--tblr-heading-color: inherit;--tblr-link-color: #00857D;--tblr-link-color-rgb: 0, 133, 125;--tblr-link-decoration: none;--tblr-link-hover-color: #006a64;--tblr-link-hover-color-rgb: 0, 106, 100;--tblr-link-hover-decoration: underline;--tblr-code-color: var(--tblr-gray-600);--tblr-highlight-color: #182433;--tblr-highlight-bg: #fdeccc;--tblr-border-width: 1px;--tblr-border-style: solid;--tblr-border-color: #dce1e7;--tblr-border-color-translucent: rgba(4, 32, 69, .1);--tblr-border-radius: 4px;--tblr-border-radius-sm: 2px;--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: #dce1e7;--tblr-body-color-rgb: 220, 225, 231;--tblr-body-bg: #040a11;--tblr-body-bg-rgb: 4, 10, 17;--tblr-emphasis-color: #ffffff;--tblr-emphasis-color-rgb: 255, 255, 255;--tblr-secondary-color: rgba(220, 225, 231, .75);--tblr-secondary-color-rgb: 220, 225, 231;--tblr-secondary-bg: #182433;--tblr-secondary-bg-rgb: 24, 36, 51;--tblr-tertiary-color: rgba(220, 225, 231, .5);--tblr-tertiary-color-rgb: 220, 225, 231;--tblr-tertiary-bg: #0e1722;--tblr-tertiary-bg-rgb: 14, 23, 34;--tblr-primary-text-emphasis: #66b6b1;--tblr-secondary-text-emphasis: #a7afbd;--tblr-success-text-emphasis: #82d18f;--tblr-info-text-emphasis: #8ec2ed;--tblr-warning-text-emphasis: #faa46a;--tblr-danger-text-emphasis: #e68888;--tblr-light-text-emphasis: #eef3f6;--tblr-dark-text-emphasis: #b8c4d4;--tblr-primary-bg-subtle: #001b19;--tblr-secondary-bg-subtle: #16181d;--tblr-success-bg-subtle: #09240e;--tblr-info-bg-subtle: #0d1f2d;--tblr-warning-bg-subtle: #311501;--tblr-danger-bg-subtle: #2b0b0b;--tblr-light-bg-subtle: #182433;--tblr-dark-bg-subtle: #0c121a;--tblr-primary-border-subtle: #00504b;--tblr-secondary-border-subtle: #414957;--tblr-success-border-subtle: #1c6b29;--tblr-info-border-subtle: #285c87;--tblr-warning-border-subtle: #943e04;--tblr-danger-border-subtle: #802222;--tblr-light-border-subtle: #3a4859;--tblr-dark-border-subtle: #182433;--tblr-heading-color: inherit;--tblr-link-color: #66b6b1;--tblr-link-hover-color: #85c5c1;--tblr-link-color-rgb: 102, 182, 177;--tblr-link-hover-color-rgb: 133, 197, 193;--tblr-code-color: var(--tblr-gray-300);--tblr-highlight-color: #dce1e7;--tblr-highlight-bg: #624000;--tblr-border-color: #25384f;--tblr-border-color-translucent: rgba(72, 110, 149, .14);--tblr-form-valid-color: #82d18f;--tblr-form-valid-border-color: #82d18f;--tblr-form-invalid-color: #e68888;--tblr-form-invalid-border-color: #e68888}*,*: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:85.714285%}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:85.714285%;color:var(--tblr-light)}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:85.714285%;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:2px}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;font-size:1.5rem;line-height:inherit}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}::-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-size:5rem;font-weight:300;line-height:1.2}.display-2{font-size:4.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}.display-5{font-size:3rem;font-weight:300;line-height:1.2}.display-6{font-size:2rem;font-weight:300;line-height:1.2}.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:85.714285%;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:85.714285%;color:#49566c}.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:85.714285%;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: 3rem}.g-6,.gy-6{--tblr-gutter-y: 3rem}.g-7,.gx-7{--tblr-gutter-x: 5rem}.g-7,.gy-7{--tblr-gutter-y: 5rem}.g-8,.gx-8{--tblr-gutter-x: 8rem}.g-8,.gy-8{--tblr-gutter-y: 8rem}@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: 3rem}.g-sm-6,.gy-sm-6{--tblr-gutter-y: 3rem}.g-sm-7,.gx-sm-7{--tblr-gutter-x: 5rem}.g-sm-7,.gy-sm-7{--tblr-gutter-y: 5rem}.g-sm-8,.gx-sm-8{--tblr-gutter-x: 8rem}.g-sm-8,.gy-sm-8{--tblr-gutter-y: 8rem}}@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: 3rem}.g-md-6,.gy-md-6{--tblr-gutter-y: 3rem}.g-md-7,.gx-md-7{--tblr-gutter-x: 5rem}.g-md-7,.gy-md-7{--tblr-gutter-y: 5rem}.g-md-8,.gx-md-8{--tblr-gutter-x: 8rem}.g-md-8,.gy-md-8{--tblr-gutter-y: 8rem}}@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: 3rem}.g-lg-6,.gy-lg-6{--tblr-gutter-y: 3rem}.g-lg-7,.gx-lg-7{--tblr-gutter-x: 5rem}.g-lg-7,.gy-lg-7{--tblr-gutter-y: 5rem}.g-lg-8,.gx-lg-8{--tblr-gutter-x: 8rem}.g-lg-8,.gy-lg-8{--tblr-gutter-y: 8rem}}@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: 3rem}.g-xl-6,.gy-xl-6{--tblr-gutter-y: 3rem}.g-xl-7,.gx-xl-7{--tblr-gutter-x: 5rem}.g-xl-7,.gy-xl-7{--tblr-gutter-y: 5rem}.g-xl-8,.gx-xl-8{--tblr-gutter-x: 8rem}.g-xl-8,.gy-xl-8{--tblr-gutter-y: 8rem}}@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: 3rem}.g-xxl-6,.gy-xxl-6{--tblr-gutter-y: 3rem}.g-xxl-7,.gx-xxl-7{--tblr-gutter-x: 5rem}.g-xxl-7,.gy-xxl-7{--tblr-gutter-y: 5rem}.g-xxl-8,.gx-xxl-8{--tblr-gutter-x: 8rem}.g-xxl-8,.gy-xxl-8{--tblr-gutter-y: 8rem}}.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: rgba(var(--tblr-emphasis-color-rgb), .1);--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)>*>*{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: #182433;--tblr-table-bg: #cce7e5;--tblr-table-border-color: #a8c0c1;--tblr-table-striped-bg: #c3dddc;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #bad4d3;--tblr-table-active-color: #182433;--tblr-table-hover-bg: #bfd8d8;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-secondary{--tblr-table-color: #182433;--tblr-table-bg: #e2e4e9;--tblr-table-border-color: #babec5;--tblr-table-striped-bg: #d8dae0;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #ced1d7;--tblr-table-active-color: #182433;--tblr-table-hover-bg: #d3d6db;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-success{--tblr-table-color: #182433;--tblr-table-bg: #d5f0da;--tblr-table-border-color: #afc7b9;--tblr-table-striped-bg: #cce6d2;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #c2dcc9;--tblr-table-active-color: #182433;--tblr-table-hover-bg: #c7e1cd;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-info{--tblr-table-color: #182433;--tblr-table-bg: #d9ebf9;--tblr-table-border-color: #b2c3d1;--tblr-table-striped-bg: #cfe1ef;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #c6d7e5;--tblr-table-active-color: #182433;--tblr-table-hover-bg: #cbdcea;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-warning{--tblr-table-color: #182433;--tblr-table-bg: #fde1cd;--tblr-table-border-color: #cfbbae;--tblr-table-striped-bg: #f2d8c5;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #e6cebe;--tblr-table-active-color: #182433;--tblr-table-hover-bg: #ecd3c1;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-danger{--tblr-table-color: #182433;--tblr-table-bg: #f7d7d7;--tblr-table-border-color: #cab3b6;--tblr-table-striped-bg: #eccecf;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #e1c5c7;--tblr-table-active-color: #f6f8fb;--tblr-table-hover-bg: #e6cacb;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-light{--tblr-table-color: #182433;--tblr-table-bg: #f6f8fb;--tblr-table-border-color: #caced3;--tblr-table-striped-bg: #ebedf1;--tblr-table-striped-color: #182433;--tblr-table-active-bg: #e0e3e7;--tblr-table-active-color: #182433;--tblr-table-hover-bg: #e5e8ec;--tblr-table-hover-color: #182433;color:var(--tblr-table-color);border-color:var(--tblr-table-border-color)}.table-dark{--tblr-table-color: #f6f8fb;--tblr-table-bg: #182433;--tblr-table-border-color: #444e5b;--tblr-table-striped-bg: #232f3d;--tblr-table-striped-color: #f6f8fb;--tblr-table-active-bg: #2e3947;--tblr-table-active-color: #f6f8fb;--tblr-table-hover-bg: #293442;--tblr-table-hover-color: #f6f8fb;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.4285714286}.col-form-label-lg{padding-top:calc(.5rem + var(--tblr-border-width));padding-bottom:calc(.5rem + var(--tblr-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.125rem + var(--tblr-border-width));padding-bottom:calc(.125rem + var(--tblr-border-width));font-size:.75rem}.form-text{margin-top:.25rem;font-size:85.714285%;color:var(--tblr-secondary-color)}.form-control{display:block;width:100%;padding:.5625rem .75rem;font-family:var(--tblr-font-sans-serif);font-size:.875rem;font-weight:400;line-height:1.4285714286;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-box-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-box-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.4285714286em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::placeholder{color:#8a97ab;opacity:1}.form-control:disabled{background-color:var(--tblr-bg-surface-secondary);opacity:1}.form-control::file-selector-button{padding:.5625rem .75rem;margin:-.5625rem -.75rem;margin-inline-end:.75rem;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.4285714286;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.4285714286em + .25rem + calc(var(--tblr-border-width) * 2));padding:.125rem .25rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.form-control-sm::file-selector-button{padding:.125rem .25rem;margin:-.125rem -.25rem;margin-inline-end:.25rem}.form-control-lg{min-height:calc(1.4285714286em + 1rem + calc(var(--tblr-border-width) * 2));padding:.5rem .75rem;font-size:1.25rem;border-radius:var(--tblr-border-radius-lg)}.form-control-lg::file-selector-button{padding:.5rem .75rem;margin:-.5rem -.75rem;margin-inline-end:.75rem}textarea.form-control{min-height:calc(1.4285714286em + 1.125rem + calc(var(--tblr-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.4285714286em + .25rem + calc(var(--tblr-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.4285714286em + 1rem + calc(var(--tblr-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.4285714286em + 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.4285714286em + .25rem + calc(var(--tblr-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.4285714286em + 1rem + 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='%238a97ab' 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 2.25rem .5625rem .75rem;font-family:var(--tblr-font-sans-serif);font-size:.875rem;font-weight:400;line-height:1.4285714286;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 .75rem 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-box-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-box-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;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:.125rem;padding-bottom:.125rem;padding-left:.25rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:.75rem;font-size:1.25rem;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='%23dce1e7' 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='%23dce1e7'/%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='%2380c2be'/%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 #f6f8fb,0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #f6f8fb,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;height:100%;padding:1rem .75rem;overflow:hidden;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 .75rem}.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}.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{color:rgba(var(--tblr-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translate(.15rem)}.form-floating>.form-control:focus~label:after,.form-floating>.form-control:not(:placeholder-shown)~label:after,.form-floating>.form-control-plaintext~label:after,.form-floating>.form-select~label:after{position:absolute;inset:1rem .375rem;z-index:-1;height:1.5em;content:"";background-color:var(--tblr-bg-forms);border-radius:var(--tblr-border-radius)}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--tblr-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translate(.15rem)}.form-floating>.form-control-plaintext~label{border-width:var(--tblr-border-width) 0}.form-floating>:disabled~label,.form-floating>.form-control:disabled~label{color:#49566c}.form-floating>:disabled~label:after,.form-floating>.form-control:disabled~label:after{background-color:var(--tblr-bg-surface-secondary)}.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 .75rem;font-size:.875rem;font-weight:400;line-height:1.4285714286;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:.5rem .75rem;font-size:1.25rem;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:.125rem .25rem;font-size:.75rem;border-radius:var(--tblr-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.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(var(--tblr-border-width) * -1);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:85.714285%;color:var(--tblr-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:var(--tblr-spacer-2) var(--tblr-spacer-2);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:calc(1.4285714286em + 1.125rem);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 calc(.3571428572em + .28125rem) center;background-size:calc(.7142857143em + .5625rem) calc(.7142857143em + .5625rem)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:var(--tblr-form-valid-border-color);box-shadow:var(--tblr-box-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:calc(1.4285714286em + 1.125rem);background-position:top calc(.3571428572em + .28125rem) right calc(.3571428572em + .28125rem)}.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:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.7142857143em + .5625rem) calc(.7142857143em + .5625rem)}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:var(--tblr-form-valid-border-color);box-shadow:var(--tblr-box-shadow-input),0 0 0 .25rem rgba(var(--tblr-success-rgb),.25)}.was-validated .form-control-color:valid,.form-control-color.is-valid{width:calc(3rem + calc(1.4285714286em + 1.125rem))}.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:85.714285%;color:var(--tblr-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:var(--tblr-spacer-2) var(--tblr-spacer-2);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,.is-invalid~.invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:var(--tblr-form-invalid-border-color);padding-right:calc(1.4285714286em + 1.125rem);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 calc(.3571428572em + .28125rem) center;background-size:calc(.7142857143em + .5625rem) calc(.7142857143em + .5625rem)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:var(--tblr-form-invalid-border-color);box-shadow:var(--tblr-box-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:calc(1.4285714286em + 1.125rem);background-position:top calc(.3571428572em + .28125rem) right calc(.3571428572em + .28125rem)}.was-validated .form-select:invalid,.form-select.is-invalid{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]),.form-select.is-invalid: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:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.7142857143em + .5625rem) calc(.7142857143em + .5625rem)}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus{border-color:var(--tblr-form-invalid-border-color);box-shadow:var(--tblr-box-shadow-input),0 0 0 .25rem rgba(var(--tblr-danger-rgb),.25)}.was-validated .form-control-color:invalid,.form-control-color.is-invalid{width:calc(3rem + calc(1.4285714286em + 1.125rem))}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:var(--tblr-form-invalid-border-color)}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:var(--tblr-form-invalid-color)}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid: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{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,.was-validated .input-group>.form-select:not(:focus):invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.input-group>.form-floating:not(:focus-within).is-invalid{z-index:4}.btn{--tblr-btn-padding-x: .5rem;--tblr-btn-padding-y: .25rem;--tblr-btn-font-family: var(--tblr-font-sans-serif);--tblr-btn-font-size: .875rem;--tblr-btn-font-weight: var(--tblr-font-weight-medium);--tblr-btn-line-height: 1.4285714286;--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-box-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: #49566c;--tblr-btn-disabled-border-color: transparent;--tblr-btn-box-shadow: 0 0 0 #000;--tblr-btn-focus-shadow-rgb: 37, 150, 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: .5rem;--tblr-btn-padding-x: .75rem;--tblr-btn-font-size: 1.25rem;--tblr-btn-border-radius: var(--tblr-border-radius-lg)}.btn-sm,.btn-group-sm>.btn{--tblr-btn-padding-y: .125rem;--tblr-btn-padding-x: .25rem;--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-box-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: #49566c;--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: #b8c4d4;--tblr-dropdown-bg: #182433;--tblr-dropdown-border-color: var(--tblr-border-color-translucent);--tblr-dropdown-box-shadow: ;--tblr-dropdown-link-color: #b8c4d4;--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: #6c7a91;--tblr-dropdown-header-color: #6c7a91}.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(var(--tblr-border-width) * -1)}.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:.1875rem;padding-left:.1875rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.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(var(--tblr-border-width) * -1)}.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~.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-basis:0;flex-grow:1;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-body-color);--tblr-navbar-hover-color: rgba(var(--tblr-emphasis-color-rgb), .8);--tblr-navbar-disabled-color: var(--tblr-disabled-color);--tblr-navbar-active-color: var(--tblr-body-color) 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) 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%2824, 36, 51, 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-basis:100%;flex-grow:1;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);--tblr-card-box-shadow: var(--tblr-shadow-card);--tblr-card-inner-border-radius: calc(var(--tblr-border-radius) - (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}}.accordion{--tblr-accordion-color: var(--tblr-body-color);--tblr-accordion-bg: transparent;--tblr-accordion-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out, border-radius .15s ease;--tblr-accordion-border-color: var(--tblr-border-color-translucent);--tblr-accordion-border-width: var(--tblr-border-width);--tblr-accordion-border-radius: var(--tblr-border-radius);--tblr-accordion-inner-border-radius: calc(var(--tblr-border-radius) - (var(--tblr-border-width)));--tblr-accordion-btn-padding-x: 1.25rem;--tblr-accordion-btn-padding-y: 1rem;--tblr-accordion-btn-color: var(--tblr-body-color);--tblr-accordion-btn-bg: transparent;--tblr-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23182433' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--tblr-accordion-btn-icon-width: 1rem;--tblr-accordion-btn-icon-transform: rotate(-180deg);--tblr-accordion-btn-icon-transition: transform .2s ease-in-out;--tblr-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23003532' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--tblr-accordion-btn-focus-box-shadow: 0 0 0 .25rem rgba(var(--tblr-primary-rgb), .25);--tblr-accordion-body-padding-x: 1.25rem;--tblr-accordion-body-padding-y: 1rem;--tblr-accordion-active-color: inherit;--tblr-accordion-active-bg: transparent}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--tblr-accordion-btn-padding-y) var(--tblr-accordion-btn-padding-x);font-size:.875rem;color:var(--tblr-accordion-btn-color);text-align:left;background-color:var(--tblr-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--tblr-accordion-transition)}@media (prefers-reduced-motion: reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--tblr-accordion-active-color);background-color:var(--tblr-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--tblr-accordion-border-width)) 0 var(--tblr-accordion-border-color)}.accordion-button:not(.collapsed):after{background-image:var(--tblr-accordion-btn-active-icon);transform:var(--tblr-accordion-btn-icon-transform)}.accordion-button:after{flex-shrink:0;width:var(--tblr-accordion-btn-icon-width);height:var(--tblr-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--tblr-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--tblr-accordion-btn-icon-width);transition:var(--tblr-accordion-btn-icon-transition)}@media (prefers-reduced-motion: reduce){.accordion-button:after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--tblr-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--tblr-accordion-color);background-color:var(--tblr-accordion-bg);border:var(--tblr-accordion-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 .accordion-button{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 .accordion-button.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{padding:var(--tblr-accordion-body-padding-y) var(--tblr-accordion-body-padding-x)}.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-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}.accordion-flush>.accordion-item>.accordion-collapse{border-radius:0}[data-bs-theme=dark] .accordion-button:after{--tblr-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2366b6b1'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--tblr-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2366b6b1'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.breadcrumb{--tblr-breadcrumb-padding-x: 0;--tblr-breadcrumb-padding-y: 0;--tblr-breadcrumb-margin-bottom: 1rem;--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;display:flex;flex-wrap:wrap;padding:var(--tblr-breadcrumb-padding-y) var(--tblr-breadcrumb-padding-x);margin-bottom:var(--tblr-breadcrumb-margin-bottom);font-size:var(--tblr-breadcrumb-font-size);list-style:none;background-color:var(--tblr-breadcrumb-bg);border-radius:var(--tblr-breadcrumb-border-radius)}.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-item.active{color:var(--tblr-breadcrumb-item-active-color)}.pagination{--tblr-pagination-padding-x: .25rem;--tblr-pagination-padding-y: .25rem;--tblr-pagination-font-size: .875rem;--tblr-pagination-color: var(--tblr-gray-500);--tblr-pagination-bg: transparent;--tblr-pagination-border-width: 0;--tblr-pagination-border-color: var(--tblr-border-color);--tblr-pagination-border-radius: var(--tblr-border-radius);--tblr-pagination-hover-color: var(--tblr-link-hover-color);--tblr-pagination-hover-bg: var(--tblr-tertiary-bg);--tblr-pagination-hover-border-color: var(--tblr-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-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:-0}.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)}.badge{--tblr-badge-padding-x: .5em;--tblr-badge-padding-y: .25em;--tblr-badge-font-size: 85.714285%;--tblr-badge-font-weight: var(--tblr-font-weight-medium);--tblr-badge-color: var(--tblr-gray-500);--tblr-badge-border-radius: var(--tblr-border-radius);display:inline-block;padding:var(--tblr-badge-padding-y) var(--tblr-badge-padding-x);font-size:var(--tblr-badge-font-size);font-weight:var(--tblr-badge-font-weight);line-height:1;color:var(--tblr-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--tblr-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--tblr-alert-bg: transparent;--tblr-alert-padding-x: 1rem;--tblr-alert-padding-y: .75rem;--tblr-alert-margin-bottom: 1rem;--tblr-alert-color: inherit;--tblr-alert-border-color: 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;position:relative;padding:var(--tblr-alert-padding-y) var(--tblr-alert-padding-x);margin-bottom:var(--tblr-alert-margin-bottom);color:var(--tblr-alert-color);background-color:var(--tblr-alert-bg);border:var(--tblr-alert-border);border-radius:var(--tblr-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:var(--tblr-font-weight-bold);color:var(--tblr-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:.9375rem 1rem}.alert-primary{--tblr-alert-color: var(--tblr-primary-text-emphasis);--tblr-alert-bg: var(--tblr-primary-bg-subtle);--tblr-alert-border-color: var(--tblr-primary-border-subtle);--tblr-alert-link-color: var(--tblr-primary-text-emphasis)}.alert-secondary{--tblr-alert-color: var(--tblr-secondary-text-emphasis);--tblr-alert-bg: var(--tblr-secondary-bg-subtle);--tblr-alert-border-color: var(--tblr-secondary-border-subtle);--tblr-alert-link-color: var(--tblr-secondary-text-emphasis)}.alert-success{--tblr-alert-color: var(--tblr-success-text-emphasis);--tblr-alert-bg: var(--tblr-success-bg-subtle);--tblr-alert-border-color: var(--tblr-success-border-subtle);--tblr-alert-link-color: var(--tblr-success-text-emphasis)}.alert-info{--tblr-alert-color: var(--tblr-info-text-emphasis);--tblr-alert-bg: var(--tblr-info-bg-subtle);--tblr-alert-border-color: var(--tblr-info-border-subtle);--tblr-alert-link-color: var(--tblr-info-text-emphasis)}.alert-warning{--tblr-alert-color: var(--tblr-warning-text-emphasis);--tblr-alert-bg: var(--tblr-warning-bg-subtle);--tblr-alert-border-color: var(--tblr-warning-border-subtle);--tblr-alert-link-color: var(--tblr-warning-text-emphasis)}.alert-danger{--tblr-alert-color: var(--tblr-danger-text-emphasis);--tblr-alert-bg: var(--tblr-danger-bg-subtle);--tblr-alert-border-color: var(--tblr-danger-border-subtle);--tblr-alert-link-color: var(--tblr-danger-text-emphasis)}.alert-light{--tblr-alert-color: var(--tblr-light-text-emphasis);--tblr-alert-bg: var(--tblr-light-bg-subtle);--tblr-alert-border-color: var(--tblr-light-border-subtle);--tblr-alert-link-color: var(--tblr-light-text-emphasis)}.alert-dark{--tblr-alert-color: var(--tblr-dark-text-emphasis);--tblr-alert-bg: var(--tblr-dark-bg-subtle);--tblr-alert-border-color: var(--tblr-dark-border-subtle);--tblr-alert-link-color: var(--tblr-dark-text-emphasis)}.alert-muted{--tblr-alert-color: var(--tblr-muted-text-emphasis);--tblr-alert-bg: var(--tblr-muted-bg-subtle);--tblr-alert-border-color: var(--tblr-muted-border-subtle);--tblr-alert-link-color: var(--tblr-muted-text-emphasis)}.alert-blue{--tblr-alert-color: var(--tblr-blue-text-emphasis);--tblr-alert-bg: var(--tblr-blue-bg-subtle);--tblr-alert-border-color: var(--tblr-blue-border-subtle);--tblr-alert-link-color: var(--tblr-blue-text-emphasis)}.alert-azure{--tblr-alert-color: var(--tblr-azure-text-emphasis);--tblr-alert-bg: var(--tblr-azure-bg-subtle);--tblr-alert-border-color: var(--tblr-azure-border-subtle);--tblr-alert-link-color: var(--tblr-azure-text-emphasis)}.alert-indigo{--tblr-alert-color: var(--tblr-indigo-text-emphasis);--tblr-alert-bg: var(--tblr-indigo-bg-subtle);--tblr-alert-border-color: var(--tblr-indigo-border-subtle);--tblr-alert-link-color: var(--tblr-indigo-text-emphasis)}.alert-purple{--tblr-alert-color: var(--tblr-purple-text-emphasis);--tblr-alert-bg: var(--tblr-purple-bg-subtle);--tblr-alert-border-color: var(--tblr-purple-border-subtle);--tblr-alert-link-color: var(--tblr-purple-text-emphasis)}.alert-pink{--tblr-alert-color: var(--tblr-pink-text-emphasis);--tblr-alert-bg: var(--tblr-pink-bg-subtle);--tblr-alert-border-color: var(--tblr-pink-border-subtle);--tblr-alert-link-color: var(--tblr-pink-text-emphasis)}.alert-red{--tblr-alert-color: var(--tblr-red-text-emphasis);--tblr-alert-bg: var(--tblr-red-bg-subtle);--tblr-alert-border-color: var(--tblr-red-border-subtle);--tblr-alert-link-color: var(--tblr-red-text-emphasis)}.alert-orange{--tblr-alert-color: var(--tblr-orange-text-emphasis);--tblr-alert-bg: var(--tblr-orange-bg-subtle);--tblr-alert-border-color: var(--tblr-orange-border-subtle);--tblr-alert-link-color: var(--tblr-orange-text-emphasis)}.alert-yellow{--tblr-alert-color: var(--tblr-yellow-text-emphasis);--tblr-alert-bg: var(--tblr-yellow-bg-subtle);--tblr-alert-border-color: var(--tblr-yellow-border-subtle);--tblr-alert-link-color: var(--tblr-yellow-text-emphasis)}.alert-lime{--tblr-alert-color: var(--tblr-lime-text-emphasis);--tblr-alert-bg: var(--tblr-lime-bg-subtle);--tblr-alert-border-color: var(--tblr-lime-border-subtle);--tblr-alert-link-color: var(--tblr-lime-text-emphasis)}.alert-green{--tblr-alert-color: var(--tblr-green-text-emphasis);--tblr-alert-bg: var(--tblr-green-bg-subtle);--tblr-alert-border-color: var(--tblr-green-border-subtle);--tblr-alert-link-color: var(--tblr-green-text-emphasis)}.alert-teal{--tblr-alert-color: var(--tblr-teal-text-emphasis);--tblr-alert-bg: var(--tblr-teal-bg-subtle);--tblr-alert-border-color: var(--tblr-teal-border-subtle);--tblr-alert-link-color: var(--tblr-teal-text-emphasis)}.alert-cyan{--tblr-alert-color: var(--tblr-cyan-text-emphasis);--tblr-alert-bg: var(--tblr-cyan-bg-subtle);--tblr-alert-border-color: var(--tblr-cyan-border-subtle);--tblr-alert-link-color: var(--tblr-cyan-text-emphasis)}.alert-x{--tblr-alert-color: var(--tblr-x-text-emphasis);--tblr-alert-bg: var(--tblr-x-bg-subtle);--tblr-alert-border-color: var(--tblr-x-border-subtle);--tblr-alert-link-color: var(--tblr-x-text-emphasis)}.alert-facebook{--tblr-alert-color: var(--tblr-facebook-text-emphasis);--tblr-alert-bg: var(--tblr-facebook-bg-subtle);--tblr-alert-border-color: var(--tblr-facebook-border-subtle);--tblr-alert-link-color: var(--tblr-facebook-text-emphasis)}.alert-twitter{--tblr-alert-color: var(--tblr-twitter-text-emphasis);--tblr-alert-bg: var(--tblr-twitter-bg-subtle);--tblr-alert-border-color: var(--tblr-twitter-border-subtle);--tblr-alert-link-color: var(--tblr-twitter-text-emphasis)}.alert-linkedin{--tblr-alert-color: var(--tblr-linkedin-text-emphasis);--tblr-alert-bg: var(--tblr-linkedin-bg-subtle);--tblr-alert-border-color: var(--tblr-linkedin-border-subtle);--tblr-alert-link-color: var(--tblr-linkedin-text-emphasis)}.alert-google{--tblr-alert-color: var(--tblr-google-text-emphasis);--tblr-alert-bg: var(--tblr-google-bg-subtle);--tblr-alert-border-color: var(--tblr-google-border-subtle);--tblr-alert-link-color: var(--tblr-google-text-emphasis)}.alert-youtube{--tblr-alert-color: var(--tblr-youtube-text-emphasis);--tblr-alert-bg: var(--tblr-youtube-bg-subtle);--tblr-alert-border-color: var(--tblr-youtube-border-subtle);--tblr-alert-link-color: var(--tblr-youtube-text-emphasis)}.alert-vimeo{--tblr-alert-color: var(--tblr-vimeo-text-emphasis);--tblr-alert-bg: var(--tblr-vimeo-bg-subtle);--tblr-alert-border-color: var(--tblr-vimeo-border-subtle);--tblr-alert-link-color: var(--tblr-vimeo-text-emphasis)}.alert-dribbble{--tblr-alert-color: var(--tblr-dribbble-text-emphasis);--tblr-alert-bg: var(--tblr-dribbble-bg-subtle);--tblr-alert-border-color: var(--tblr-dribbble-border-subtle);--tblr-alert-link-color: var(--tblr-dribbble-text-emphasis)}.alert-github{--tblr-alert-color: var(--tblr-github-text-emphasis);--tblr-alert-bg: var(--tblr-github-bg-subtle);--tblr-alert-border-color: var(--tblr-github-border-subtle);--tblr-alert-link-color: var(--tblr-github-text-emphasis)}.alert-instagram{--tblr-alert-color: var(--tblr-instagram-text-emphasis);--tblr-alert-bg: var(--tblr-instagram-bg-subtle);--tblr-alert-border-color: var(--tblr-instagram-border-subtle);--tblr-alert-link-color: var(--tblr-instagram-text-emphasis)}.alert-pinterest{--tblr-alert-color: var(--tblr-pinterest-text-emphasis);--tblr-alert-bg: var(--tblr-pinterest-bg-subtle);--tblr-alert-border-color: var(--tblr-pinterest-border-subtle);--tblr-alert-link-color: var(--tblr-pinterest-text-emphasis)}.alert-vk{--tblr-alert-color: var(--tblr-vk-text-emphasis);--tblr-alert-bg: var(--tblr-vk-bg-subtle);--tblr-alert-border-color: var(--tblr-vk-border-subtle);--tblr-alert-link-color: var(--tblr-vk-text-emphasis)}.alert-rss{--tblr-alert-color: var(--tblr-rss-text-emphasis);--tblr-alert-bg: var(--tblr-rss-bg-subtle);--tblr-alert-border-color: var(--tblr-rss-border-subtle);--tblr-alert-link-color: var(--tblr-rss-text-emphasis)}.alert-flickr{--tblr-alert-color: var(--tblr-flickr-text-emphasis);--tblr-alert-bg: var(--tblr-flickr-bg-subtle);--tblr-alert-border-color: var(--tblr-flickr-border-subtle);--tblr-alert-link-color: var(--tblr-flickr-text-emphasis)}.alert-bitbucket{--tblr-alert-color: var(--tblr-bitbucket-text-emphasis);--tblr-alert-bg: var(--tblr-bitbucket-bg-subtle);--tblr-alert-border-color: var(--tblr-bitbucket-border-subtle);--tblr-alert-link-color: var(--tblr-bitbucket-text-emphasis)}.alert-tabler{--tblr-alert-color: var(--tblr-tabler-text-emphasis);--tblr-alert-bg: var(--tblr-tabler-bg-subtle);--tblr-alert-border-color: var(--tblr-tabler-border-subtle);--tblr-alert-link-color: var(--tblr-tabler-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:.5rem}}.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-action{width:100%;color:var(--tblr-list-group-action-color);text-align:inherit}.list-group-item-action:hover,.list-group-item-action: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:active{color:var(--tblr-list-group-action-active-color);background-color:var(--tblr-list-group-action-active-bg)}.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-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)}.list-group-item-x{--tblr-list-group-color: var(--tblr-x-text-emphasis);--tblr-list-group-bg: var(--tblr-x-bg-subtle);--tblr-list-group-border-color: var(--tblr-x-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-x-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-x-border-subtle);--tblr-list-group-active-color: var(--tblr-x-bg-subtle);--tblr-list-group-active-bg: var(--tblr-x-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-x-text-emphasis)}.list-group-item-facebook{--tblr-list-group-color: var(--tblr-facebook-text-emphasis);--tblr-list-group-bg: var(--tblr-facebook-bg-subtle);--tblr-list-group-border-color: var(--tblr-facebook-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-facebook-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-facebook-border-subtle);--tblr-list-group-active-color: var(--tblr-facebook-bg-subtle);--tblr-list-group-active-bg: var(--tblr-facebook-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-facebook-text-emphasis)}.list-group-item-twitter{--tblr-list-group-color: var(--tblr-twitter-text-emphasis);--tblr-list-group-bg: var(--tblr-twitter-bg-subtle);--tblr-list-group-border-color: var(--tblr-twitter-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-twitter-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-twitter-border-subtle);--tblr-list-group-active-color: var(--tblr-twitter-bg-subtle);--tblr-list-group-active-bg: var(--tblr-twitter-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-twitter-text-emphasis)}.list-group-item-linkedin{--tblr-list-group-color: var(--tblr-linkedin-text-emphasis);--tblr-list-group-bg: var(--tblr-linkedin-bg-subtle);--tblr-list-group-border-color: var(--tblr-linkedin-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-linkedin-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-linkedin-border-subtle);--tblr-list-group-active-color: var(--tblr-linkedin-bg-subtle);--tblr-list-group-active-bg: var(--tblr-linkedin-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-linkedin-text-emphasis)}.list-group-item-google{--tblr-list-group-color: var(--tblr-google-text-emphasis);--tblr-list-group-bg: var(--tblr-google-bg-subtle);--tblr-list-group-border-color: var(--tblr-google-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-google-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-google-border-subtle);--tblr-list-group-active-color: var(--tblr-google-bg-subtle);--tblr-list-group-active-bg: var(--tblr-google-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-google-text-emphasis)}.list-group-item-youtube{--tblr-list-group-color: var(--tblr-youtube-text-emphasis);--tblr-list-group-bg: var(--tblr-youtube-bg-subtle);--tblr-list-group-border-color: var(--tblr-youtube-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-youtube-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-youtube-border-subtle);--tblr-list-group-active-color: var(--tblr-youtube-bg-subtle);--tblr-list-group-active-bg: var(--tblr-youtube-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-youtube-text-emphasis)}.list-group-item-vimeo{--tblr-list-group-color: var(--tblr-vimeo-text-emphasis);--tblr-list-group-bg: var(--tblr-vimeo-bg-subtle);--tblr-list-group-border-color: var(--tblr-vimeo-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-vimeo-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-vimeo-border-subtle);--tblr-list-group-active-color: var(--tblr-vimeo-bg-subtle);--tblr-list-group-active-bg: var(--tblr-vimeo-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-vimeo-text-emphasis)}.list-group-item-dribbble{--tblr-list-group-color: var(--tblr-dribbble-text-emphasis);--tblr-list-group-bg: var(--tblr-dribbble-bg-subtle);--tblr-list-group-border-color: var(--tblr-dribbble-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-dribbble-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-dribbble-border-subtle);--tblr-list-group-active-color: var(--tblr-dribbble-bg-subtle);--tblr-list-group-active-bg: var(--tblr-dribbble-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-dribbble-text-emphasis)}.list-group-item-github{--tblr-list-group-color: var(--tblr-github-text-emphasis);--tblr-list-group-bg: var(--tblr-github-bg-subtle);--tblr-list-group-border-color: var(--tblr-github-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-github-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-github-border-subtle);--tblr-list-group-active-color: var(--tblr-github-bg-subtle);--tblr-list-group-active-bg: var(--tblr-github-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-github-text-emphasis)}.list-group-item-instagram{--tblr-list-group-color: var(--tblr-instagram-text-emphasis);--tblr-list-group-bg: var(--tblr-instagram-bg-subtle);--tblr-list-group-border-color: var(--tblr-instagram-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-instagram-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-instagram-border-subtle);--tblr-list-group-active-color: var(--tblr-instagram-bg-subtle);--tblr-list-group-active-bg: var(--tblr-instagram-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-instagram-text-emphasis)}.list-group-item-pinterest{--tblr-list-group-color: var(--tblr-pinterest-text-emphasis);--tblr-list-group-bg: var(--tblr-pinterest-bg-subtle);--tblr-list-group-border-color: var(--tblr-pinterest-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-pinterest-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-pinterest-border-subtle);--tblr-list-group-active-color: var(--tblr-pinterest-bg-subtle);--tblr-list-group-active-bg: var(--tblr-pinterest-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-pinterest-text-emphasis)}.list-group-item-vk{--tblr-list-group-color: var(--tblr-vk-text-emphasis);--tblr-list-group-bg: var(--tblr-vk-bg-subtle);--tblr-list-group-border-color: var(--tblr-vk-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-vk-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-vk-border-subtle);--tblr-list-group-active-color: var(--tblr-vk-bg-subtle);--tblr-list-group-active-bg: var(--tblr-vk-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-vk-text-emphasis)}.list-group-item-rss{--tblr-list-group-color: var(--tblr-rss-text-emphasis);--tblr-list-group-bg: var(--tblr-rss-bg-subtle);--tblr-list-group-border-color: var(--tblr-rss-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-rss-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-rss-border-subtle);--tblr-list-group-active-color: var(--tblr-rss-bg-subtle);--tblr-list-group-active-bg: var(--tblr-rss-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-rss-text-emphasis)}.list-group-item-flickr{--tblr-list-group-color: var(--tblr-flickr-text-emphasis);--tblr-list-group-bg: var(--tblr-flickr-bg-subtle);--tblr-list-group-border-color: var(--tblr-flickr-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-flickr-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-flickr-border-subtle);--tblr-list-group-active-color: var(--tblr-flickr-bg-subtle);--tblr-list-group-active-bg: var(--tblr-flickr-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-flickr-text-emphasis)}.list-group-item-bitbucket{--tblr-list-group-color: var(--tblr-bitbucket-text-emphasis);--tblr-list-group-bg: var(--tblr-bitbucket-bg-subtle);--tblr-list-group-border-color: var(--tblr-bitbucket-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-bitbucket-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-bitbucket-border-subtle);--tblr-list-group-active-color: var(--tblr-bitbucket-bg-subtle);--tblr-list-group-active-bg: var(--tblr-bitbucket-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-bitbucket-text-emphasis)}.list-group-item-tabler{--tblr-list-group-color: var(--tblr-tabler-text-emphasis);--tblr-list-group-bg: var(--tblr-tabler-bg-subtle);--tblr-list-group-border-color: var(--tblr-tabler-border-subtle);--tblr-list-group-action-hover-color: var(--tblr-emphasis-color);--tblr-list-group-action-hover-bg: var(--tblr-tabler-border-subtle);--tblr-list-group-action-active-color: var(--tblr-emphasis-color);--tblr-list-group-action-active-bg: var(--tblr-tabler-border-subtle);--tblr-list-group-active-color: var(--tblr-tabler-bg-subtle);--tblr-list-group-active-bg: var(--tblr-tabler-text-emphasis);--tblr-list-group-active-border-color: var(--tblr-tabler-text-emphasis)}.btn-close{--tblr-btn-close-color: #182433;--tblr-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23182433'%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.414z'/%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-white-filter: invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em;color:var(--tblr-btn-close-color);background:transparent var(--tblr-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:4px;opacity:var(--tblr-btn-close-opacity)}.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)}.btn-close-white,[data-bs-theme=dark] .btn-close,body[data-bs-theme=dark] [data-bs-theme=light] .btn-close{filter:var(--tblr-btn-close-white-filter)}.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: rgba(var(--tblr-body-bg-rgb), .85);--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: ;--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{transition:transform .3s ease-out;transform:translateY(-1rem)}@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: #182433;--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:calc(-.5 * var(--tblr-modal-header-padding-y)) calc(-.5 * var(--tblr-modal-header-padding-x)) calc(-.5 * var(--tblr-modal-header-padding-y)) 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-2);--tblr-tooltip-padding-y: var(--tblr-spacer-2);--tblr-tooltip-margin: ;--tblr-tooltip-font-size: .765625rem;--tblr-tooltip-color: var(--tblr-light);--tblr-tooltip-bg: var(--tblr-bg-surface-dark);--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-box-shadow);--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: 1rem;--tblr-popover-body-padding-y: 1rem;--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;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:#fff;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:#fff;text-align:center}.carousel-dark .carousel-control-prev-icon,.carousel-dark .carousel-control-next-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon{filter:invert(1) grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-grow,.spinner-border{display:inline-block;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:#182433}.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:calc(-.5 * var(--tblr-offcanvas-padding-y)) calc(-.5 * var(--tblr-offcanvas-padding-x)) calc(-.5 * var(--tblr-offcanvas-padding-y)) 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%}}.clearfix:after{display:block;clear:both;content:""}.text-bg-primary{color:#f6f8fb!important;background-color:RGBA(var(--tblr-primary-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-secondary,.text-bg-gray{color:#f6f8fb!important;background-color:RGBA(var(--tblr-secondary-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-success{color:#f6f8fb!important;background-color:RGBA(var(--tblr-success-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-info{color:#f6f8fb!important;background-color:RGBA(var(--tblr-info-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-warning{color:#f6f8fb!important;background-color:RGBA(var(--tblr-warning-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-danger{color:#f6f8fb!important;background-color:RGBA(var(--tblr-danger-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-light,.text-bg-white{color:#182433!important;background-color:RGBA(var(--tblr-light-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-dark,.text-bg-black{color:#f6f8fb!important;background-color:RGBA(var(--tblr-dark-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-muted{color:#f6f8fb!important;background-color:RGBA(var(--tblr-muted-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-blue{color:#f6f8fb!important;background-color:RGBA(var(--tblr-blue-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-azure{color:#f6f8fb!important;background-color:RGBA(var(--tblr-azure-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-indigo{color:#f6f8fb!important;background-color:RGBA(var(--tblr-indigo-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-purple{color:#f6f8fb!important;background-color:RGBA(var(--tblr-purple-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-pink{color:#f6f8fb!important;background-color:RGBA(var(--tblr-pink-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-red{color:#f6f8fb!important;background-color:RGBA(var(--tblr-red-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-orange{color:#f6f8fb!important;background-color:RGBA(var(--tblr-orange-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-yellow{color:#f6f8fb!important;background-color:RGBA(var(--tblr-yellow-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-lime{color:#f6f8fb!important;background-color:RGBA(var(--tblr-lime-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-green{color:#f6f8fb!important;background-color:RGBA(var(--tblr-green-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-teal{color:#f6f8fb!important;background-color:RGBA(var(--tblr-teal-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-cyan{color:#f6f8fb!important;background-color:RGBA(var(--tblr-cyan-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-x{color:#f6f8fb!important;background-color:RGBA(var(--tblr-x-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-facebook{color:#f6f8fb!important;background-color:RGBA(var(--tblr-facebook-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-twitter{color:#f6f8fb!important;background-color:RGBA(var(--tblr-twitter-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-linkedin{color:#f6f8fb!important;background-color:RGBA(var(--tblr-linkedin-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-google{color:#f6f8fb!important;background-color:RGBA(var(--tblr-google-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-youtube{color:#f6f8fb!important;background-color:RGBA(var(--tblr-youtube-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-vimeo{color:#f6f8fb!important;background-color:RGBA(var(--tblr-vimeo-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-dribbble{color:#f6f8fb!important;background-color:RGBA(var(--tblr-dribbble-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-github{color:#f6f8fb!important;background-color:RGBA(var(--tblr-github-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-instagram{color:#f6f8fb!important;background-color:RGBA(var(--tblr-instagram-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-pinterest{color:#f6f8fb!important;background-color:RGBA(var(--tblr-pinterest-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-vk{color:#f6f8fb!important;background-color:RGBA(var(--tblr-vk-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-rss{color:#f6f8fb!important;background-color:RGBA(var(--tblr-rss-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-flickr{color:#f6f8fb!important;background-color:RGBA(var(--tblr-flickr-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-bitbucket{color:#f6f8fb!important;background-color:RGBA(var(--tblr-bitbucket-rgb),var(--tblr-bg-opacity, 1))!important}.text-bg-tabler{color:#f6f8fb!important;background-color:RGBA(var(--tblr-tabler-rgb),var(--tblr-bg-opacity, 1))!important}.link-primary{color:RGBA(var(--tblr-primary-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-primary-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-secondary{color:RGBA(var(--tblr-secondary-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-secondary-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-secondary:hover,.link-secondary:focus{color:RGBA(86,98,116,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(86,98,116,var(--tblr-link-underline-opacity, 1))!important}.link-success{color:RGBA(var(--tblr-success-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-success-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-info{color:RGBA(var(--tblr-info-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-info-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-warning{color:RGBA(var(--tblr-warning-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-warning-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-warning:hover,.link-warning: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}.link-danger{color:RGBA(var(--tblr-danger-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-danger-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-light{color:RGBA(var(--tblr-light-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-light-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-light:hover,.link-light:focus{color:RGBA(248,249,252,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(248,249,252,var(--tblr-link-underline-opacity, 1))!important}.link-dark{color:RGBA(var(--tblr-dark-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-dark-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-dark:hover,.link-dark:focus{color:RGBA(19,29,41,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(19,29,41,var(--tblr-link-underline-opacity, 1))!important}.link-muted{color:RGBA(var(--tblr-muted-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-muted-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-muted:hover,.link-muted:focus{color:RGBA(86,98,116,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(86,98,116,var(--tblr-link-underline-opacity, 1))!important}.link-blue{color:RGBA(var(--tblr-blue-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-blue-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-azure{color:RGBA(var(--tblr-azure-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-azure-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-indigo{color:RGBA(var(--tblr-indigo-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-indigo-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-purple{color:RGBA(var(--tblr-purple-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-purple-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-pink{color:RGBA(var(--tblr-pink-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-pink-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-red{color:RGBA(var(--tblr-red-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-red-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-orange{color:RGBA(var(--tblr-orange-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-orange-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-yellow{color:RGBA(var(--tblr-yellow-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-yellow-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-lime{color:RGBA(var(--tblr-lime-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-lime-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-green{color:RGBA(var(--tblr-green-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-green-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-teal{color:RGBA(var(--tblr-teal-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-teal-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-cyan{color:RGBA(var(--tblr-cyan-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-cyan-rgb),var(--tblr-link-underline-opacity, 1))!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}.link-x{color:RGBA(var(--tblr-x-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-x-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-x:hover,.link-x:focus{color:RGBA(0,0,0,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(0,0,0,var(--tblr-link-underline-opacity, 1))!important}.link-facebook{color:RGBA(var(--tblr-facebook-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-facebook-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-facebook:hover,.link-facebook:focus{color:RGBA(19,95,194,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(19,95,194,var(--tblr-link-underline-opacity, 1))!important}.link-twitter{color:RGBA(var(--tblr-twitter-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-twitter-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-twitter:hover,.link-twitter:focus{color:RGBA(23,129,194,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(23,129,194,var(--tblr-link-underline-opacity, 1))!important}.link-linkedin{color:RGBA(var(--tblr-linkedin-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-linkedin-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-linkedin:hover,.link-linkedin:focus{color:RGBA(8,82,155,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(8,82,155,var(--tblr-link-underline-opacity, 1))!important}.link-google{color:RGBA(var(--tblr-google-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-google-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-google:hover,.link-google:focus{color:RGBA(176,62,52,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(176,62,52,var(--tblr-link-underline-opacity, 1))!important}.link-youtube{color:RGBA(var(--tblr-youtube-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-youtube-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-youtube:hover,.link-youtube:focus{color:RGBA(204,0,0,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(204,0,0,var(--tblr-link-underline-opacity, 1))!important}.link-vimeo{color:RGBA(var(--tblr-vimeo-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-vimeo-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-vimeo:hover,.link-vimeo:focus{color:RGBA(21,146,187,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(21,146,187,var(--tblr-link-underline-opacity, 1))!important}.link-dribbble{color:RGBA(var(--tblr-dribbble-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-dribbble-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-dribbble:hover,.link-dribbble:focus{color:RGBA(187,61,110,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(187,61,110,var(--tblr-link-underline-opacity, 1))!important}.link-github{color:RGBA(var(--tblr-github-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-github-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-github:hover,.link-github:focus{color:RGBA(19,18,18,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(19,18,18,var(--tblr-link-underline-opacity, 1))!important}.link-instagram{color:RGBA(var(--tblr-instagram-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-instagram-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-instagram:hover,.link-instagram:focus{color:RGBA(182,51,76,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(182,51,76,var(--tblr-link-underline-opacity, 1))!important}.link-pinterest{color:RGBA(var(--tblr-pinterest-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-pinterest-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-pinterest:hover,.link-pinterest:focus{color:RGBA(151,6,22,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(151,6,22,var(--tblr-link-underline-opacity, 1))!important}.link-vk{color:RGBA(var(--tblr-vk-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-vk-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-vk:hover,.link-vk:focus{color:RGBA(79,105,134,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(79,105,134,var(--tblr-link-underline-opacity, 1))!important}.link-rss{color:RGBA(var(--tblr-rss-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-rss-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-rss:hover,.link-rss:focus{color:RGBA(204,132,0,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(204,132,0,var(--tblr-link-underline-opacity, 1))!important}.link-flickr{color:RGBA(var(--tblr-flickr-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-flickr-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-flickr:hover,.link-flickr:focus{color:RGBA(0,79,176,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(0,79,176,var(--tblr-link-underline-opacity, 1))!important}.link-bitbucket{color:RGBA(var(--tblr-bitbucket-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-bitbucket-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-bitbucket:hover,.link-bitbucket:focus{color:RGBA(0,66,163,var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(0,66,163,var(--tblr-link-underline-opacity, 1))!important}.link-tabler{color:RGBA(var(--tblr-tabler-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-tabler-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-tabler:hover,.link-tabler: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}.link-body-emphasis{color:RGBA(var(--tblr-emphasis-color-rgb),var(--tblr-link-opacity, 1))!important;text-decoration-color:RGBA(var(--tblr-emphasis-color-rgb),var(--tblr-link-underline-opacity, 1))!important}.link-body-emphasis:hover,.link-body-emphasis:focus{color:RGBA(var(--tblr-emphasis-color-rgb),var(--tblr-link-opacity, .75))!important;text-decoration-color:RGBA(var(--tblr-emphasis-color-rgb),var(--tblr-link-underline-opacity, .75))!important}.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)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;text-decoration-color:rgba(var(--tblr-link-color-rgb),var(--tblr-link-opacity, .5));text-underline-offset:.25em;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media (prefers-reduced-motion: reduce){.icon-link>.bi{transition:none}}.icon-link-hover:hover>.bi,.icon-link-hover:focus-visible>.bi{transform:var(--tblr-icon-link-transform, translate3d(.25em, 0, 0))}.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-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%}.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}}.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}.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}.stretched-link:after{position:absolute;inset:0;z-index:1;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}.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))}.focus-ring-x{--tblr-focus-ring-color: rgba(var(--tblr-x-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-facebook{--tblr-focus-ring-color: rgba(var(--tblr-facebook-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-twitter{--tblr-focus-ring-color: rgba(var(--tblr-twitter-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-linkedin{--tblr-focus-ring-color: rgba(var(--tblr-linkedin-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-google{--tblr-focus-ring-color: rgba(var(--tblr-google-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-youtube{--tblr-focus-ring-color: rgba(var(--tblr-youtube-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-vimeo{--tblr-focus-ring-color: rgba(var(--tblr-vimeo-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-dribbble{--tblr-focus-ring-color: rgba(var(--tblr-dribbble-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-github{--tblr-focus-ring-color: rgba(var(--tblr-github-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-instagram{--tblr-focus-ring-color: rgba(var(--tblr-instagram-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-pinterest{--tblr-focus-ring-color: rgba(var(--tblr-pinterest-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-vk{--tblr-focus-ring-color: rgba(var(--tblr-vk-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-rss{--tblr-focus-ring-color: rgba(var(--tblr-rss-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-flickr{--tblr-focus-ring-color: rgba(var(--tblr-flickr-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-bitbucket{--tblr-focus-ring-color: rgba(var(--tblr-bitbucket-rgb), var(--tblr-focus-ring-opacity))}.focus-ring-tabler{--tblr-focus-ring-color: rgba(var(--tblr-tabler-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) rgba(4,32,69,.1)!important}.border-wide{border:2px var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-0{border:0!important}.border-top{border-top:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-top-wide{border-top:2px var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-end-wide{border-right:2px var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-bottom-wide{border-bottom:2px var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-start-wide{border-left:2px var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-start-0{border-left:0!important}.border-primary{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-primary-rgb),var(--tblr-border-opacity))!important}.border-secondary{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-secondary-rgb),var(--tblr-border-opacity))!important}.border-success{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-success-rgb),var(--tblr-border-opacity))!important}.border-info{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-info-rgb),var(--tblr-border-opacity))!important}.border-warning{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-warning-rgb),var(--tblr-border-opacity))!important}.border-danger{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-danger-rgb),var(--tblr-border-opacity))!important}.border-light{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-light-rgb),var(--tblr-border-opacity))!important}.border-dark{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-dark-rgb),var(--tblr-border-opacity))!important}.border-muted{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-muted-rgb),var(--tblr-border-opacity))!important}.border-blue{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-blue-rgb),var(--tblr-border-opacity))!important}.border-azure{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-azure-rgb),var(--tblr-border-opacity))!important}.border-indigo{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-indigo-rgb),var(--tblr-border-opacity))!important}.border-purple{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-purple-rgb),var(--tblr-border-opacity))!important}.border-pink{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-pink-rgb),var(--tblr-border-opacity))!important}.border-red{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-red-rgb),var(--tblr-border-opacity))!important}.border-orange{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-orange-rgb),var(--tblr-border-opacity))!important}.border-yellow{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-yellow-rgb),var(--tblr-border-opacity))!important}.border-lime{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-lime-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-teal{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-teal-rgb),var(--tblr-border-opacity))!important}.border-cyan{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-cyan-rgb),var(--tblr-border-opacity))!important}.border-x{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-x-rgb),var(--tblr-border-opacity))!important}.border-facebook{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-facebook-rgb),var(--tblr-border-opacity))!important}.border-twitter{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-twitter-rgb),var(--tblr-border-opacity))!important}.border-linkedin{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-linkedin-rgb),var(--tblr-border-opacity))!important}.border-google{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-google-rgb),var(--tblr-border-opacity))!important}.border-youtube{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-youtube-rgb),var(--tblr-border-opacity))!important}.border-vimeo{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-vimeo-rgb),var(--tblr-border-opacity))!important}.border-dribbble{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-dribbble-rgb),var(--tblr-border-opacity))!important}.border-github{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-github-rgb),var(--tblr-border-opacity))!important}.border-instagram{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-instagram-rgb),var(--tblr-border-opacity))!important}.border-pinterest{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-pinterest-rgb),var(--tblr-border-opacity))!important}.border-vk{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-vk-rgb),var(--tblr-border-opacity))!important}.border-rss{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-rss-rgb),var(--tblr-border-opacity))!important}.border-flickr{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-flickr-rgb),var(--tblr-border-opacity))!important}.border-bitbucket{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-border-opacity))!important}.border-tabler{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-tabler-rgb),var(--tblr-border-opacity))!important}.border-black{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-black-rgb),var(--tblr-border-opacity))!important}.border-white{--tblr-border-opacity: 1;border-color:rgba(var(--tblr-white-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:3rem!important}.m-7{margin:5rem!important}.m-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-7{margin-right:5rem!important;margin-left:5rem!important}.mx-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-7{margin-top:5rem!important}.mt-8{margin-top:8rem!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:3rem!important}.me-7{margin-right:5rem!important}.me-8{margin-right:8rem!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:3rem!important}.mb-7{margin-bottom:5rem!important}.mb-8{margin-bottom:8rem!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:3rem!important}.ms-7{margin-left:5rem!important}.ms-8{margin-left:8rem!important}.ms-auto{margin-left:auto!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:3rem!important}.p-7{padding:5rem!important}.p-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-7{padding-right:5rem!important;padding-left:5rem!important}.px-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-7{padding-top:5rem!important}.pt-8{padding-top:8rem!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:3rem!important}.pe-7{padding-right:5rem!important}.pe-8{padding-right:8rem!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:3rem!important}.pb-7{padding-bottom:5rem!important}.pb-8{padding-bottom:8rem!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:3rem!important}.ps-7{padding-left:5rem!important}.ps-8{padding-left:8rem!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:3rem!important}.gap-7{gap:5rem!important}.gap-8{gap:8rem!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:3rem!important}.row-gap-7{row-gap:5rem!important}.row-gap-8{row-gap:8rem!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:3rem!important}.column-gap-7{column-gap:5rem!important}.column-gap-8{column-gap:8rem!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-muted{--tblr-text-opacity: 1;color:var(--tblr-secondary-color)!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-x{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-x-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-facebook{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-facebook-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-twitter{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-twitter-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-linkedin{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-linkedin-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-google{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-google-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-youtube{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-youtube-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-vimeo{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-vimeo-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-dribbble{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-dribbble-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-github{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-github-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-instagram{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-instagram-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-pinterest{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-pinterest-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-vk{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-vk-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-rss{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-rss-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-flickr{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-flickr-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-bitbucket{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-link-underline-opacity))!important}.link-underline-tabler{--tblr-link-underline-opacity: 1;text-decoration-color:rgba(var(--tblr-tabler-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-black{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-black-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}.tracking-tight{letter-spacing:-.05em!important}.tracking-normal{letter-spacing:0!important}.tracking-wide{letter-spacing:.05em!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}.border-x{border-left:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.1)!important;border-right:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-x-wide{border-left:2px var(--tblr-border-style) rgba(4,32,69,.1)!important;border-right:2px var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-x-0{border-left:0!important;border-right:0!important}.border-y{border-top:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.1)!important;border-bottom:var(--tblr-border-width) var(--tblr-border-style) rgba(4,32,69,.1)!important}.border-y-wide{border-top:2px var(--tblr-border-style) rgba(4,32,69,.1)!important;border-bottom:2px var(--tblr-border-style) rgba(4,32,69,.1)!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}@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:3rem!important}.m-sm-7{margin:5rem!important}.m-sm-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-sm-7{margin-right:5rem!important;margin-left:5rem!important}.mx-sm-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-sm-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-sm-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-sm-7{margin-top:5rem!important}.mt-sm-8{margin-top:8rem!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:3rem!important}.me-sm-7{margin-right:5rem!important}.me-sm-8{margin-right:8rem!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:3rem!important}.mb-sm-7{margin-bottom:5rem!important}.mb-sm-8{margin-bottom:8rem!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:3rem!important}.ms-sm-7{margin-left:5rem!important}.ms-sm-8{margin-left:8rem!important}.ms-sm-auto{margin-left:auto!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:3rem!important}.p-sm-7{padding:5rem!important}.p-sm-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-sm-7{padding-right:5rem!important;padding-left:5rem!important}.px-sm-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-sm-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-sm-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-sm-7{padding-top:5rem!important}.pt-sm-8{padding-top:8rem!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:3rem!important}.pe-sm-7{padding-right:5rem!important}.pe-sm-8{padding-right:8rem!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:3rem!important}.pb-sm-7{padding-bottom:5rem!important}.pb-sm-8{padding-bottom:8rem!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:3rem!important}.ps-sm-7{padding-left:5rem!important}.ps-sm-8{padding-left:8rem!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:3rem!important}.gap-sm-7{gap:5rem!important}.gap-sm-8{gap:8rem!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:3rem!important}.row-gap-sm-7{row-gap:5rem!important}.row-gap-sm-8{row-gap:8rem!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:3rem!important}.column-gap-sm-7{column-gap:5rem!important}.column-gap-sm-8{column-gap:8rem!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:3rem!important}.m-md-7{margin:5rem!important}.m-md-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-md-7{margin-right:5rem!important;margin-left:5rem!important}.mx-md-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-md-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-md-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-md-7{margin-top:5rem!important}.mt-md-8{margin-top:8rem!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:3rem!important}.me-md-7{margin-right:5rem!important}.me-md-8{margin-right:8rem!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:3rem!important}.mb-md-7{margin-bottom:5rem!important}.mb-md-8{margin-bottom:8rem!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:3rem!important}.ms-md-7{margin-left:5rem!important}.ms-md-8{margin-left:8rem!important}.ms-md-auto{margin-left:auto!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:3rem!important}.p-md-7{padding:5rem!important}.p-md-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-md-7{padding-right:5rem!important;padding-left:5rem!important}.px-md-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-md-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-md-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-md-7{padding-top:5rem!important}.pt-md-8{padding-top:8rem!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:3rem!important}.pe-md-7{padding-right:5rem!important}.pe-md-8{padding-right:8rem!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:3rem!important}.pb-md-7{padding-bottom:5rem!important}.pb-md-8{padding-bottom:8rem!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:3rem!important}.ps-md-7{padding-left:5rem!important}.ps-md-8{padding-left:8rem!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:3rem!important}.gap-md-7{gap:5rem!important}.gap-md-8{gap:8rem!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:3rem!important}.row-gap-md-7{row-gap:5rem!important}.row-gap-md-8{row-gap:8rem!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:3rem!important}.column-gap-md-7{column-gap:5rem!important}.column-gap-md-8{column-gap:8rem!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:3rem!important}.m-lg-7{margin:5rem!important}.m-lg-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-lg-7{margin-right:5rem!important;margin-left:5rem!important}.mx-lg-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-lg-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-lg-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-lg-7{margin-top:5rem!important}.mt-lg-8{margin-top:8rem!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:3rem!important}.me-lg-7{margin-right:5rem!important}.me-lg-8{margin-right:8rem!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:3rem!important}.mb-lg-7{margin-bottom:5rem!important}.mb-lg-8{margin-bottom:8rem!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:3rem!important}.ms-lg-7{margin-left:5rem!important}.ms-lg-8{margin-left:8rem!important}.ms-lg-auto{margin-left:auto!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:3rem!important}.p-lg-7{padding:5rem!important}.p-lg-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-lg-7{padding-right:5rem!important;padding-left:5rem!important}.px-lg-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-lg-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-lg-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-lg-7{padding-top:5rem!important}.pt-lg-8{padding-top:8rem!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:3rem!important}.pe-lg-7{padding-right:5rem!important}.pe-lg-8{padding-right:8rem!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:3rem!important}.pb-lg-7{padding-bottom:5rem!important}.pb-lg-8{padding-bottom:8rem!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:3rem!important}.ps-lg-7{padding-left:5rem!important}.ps-lg-8{padding-left:8rem!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:3rem!important}.gap-lg-7{gap:5rem!important}.gap-lg-8{gap:8rem!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:3rem!important}.row-gap-lg-7{row-gap:5rem!important}.row-gap-lg-8{row-gap:8rem!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:3rem!important}.column-gap-lg-7{column-gap:5rem!important}.column-gap-lg-8{column-gap:8rem!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:3rem!important}.m-xl-7{margin:5rem!important}.m-xl-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-xl-7{margin-right:5rem!important;margin-left:5rem!important}.mx-xl-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-xl-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-xl-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-xl-7{margin-top:5rem!important}.mt-xl-8{margin-top:8rem!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:3rem!important}.me-xl-7{margin-right:5rem!important}.me-xl-8{margin-right:8rem!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:3rem!important}.mb-xl-7{margin-bottom:5rem!important}.mb-xl-8{margin-bottom:8rem!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:3rem!important}.ms-xl-7{margin-left:5rem!important}.ms-xl-8{margin-left:8rem!important}.ms-xl-auto{margin-left:auto!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:3rem!important}.p-xl-7{padding:5rem!important}.p-xl-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-xl-7{padding-right:5rem!important;padding-left:5rem!important}.px-xl-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-xl-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-xl-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-xl-7{padding-top:5rem!important}.pt-xl-8{padding-top:8rem!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:3rem!important}.pe-xl-7{padding-right:5rem!important}.pe-xl-8{padding-right:8rem!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:3rem!important}.pb-xl-7{padding-bottom:5rem!important}.pb-xl-8{padding-bottom:8rem!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:3rem!important}.ps-xl-7{padding-left:5rem!important}.ps-xl-8{padding-left:8rem!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:3rem!important}.gap-xl-7{gap:5rem!important}.gap-xl-8{gap:8rem!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:3rem!important}.row-gap-xl-7{row-gap:5rem!important}.row-gap-xl-8{row-gap:8rem!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:3rem!important}.column-gap-xl-7{column-gap:5rem!important}.column-gap-xl-8{column-gap:8rem!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:3rem!important}.m-xxl-7{margin:5rem!important}.m-xxl-8{margin:8rem!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:3rem!important;margin-left:3rem!important}.mx-xxl-7{margin-right:5rem!important;margin-left:5rem!important}.mx-xxl-8{margin-right:8rem!important;margin-left:8rem!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:3rem!important;margin-bottom:3rem!important}.my-xxl-7{margin-top:5rem!important;margin-bottom:5rem!important}.my-xxl-8{margin-top:8rem!important;margin-bottom:8rem!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:3rem!important}.mt-xxl-7{margin-top:5rem!important}.mt-xxl-8{margin-top:8rem!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:3rem!important}.me-xxl-7{margin-right:5rem!important}.me-xxl-8{margin-right:8rem!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:3rem!important}.mb-xxl-7{margin-bottom:5rem!important}.mb-xxl-8{margin-bottom:8rem!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:3rem!important}.ms-xxl-7{margin-left:5rem!important}.ms-xxl-8{margin-left:8rem!important}.ms-xxl-auto{margin-left:auto!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:3rem!important}.p-xxl-7{padding:5rem!important}.p-xxl-8{padding:8rem!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:3rem!important;padding-left:3rem!important}.px-xxl-7{padding-right:5rem!important;padding-left:5rem!important}.px-xxl-8{padding-right:8rem!important;padding-left:8rem!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:3rem!important;padding-bottom:3rem!important}.py-xxl-7{padding-top:5rem!important;padding-bottom:5rem!important}.py-xxl-8{padding-top:8rem!important;padding-bottom:8rem!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:3rem!important}.pt-xxl-7{padding-top:5rem!important}.pt-xxl-8{padding-top:8rem!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:3rem!important}.pe-xxl-7{padding-right:5rem!important}.pe-xxl-8{padding-right:8rem!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:3rem!important}.pb-xxl-7{padding-bottom:5rem!important}.pb-xxl-8{padding-bottom:8rem!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:3rem!important}.ps-xxl-7{padding-left:5rem!important}.ps-xxl-8{padding-left:8rem!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:3rem!important}.gap-xxl-7{gap:5rem!important}.gap-xxl-8{gap:8rem!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:3rem!important}.row-gap-xxl-7{row-gap:5rem!important}.row-gap-xxl-8{row-gap:8rem!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:3rem!important}.column-gap-xxl-7{column-gap:5rem!important}.column-gap-xxl-8{column-gap:8rem!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{font-size:16px;height:100%}:root,:host,[data-bs-theme=light]{--tblr-primary: #00857D;--tblr-primary-rgb: 0, 133, 125;--tblr-primary-fg: var(--tblr-light);--tblr-primary-darken: #007871;--tblr-primary-lt: #e6f3f2;--tblr-primary-lt-rgb: 230, 243, 242;--tblr-secondary: #6c7a91;--tblr-secondary-rgb: 108, 122, 145;--tblr-secondary-fg: var(--tblr-light);--tblr-secondary-darken: #616e83;--tblr-secondary-lt: #f0f2f4;--tblr-secondary-lt-rgb: 240, 242, 244;--tblr-success: #2fb344;--tblr-success-rgb: 47, 179, 68;--tblr-success-fg: var(--tblr-light);--tblr-success-darken: #2aa13d;--tblr-success-lt: #eaf7ec;--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: #3b8acb;--tblr-info-lt: #ecf5fc;--tblr-info-lt-rgb: 236, 245, 252;--tblr-warning: #f76707;--tblr-warning-rgb: 247, 103, 7;--tblr-warning-fg: var(--tblr-light);--tblr-warning-darken: #de5d06;--tblr-warning-lt: #fef0e6;--tblr-warning-lt-rgb: 254, 240, 230;--tblr-danger: #d63939;--tblr-danger-rgb: 214, 57, 57;--tblr-danger-fg: var(--tblr-light);--tblr-danger-darken: #c13333;--tblr-danger-lt: #fbebeb;--tblr-danger-lt-rgb: 251, 235, 235;--tblr-light: #f6f8fb;--tblr-light-rgb: 246, 248, 251;--tblr-light-fg: var(--tblr-dark);--tblr-light-darken: #dddfe2;--tblr-light-lt: #fefeff;--tblr-light-lt-rgb: 254, 254, 255;--tblr-dark: #182433;--tblr-dark-rgb: 24, 36, 51;--tblr-dark-fg: var(--tblr-light);--tblr-dark-darken: #16202e;--tblr-dark-lt: #e8e9eb;--tblr-dark-lt-rgb: 232, 233, 235;--tblr-muted: #6c7a91;--tblr-muted-rgb: 108, 122, 145;--tblr-muted-fg: var(--tblr-light);--tblr-muted-darken: #616e83;--tblr-muted-lt: #f0f2f4;--tblr-muted-lt-rgb: 240, 242, 244;--tblr-blue: #066fd1;--tblr-blue-rgb: 6, 111, 209;--tblr-blue-fg: var(--tblr-light);--tblr-blue-darken: #0564bc;--tblr-blue-lt: #e6f1fa;--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: #3b8acb;--tblr-azure-lt: #ecf5fc;--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: #3b59d4;--tblr-indigo-lt: #eceffd;--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: #9d38b5;--tblr-purple-lt: #f7ecfa;--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: #c12e61;--tblr-pink-lt: #fbebf0;--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: #c13333;--tblr-red-lt: #fbebeb;--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: #de5d06;--tblr-orange-lt: #fef0e6;--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: #dd8f00;--tblr-yellow-lt: #fef5e6;--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: #68a614;--tblr-lime-lt: #f1f8e8;--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: #2aa13d;--tblr-green-lt: #eaf7ec;--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: #0b956c;--tblr-teal-lt: #e7f6f2;--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: #1592a6;--tblr-cyan-lt: #e8f6f8;--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-lt: #e6e6e6;--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: #166bda;--tblr-facebook-lt: #e8f1fe;--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: #1a91da;--tblr-twitter-lt: #e8f6fe;--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: #095caf;--tblr-linkedin-lt: #e7f0f9;--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: #c6463b;--tblr-google-lt: #fcedec;--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: #e60000;--tblr-youtube-lt: #ffe6e6;--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: #17a5d3;--tblr-vimeo-lt: #e8f8fd;--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: #d3447b;--tblr-dribbble-lt: #fdedf3;--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: #161515;--tblr-github-lt: #e8e8e8;--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: #cd3a56;--tblr-instagram-lt: #fcecef;--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: #aa0719;--tblr-pinterest-lt: #f8e6e8;--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: #597697;--tblr-vk-lt: #eff3f6;--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: #e69500;--tblr-rss-lt: #fff6e6;--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: #0059c6;--tblr-flickr-lt: #e6effc;--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: #004ab8;--tblr-bitbucket-lt: #e6eefa;--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: #0564bc;--tblr-tabler-lt: #e6f1fa;--tblr-tabler-lt-rgb: 230, 241, 250;--tblr-gray-50: #f6f8fb;--tblr-gray-50-rgb: 246, 248, 251;--tblr-gray-50-fg: var(--tblr-dark);--tblr-gray-50-darken: #dddfe2;--tblr-gray-50-lt: #fefeff;--tblr-gray-50-lt-rgb: 254, 254, 255;--tblr-gray-100: #eef3f6;--tblr-gray-100-rgb: 238, 243, 246;--tblr-gray-100-fg: var(--tblr-dark);--tblr-gray-100-darken: #d6dbdd;--tblr-gray-100-lt: #fdfefe;--tblr-gray-100-lt-rgb: 253, 254, 254;--tblr-gray-200: #dce1e7;--tblr-gray-200-rgb: 220, 225, 231;--tblr-gray-200-fg: var(--tblr-dark);--tblr-gray-200-darken: #c6cbd0;--tblr-gray-200-lt: #fcfcfd;--tblr-gray-200-lt-rgb: 252, 252, 253;--tblr-gray-300: #b8c4d4;--tblr-gray-300-rgb: 184, 196, 212;--tblr-gray-300-fg: var(--tblr-light);--tblr-gray-300-darken: #a6b0bf;--tblr-gray-300-lt: #f8f9fb;--tblr-gray-300-lt-rgb: 248, 249, 251;--tblr-gray-400: #8a97ab;--tblr-gray-400-rgb: 138, 151, 171;--tblr-gray-400-fg: var(--tblr-light);--tblr-gray-400-darken: #7c889a;--tblr-gray-400-lt: #f3f5f7;--tblr-gray-400-lt-rgb: 243, 245, 247;--tblr-gray-500: #6c7a91;--tblr-gray-500-rgb: 108, 122, 145;--tblr-gray-500-fg: var(--tblr-light);--tblr-gray-500-darken: #616e83;--tblr-gray-500-lt: #f0f2f4;--tblr-gray-500-lt-rgb: 240, 242, 244;--tblr-gray-600: #49566c;--tblr-gray-600-rgb: 73, 86, 108;--tblr-gray-600-fg: var(--tblr-light);--tblr-gray-600-darken: #424d61;--tblr-gray-600-lt: #edeef0;--tblr-gray-600-lt-rgb: 237, 238, 240;--tblr-gray-700: #3a4859;--tblr-gray-700-rgb: 58, 72, 89;--tblr-gray-700-fg: var(--tblr-light);--tblr-gray-700-darken: #344150;--tblr-gray-700-lt: #ebedee;--tblr-gray-700-lt-rgb: 235, 237, 238;--tblr-gray-800: #182433;--tblr-gray-800-rgb: 24, 36, 51;--tblr-gray-800-fg: var(--tblr-light);--tblr-gray-800-darken: #16202e;--tblr-gray-800-lt: #e8e9eb;--tblr-gray-800-lt-rgb: 232, 233, 235;--tblr-gray-900: #040a11;--tblr-gray-900-rgb: 4, 10, 17;--tblr-gray-900-fg: var(--tblr-light);--tblr-gray-900-darken: #04090f;--tblr-gray-900-lt: #e6e7e7;--tblr-gray-900-lt-rgb: 230, 231, 231;--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: 3rem;--tblr-spacer-7: 5rem;--tblr-spacer-8: 8rem;--tblr-spacer: 1rem;--tblr-bg-surface: var(--tblr-white);--tblr-bg-surface-secondary: var(--tblr-gray-100);--tblr-bg-surface-tertiary: var(--tblr-gray-50);--tblr-bg-surface-dark: var(--tblr-dark);--tblr-bg-forms: var(--tblr-bg-surface);--tblr-border-color: #dce1e7;--tblr-border-color-translucent: rgba(4, 32, 69, .1);--tblr-border-dark-color: #8a97ab;--tblr-border-dark-color-translucent: rgba(4, 32, 69, .27);--tblr-border-active-color: #aab2bf;--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: var(--tblr-gray-300);--tblr-code-color: var(--tblr-gray-600);--tblr-code-bg: var(--tblr-bg-surface-secondary);--tblr-dark-mode-border-color: #25384f;--tblr-dark-mode-border-color-translucent: rgba(72, 110, 149, .14);--tblr-dark-mode-border-active-color: #2c415d;--tblr-dark-mode-border-dark-color: #1f2e41;--tblr-page-padding: var(--tblr-spacer-3);--tblr-page-padding-y: var(--tblr-spacer-4);--tblr-font-weight-light: 300;--tblr-font-weight-normal: 400;--tblr-font-weight-medium: 500;--tblr-font-weight-bold: 600;--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-box-shadow: rgba(var(--tblr-body-color-rgb), .04) 0 2px 4px 0;--tblr-box-shadow-border: inset 0 0 0 1px var(--tblr-border-color-translucent);--tblr-box-shadow-transparent: 0 0 0 0 transparent;--tblr-box-shadow-input: 0 1px 1px rgba(var(--tblr-body-color-rgb), .06);--tblr-box-shadow-card: 0 0 4px rgba(var(--tblr-body-color-rgb), .04);--tblr-box-shadow-card-hover: rgba(var(--tblr-body-color-rgb), .16) 0 2px 16px 0;--tblr-box-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)}@media (max-width: 991.98px){:root,:host,[data-bs-theme=light]{--tblr-page-padding: var(--tblr-spacer-2)}}@keyframes pulse{0%{opacity:1;transform:scale3d(.8,.8,.8)}50%{transform:scaleZ(1);opacity:1}to{opacity:1;transform:scale3d(.8,.8,.8)}}@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}}body{letter-spacing:0;touch-action:manipulation;text-rendering:optimizeLegibility;font-feature-settings:"liga" 0;position:relative;min-height:100%;height:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media print{body{background:transparent}}*{scrollbar-color:rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.16)}*::-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 rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.16)}*::-webkit-scrollbar-track{background:transparent}*:hover::-webkit-scrollbar-thumb{box-shadow:inset 0 0 0 1rem rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.32)}*::-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:#182433 linear-gradient(to right,rgba(255,255,255,.1),transparent) fixed;padding:1rem;--tblr-theme-boxed-border-radius: 4px}}.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, .06);--tblr-navbar-color: var(--tblr-body-color);--tblr-navbar-border-color: var(--tblr-border-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:2rem;min-height:2rem;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%)}.navbar-nav{margin:0;padding:0}@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-gray-300);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: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{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{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-gray-300);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: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{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{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-gray-300);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: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{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{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-gray-300);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: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{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{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-gray-300);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: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{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{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-gray-300);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: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{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{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;position:relative}@media (prefers-reduced-motion: reduce){.navbar-toggler-icon{transition:none}}.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{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{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{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-gray-300);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{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{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-gray-300);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{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{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-gray-300);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{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{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-gray-300);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{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{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-gray-300);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;overflow-y:scroll;padding:0}@media (prefers-reduced-motion: reduce){.navbar-vertical.navbar-expand{transition:none}}.navbar-vertical.navbar-expand.navbar-right{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{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-gray-300);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}.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:.625rem;font-weight:var(--tblr-font-weight-bold);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:#fff;padding:2rem 0;color:var(--tblr-gray-500);margin-top:auto}.footer-transparent{background-color:transparent;border-top:0}body:not(.theme-dark):not([data-bs-theme=dark]) .hide-theme-light{display:none!important}body:not(.theme-dark):not([data-bs-theme=dark]) .img-dark{display:none!important}body.theme-dark .hide-theme-dark,body[data-bs-theme=dark] .hide-theme-dark,body.theme-dark .img-light,body[data-bs-theme=dark] .img-light{display:none!important}[data-bs-theme=dark],body[data-bs-theme=dark] [data-bs-theme=light]{--tblr-body-color: #dce1e7;--tblr-body-color-rgb: 220, 225, 231;--tblr-muted: #49566c;--tblr-body-bg: #151f2c;--tblr-body-bg-rgb: 21, 31, 44;--tblr-emphasis-color: #ffffff;--tblr-emphasis-color-rgb: 255, 255, 255;--tblr-bg-forms: #151f2c;--tblr-bg-surface: #182433;--tblr-bg-surface-dark: #151f2c;--tblr-bg-surface-secondary: #1b293a;--tblr-bg-surface-tertiary: #151f2c;--tblr-link-color: #00aea3;--tblr-link-hover-color: #00857D;--tblr-active-bg: #1b293a;--tblr-disabled-color: var(--tblr-gray-700);--tblr-border-color: var(--tblr-dark-mode-border-color);--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: #151f2c;--tblr-code-color: var(--tblr-body-color);--tblr-code-bg: #1f2e41;--tblr-primary-lt: #162e3a;--tblr-primary-lt-rgb: 22, 46, 58;--tblr-secondary-lt: #202d3c;--tblr-secondary-lt-rgb: 32, 45, 60;--tblr-success-lt: #1a3235;--tblr-success-lt-rgb: 26, 50, 53;--tblr-info-lt: #1c3044;--tblr-info-lt-rgb: 28, 48, 68;--tblr-warning-lt: #2e2b2f;--tblr-warning-lt-rgb: 46, 43, 47;--tblr-danger-lt: #2b2634;--tblr-danger-lt-rgb: 43, 38, 52;--tblr-light-lt: #2e3947;--tblr-light-lt-rgb: 46, 57, 71;--tblr-dark-lt: #182433;--tblr-dark-lt-rgb: 24, 36, 51;--tblr-muted-lt: #202d3c;--tblr-muted-lt-rgb: 32, 45, 60;--tblr-blue-lt: #162c43;--tblr-blue-lt-rgb: 22, 44, 67;--tblr-azure-lt: #1c3044;--tblr-azure-lt-rgb: 28, 48, 68;--tblr-indigo-lt: #1c2a45;--tblr-indigo-lt-rgb: 28, 42, 69;--tblr-purple-lt: #272742;--tblr-purple-lt-rgb: 39, 39, 66;--tblr-pink-lt: #2b2639;--tblr-pink-lt-rgb: 43, 38, 57;--tblr-red-lt: #2b2634;--tblr-red-lt-rgb: 43, 38, 52;--tblr-orange-lt: #2e2b2f;--tblr-orange-lt-rgb: 46, 43, 47;--tblr-yellow-lt: #2e302e;--tblr-yellow-lt-rgb: 46, 48, 46;--tblr-lime-lt: #213330;--tblr-lime-lt-rgb: 33, 51, 48;--tblr-green-lt: #1a3235;--tblr-green-lt-rgb: 26, 50, 53;--tblr-teal-lt: #17313a;--tblr-teal-lt-rgb: 23, 49, 58;--tblr-cyan-lt: #183140;--tblr-cyan-lt-rgb: 24, 49, 64;--tblr-x-lt: #16202e;--tblr-x-lt-rgb: 22, 32, 46;--tblr-facebook-lt: #182c46;--tblr-facebook-lt-rgb: 24, 44, 70;--tblr-twitter-lt: #193146;--tblr-twitter-lt-rgb: 25, 49, 70;--tblr-linkedin-lt: #172b41;--tblr-linkedin-lt-rgb: 23, 43, 65;--tblr-google-lt: #2c2834;--tblr-google-lt-rgb: 44, 40, 52;--tblr-youtube-lt: #2f202e;--tblr-youtube-lt-rgb: 47, 32, 46;--tblr-vimeo-lt: #183345;--tblr-vimeo-lt-rgb: 24, 51, 69;--tblr-dribbble-lt: #2d283c;--tblr-dribbble-lt-rgb: 45, 40, 60;--tblr-github-lt: #182330;--tblr-github-lt-rgb: 24, 35, 48;--tblr-instagram-lt: #2c2737;--tblr-instagram-lt-rgb: 44, 39, 55;--tblr-pinterest-lt: #292131;--tblr-pinterest-lt-rgb: 41, 33, 49;--tblr-vk-lt: #202e3f;--tblr-vk-lt-rgb: 32, 46, 63;--tblr-rss-lt: #2f312e;--tblr-rss-lt-rgb: 47, 49, 46;--tblr-flickr-lt: #162a44;--tblr-flickr-lt-rgb: 22, 42, 68;--tblr-bitbucket-lt: #162942;--tblr-bitbucket-lt-rgb: 22, 41, 66;--tblr-tabler-lt: #162c43;--tblr-tabler-lt-rgb: 22, 44, 67}[data-bs-theme=dark] .navbar-brand-autodark .navbar-brand-image{filter:brightness(0) invert(1)}.accordion{--tblr-accordion-color: var(--tblr-body-color)}.accordion-button:focus:not(:focus-visible){outline:none;box-shadow:none}.accordion-button:after{opacity:.7}.accordion-button:not(.collapsed){font-weight:var(--tblr-font-weight-bold);border-bottom-color:transparent;box-shadow:none}.accordion-button:not(.collapsed):after{opacity:1}.alert{--tblr-alert-color: var(--tblr-secondary);--tblr-alert-bg: var(--tblr-bg-surface);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent);border-left:.25rem var(--tblr-border-style) var(--tblr-alert-color);box-shadow:#1824330a 0 2px 4px}.alert>:last-child{margin-bottom:0}.alert-important{border-color:transparent;background:var(--tblr-alert-color);color:#fff}.alert-important .alert-icon,.alert-important .alert-link,.alert-important .alert-title,.alert-important .alert-link:hover{color:inherit}.alert-important .btn-close{filter:var(--tblr-btn-close-white-filter)}.alert-link,.alert-link:hover{color:var(--tblr-alert-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)}.alert-x{--tblr-alert-color: var(--tblr-x)}.alert-facebook{--tblr-alert-color: var(--tblr-facebook)}.alert-twitter{--tblr-alert-color: var(--tblr-twitter)}.alert-linkedin{--tblr-alert-color: var(--tblr-linkedin)}.alert-google{--tblr-alert-color: var(--tblr-google)}.alert-youtube{--tblr-alert-color: var(--tblr-youtube)}.alert-vimeo{--tblr-alert-color: var(--tblr-vimeo)}.alert-dribbble{--tblr-alert-color: var(--tblr-dribbble)}.alert-github{--tblr-alert-color: var(--tblr-github)}.alert-instagram{--tblr-alert-color: var(--tblr-instagram)}.alert-pinterest{--tblr-alert-color: var(--tblr-pinterest)}.alert-vk{--tblr-alert-color: var(--tblr-vk)}.alert-rss{--tblr-alert-color: var(--tblr-rss)}.alert-flickr{--tblr-alert-color: var(--tblr-flickr)}.alert-bitbucket{--tblr-alert-color: var(--tblr-bitbucket)}.alert-tabler{--tblr-alert-color: var(--tblr-tabler)}.alert-icon{color:var(--tblr-alert-color);width:1.5rem!important;height:1.5rem!important;margin:-.125rem 1rem -.125rem 0}.alert-title{font-size:.875rem;line-height:1.25rem;font-weight:var(--tblr-font-weight-bold);margin-bottom:.25rem;color:var(--tblr-alert-color)}.avatar{--tblr-avatar-size: 2.5rem;--tblr-avatar-status-size: .75rem;--tblr-avatar-bg: var(--tblr-bg-surface-secondary);--tblr-avatar-box-shadow: var(--tblr-box-shadow-border);--tblr-avatar-font-size: 1rem;--tblr-avatar-icon-size: 1.5rem;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)}.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}.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: .75rem}.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: 1rem}.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.25rem}.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}.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}.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}.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}.avatar-2xl .badge:empty{width:1rem;height:1rem}.avatar-list{--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-cap-bg, var(--tblr-card-bg, var(--tblr-bg-surface)))}.avatar-upload{width:4rem;height:4rem;border:var(--tblr-border-width) dashed var(--tblr-border-color);background:var(--tblr-bg-forms);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))}.badge{justify-content:center;align-items:center;background:var(--tblr-bg-surface-secondary);overflow:hidden;user-select:none;border:var(--tblr-border-width) var(--tblr-border-style) transparent;min-width:1.35714285em;font-weight:var(--tblr-font-weight-bold);letter-spacing:.04em;vertical-align:bottom}a.badge{color:var(--tblr-bg-surface)}.badge .avatar{box-sizing:content-box;width:1.25rem;height:1.25rem;margin:0 .5rem 0 -.5rem}.badge .icon{width:1em;height:1em;font-size:1rem;stroke-width:2}.badge:empty,.badge-empty{display:inline-block;width:.5rem;height:.5rem;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}.breadcrumb{--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);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{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-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-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-box-shadow: var(--tblr-box-shadow-input);--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)}.btn .icon{width:var(--tblr-btn-icon-size);height:var(--tblr-btn-icon-size);min-width: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:inherit}.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{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);--tblr-btn-bg: var(--tblr-primary);--tblr-btn-hover-color: var(--tblr-primary-fg);--tblr-btn-hover-bg: rgba(var(--tblr-primary-rgb), .8);--tblr-btn-active-color: var(--tblr-primary-fg);--tblr-btn-active-bg: rgba(var(--tblr-primary-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-primary);--tblr-btn-disabled-color: var(--tblr-primary-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-primary);--tblr-btn-disabled-border-color: var(--tblr-primary)}.btn-secondary{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-secondary-fg);--tblr-btn-bg: var(--tblr-secondary);--tblr-btn-hover-color: var(--tblr-secondary-fg);--tblr-btn-hover-bg: rgba(var(--tblr-secondary-rgb), .8);--tblr-btn-active-color: var(--tblr-secondary-fg);--tblr-btn-active-bg: rgba(var(--tblr-secondary-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-secondary);--tblr-btn-disabled-color: var(--tblr-secondary-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-secondary{--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-disabled-color: var(--tblr-secondary);--tblr-btn-disabled-border-color: var(--tblr-secondary)}.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);--tblr-btn-bg: var(--tblr-success);--tblr-btn-hover-color: var(--tblr-success-fg);--tblr-btn-hover-bg: rgba(var(--tblr-success-rgb), .8);--tblr-btn-active-color: var(--tblr-success-fg);--tblr-btn-active-bg: rgba(var(--tblr-success-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-success);--tblr-btn-disabled-color: var(--tblr-success-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-success);--tblr-btn-disabled-border-color: var(--tblr-success)}.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);--tblr-btn-bg: var(--tblr-info);--tblr-btn-hover-color: var(--tblr-info-fg);--tblr-btn-hover-bg: rgba(var(--tblr-info-rgb), .8);--tblr-btn-active-color: var(--tblr-info-fg);--tblr-btn-active-bg: rgba(var(--tblr-info-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-info);--tblr-btn-disabled-color: var(--tblr-info-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-info);--tblr-btn-disabled-border-color: var(--tblr-info)}.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);--tblr-btn-bg: var(--tblr-warning);--tblr-btn-hover-color: var(--tblr-warning-fg);--tblr-btn-hover-bg: rgba(var(--tblr-warning-rgb), .8);--tblr-btn-active-color: var(--tblr-warning-fg);--tblr-btn-active-bg: rgba(var(--tblr-warning-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-warning);--tblr-btn-disabled-color: var(--tblr-warning-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-warning);--tblr-btn-disabled-border-color: var(--tblr-warning)}.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);--tblr-btn-bg: var(--tblr-danger);--tblr-btn-hover-color: var(--tblr-danger-fg);--tblr-btn-hover-bg: rgba(var(--tblr-danger-rgb), .8);--tblr-btn-active-color: var(--tblr-danger-fg);--tblr-btn-active-bg: rgba(var(--tblr-danger-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-danger);--tblr-btn-disabled-color: var(--tblr-danger-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-danger);--tblr-btn-disabled-border-color: var(--tblr-danger)}.btn-light{--tblr-btn-border-color: transparent;--tblr-btn-hover-border-color: transparent;--tblr-btn-active-border-color: transparent;--tblr-btn-color: var(--tblr-light-fg);--tblr-btn-bg: var(--tblr-light);--tblr-btn-hover-color: var(--tblr-light-fg);--tblr-btn-hover-bg: rgba(var(--tblr-light-rgb), .8);--tblr-btn-active-color: var(--tblr-light-fg);--tblr-btn-active-bg: rgba(var(--tblr-light-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-light);--tblr-btn-disabled-color: var(--tblr-light-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-light{--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-disabled-color: var(--tblr-light);--tblr-btn-disabled-border-color: var(--tblr-light)}.btn-dark{--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);--tblr-btn-bg: var(--tblr-dark);--tblr-btn-hover-color: var(--tblr-dark-fg);--tblr-btn-hover-bg: rgba(var(--tblr-dark-rgb), .8);--tblr-btn-active-color: var(--tblr-dark-fg);--tblr-btn-active-bg: rgba(var(--tblr-dark-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-dark);--tblr-btn-disabled-color: var(--tblr-dark-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-dark{--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-disabled-color: var(--tblr-dark);--tblr-btn-disabled-border-color: var(--tblr-dark)}.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);--tblr-btn-bg: var(--tblr-muted);--tblr-btn-hover-color: var(--tblr-muted-fg);--tblr-btn-hover-bg: rgba(var(--tblr-muted-rgb), .8);--tblr-btn-active-color: var(--tblr-muted-fg);--tblr-btn-active-bg: rgba(var(--tblr-muted-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-muted);--tblr-btn-disabled-color: var(--tblr-muted-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-muted);--tblr-btn-disabled-border-color: var(--tblr-muted)}.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);--tblr-btn-bg: var(--tblr-blue);--tblr-btn-hover-color: var(--tblr-blue-fg);--tblr-btn-hover-bg: rgba(var(--tblr-blue-rgb), .8);--tblr-btn-active-color: var(--tblr-blue-fg);--tblr-btn-active-bg: rgba(var(--tblr-blue-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-blue);--tblr-btn-disabled-color: var(--tblr-blue-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-blue);--tblr-btn-disabled-border-color: var(--tblr-blue)}.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);--tblr-btn-bg: var(--tblr-azure);--tblr-btn-hover-color: var(--tblr-azure-fg);--tblr-btn-hover-bg: rgba(var(--tblr-azure-rgb), .8);--tblr-btn-active-color: var(--tblr-azure-fg);--tblr-btn-active-bg: rgba(var(--tblr-azure-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-azure);--tblr-btn-disabled-color: var(--tblr-azure-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-azure);--tblr-btn-disabled-border-color: var(--tblr-azure)}.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);--tblr-btn-bg: var(--tblr-indigo);--tblr-btn-hover-color: var(--tblr-indigo-fg);--tblr-btn-hover-bg: rgba(var(--tblr-indigo-rgb), .8);--tblr-btn-active-color: var(--tblr-indigo-fg);--tblr-btn-active-bg: rgba(var(--tblr-indigo-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-indigo);--tblr-btn-disabled-color: var(--tblr-indigo-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-indigo);--tblr-btn-disabled-border-color: var(--tblr-indigo)}.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);--tblr-btn-bg: var(--tblr-purple);--tblr-btn-hover-color: var(--tblr-purple-fg);--tblr-btn-hover-bg: rgba(var(--tblr-purple-rgb), .8);--tblr-btn-active-color: var(--tblr-purple-fg);--tblr-btn-active-bg: rgba(var(--tblr-purple-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-purple);--tblr-btn-disabled-color: var(--tblr-purple-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-purple);--tblr-btn-disabled-border-color: var(--tblr-purple)}.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);--tblr-btn-bg: var(--tblr-pink);--tblr-btn-hover-color: var(--tblr-pink-fg);--tblr-btn-hover-bg: rgba(var(--tblr-pink-rgb), .8);--tblr-btn-active-color: var(--tblr-pink-fg);--tblr-btn-active-bg: rgba(var(--tblr-pink-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-pink);--tblr-btn-disabled-color: var(--tblr-pink-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-pink);--tblr-btn-disabled-border-color: var(--tblr-pink)}.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);--tblr-btn-bg: var(--tblr-red);--tblr-btn-hover-color: var(--tblr-red-fg);--tblr-btn-hover-bg: rgba(var(--tblr-red-rgb), .8);--tblr-btn-active-color: var(--tblr-red-fg);--tblr-btn-active-bg: rgba(var(--tblr-red-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-red);--tblr-btn-disabled-color: var(--tblr-red-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-red);--tblr-btn-disabled-border-color: var(--tblr-red)}.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);--tblr-btn-bg: var(--tblr-orange);--tblr-btn-hover-color: var(--tblr-orange-fg);--tblr-btn-hover-bg: rgba(var(--tblr-orange-rgb), .8);--tblr-btn-active-color: var(--tblr-orange-fg);--tblr-btn-active-bg: rgba(var(--tblr-orange-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-orange);--tblr-btn-disabled-color: var(--tblr-orange-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-orange);--tblr-btn-disabled-border-color: var(--tblr-orange)}.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);--tblr-btn-bg: var(--tblr-yellow);--tblr-btn-hover-color: var(--tblr-yellow-fg);--tblr-btn-hover-bg: rgba(var(--tblr-yellow-rgb), .8);--tblr-btn-active-color: var(--tblr-yellow-fg);--tblr-btn-active-bg: rgba(var(--tblr-yellow-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-yellow);--tblr-btn-disabled-color: var(--tblr-yellow-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-yellow);--tblr-btn-disabled-border-color: var(--tblr-yellow)}.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);--tblr-btn-bg: var(--tblr-lime);--tblr-btn-hover-color: var(--tblr-lime-fg);--tblr-btn-hover-bg: rgba(var(--tblr-lime-rgb), .8);--tblr-btn-active-color: var(--tblr-lime-fg);--tblr-btn-active-bg: rgba(var(--tblr-lime-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-lime);--tblr-btn-disabled-color: var(--tblr-lime-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-lime);--tblr-btn-disabled-border-color: var(--tblr-lime)}.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);--tblr-btn-bg: var(--tblr-green);--tblr-btn-hover-color: var(--tblr-green-fg);--tblr-btn-hover-bg: rgba(var(--tblr-green-rgb), .8);--tblr-btn-active-color: var(--tblr-green-fg);--tblr-btn-active-bg: rgba(var(--tblr-green-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-green);--tblr-btn-disabled-color: var(--tblr-green-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-green);--tblr-btn-disabled-border-color: var(--tblr-green)}.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);--tblr-btn-bg: var(--tblr-teal);--tblr-btn-hover-color: var(--tblr-teal-fg);--tblr-btn-hover-bg: rgba(var(--tblr-teal-rgb), .8);--tblr-btn-active-color: var(--tblr-teal-fg);--tblr-btn-active-bg: rgba(var(--tblr-teal-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-teal);--tblr-btn-disabled-color: var(--tblr-teal-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-teal);--tblr-btn-disabled-border-color: var(--tblr-teal)}.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);--tblr-btn-bg: var(--tblr-cyan);--tblr-btn-hover-color: var(--tblr-cyan-fg);--tblr-btn-hover-bg: rgba(var(--tblr-cyan-rgb), .8);--tblr-btn-active-color: var(--tblr-cyan-fg);--tblr-btn-active-bg: rgba(var(--tblr-cyan-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-cyan);--tblr-btn-disabled-color: var(--tblr-cyan-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-cyan);--tblr-btn-disabled-border-color: var(--tblr-cyan)}.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);--tblr-btn-bg: var(--tblr-x);--tblr-btn-hover-color: var(--tblr-x-fg);--tblr-btn-hover-bg: rgba(var(--tblr-x-rgb), .8);--tblr-btn-active-color: var(--tblr-x-fg);--tblr-btn-active-bg: rgba(var(--tblr-x-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-x);--tblr-btn-disabled-color: var(--tblr-x-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-x);--tblr-btn-disabled-border-color: var(--tblr-x)}.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);--tblr-btn-bg: var(--tblr-facebook);--tblr-btn-hover-color: var(--tblr-facebook-fg);--tblr-btn-hover-bg: rgba(var(--tblr-facebook-rgb), .8);--tblr-btn-active-color: var(--tblr-facebook-fg);--tblr-btn-active-bg: rgba(var(--tblr-facebook-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-facebook);--tblr-btn-disabled-color: var(--tblr-facebook-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-facebook);--tblr-btn-disabled-border-color: var(--tblr-facebook)}.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);--tblr-btn-bg: var(--tblr-twitter);--tblr-btn-hover-color: var(--tblr-twitter-fg);--tblr-btn-hover-bg: rgba(var(--tblr-twitter-rgb), .8);--tblr-btn-active-color: var(--tblr-twitter-fg);--tblr-btn-active-bg: rgba(var(--tblr-twitter-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-twitter);--tblr-btn-disabled-color: var(--tblr-twitter-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-twitter);--tblr-btn-disabled-border-color: var(--tblr-twitter)}.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);--tblr-btn-bg: var(--tblr-linkedin);--tblr-btn-hover-color: var(--tblr-linkedin-fg);--tblr-btn-hover-bg: rgba(var(--tblr-linkedin-rgb), .8);--tblr-btn-active-color: var(--tblr-linkedin-fg);--tblr-btn-active-bg: rgba(var(--tblr-linkedin-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-linkedin);--tblr-btn-disabled-color: var(--tblr-linkedin-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-linkedin);--tblr-btn-disabled-border-color: var(--tblr-linkedin)}.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);--tblr-btn-bg: var(--tblr-google);--tblr-btn-hover-color: var(--tblr-google-fg);--tblr-btn-hover-bg: rgba(var(--tblr-google-rgb), .8);--tblr-btn-active-color: var(--tblr-google-fg);--tblr-btn-active-bg: rgba(var(--tblr-google-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-google);--tblr-btn-disabled-color: var(--tblr-google-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-google);--tblr-btn-disabled-border-color: var(--tblr-google)}.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);--tblr-btn-bg: var(--tblr-youtube);--tblr-btn-hover-color: var(--tblr-youtube-fg);--tblr-btn-hover-bg: rgba(var(--tblr-youtube-rgb), .8);--tblr-btn-active-color: var(--tblr-youtube-fg);--tblr-btn-active-bg: rgba(var(--tblr-youtube-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-youtube);--tblr-btn-disabled-color: var(--tblr-youtube-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-youtube);--tblr-btn-disabled-border-color: var(--tblr-youtube)}.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);--tblr-btn-bg: var(--tblr-vimeo);--tblr-btn-hover-color: var(--tblr-vimeo-fg);--tblr-btn-hover-bg: rgba(var(--tblr-vimeo-rgb), .8);--tblr-btn-active-color: var(--tblr-vimeo-fg);--tblr-btn-active-bg: rgba(var(--tblr-vimeo-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-vimeo);--tblr-btn-disabled-color: var(--tblr-vimeo-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-vimeo);--tblr-btn-disabled-border-color: var(--tblr-vimeo)}.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);--tblr-btn-bg: var(--tblr-dribbble);--tblr-btn-hover-color: var(--tblr-dribbble-fg);--tblr-btn-hover-bg: rgba(var(--tblr-dribbble-rgb), .8);--tblr-btn-active-color: var(--tblr-dribbble-fg);--tblr-btn-active-bg: rgba(var(--tblr-dribbble-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-dribbble);--tblr-btn-disabled-color: var(--tblr-dribbble-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-dribbble);--tblr-btn-disabled-border-color: var(--tblr-dribbble)}.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);--tblr-btn-bg: var(--tblr-github);--tblr-btn-hover-color: var(--tblr-github-fg);--tblr-btn-hover-bg: rgba(var(--tblr-github-rgb), .8);--tblr-btn-active-color: var(--tblr-github-fg);--tblr-btn-active-bg: rgba(var(--tblr-github-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-github);--tblr-btn-disabled-color: var(--tblr-github-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-github);--tblr-btn-disabled-border-color: var(--tblr-github)}.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);--tblr-btn-bg: var(--tblr-instagram);--tblr-btn-hover-color: var(--tblr-instagram-fg);--tblr-btn-hover-bg: rgba(var(--tblr-instagram-rgb), .8);--tblr-btn-active-color: var(--tblr-instagram-fg);--tblr-btn-active-bg: rgba(var(--tblr-instagram-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-instagram);--tblr-btn-disabled-color: var(--tblr-instagram-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-instagram);--tblr-btn-disabled-border-color: var(--tblr-instagram)}.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);--tblr-btn-bg: var(--tblr-pinterest);--tblr-btn-hover-color: var(--tblr-pinterest-fg);--tblr-btn-hover-bg: rgba(var(--tblr-pinterest-rgb), .8);--tblr-btn-active-color: var(--tblr-pinterest-fg);--tblr-btn-active-bg: rgba(var(--tblr-pinterest-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-pinterest);--tblr-btn-disabled-color: var(--tblr-pinterest-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-pinterest);--tblr-btn-disabled-border-color: var(--tblr-pinterest)}.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);--tblr-btn-bg: var(--tblr-vk);--tblr-btn-hover-color: var(--tblr-vk-fg);--tblr-btn-hover-bg: rgba(var(--tblr-vk-rgb), .8);--tblr-btn-active-color: var(--tblr-vk-fg);--tblr-btn-active-bg: rgba(var(--tblr-vk-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-vk);--tblr-btn-disabled-color: var(--tblr-vk-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-vk);--tblr-btn-disabled-border-color: var(--tblr-vk)}.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);--tblr-btn-bg: var(--tblr-rss);--tblr-btn-hover-color: var(--tblr-rss-fg);--tblr-btn-hover-bg: rgba(var(--tblr-rss-rgb), .8);--tblr-btn-active-color: var(--tblr-rss-fg);--tblr-btn-active-bg: rgba(var(--tblr-rss-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-rss);--tblr-btn-disabled-color: var(--tblr-rss-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-rss);--tblr-btn-disabled-border-color: var(--tblr-rss)}.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);--tblr-btn-bg: var(--tblr-flickr);--tblr-btn-hover-color: var(--tblr-flickr-fg);--tblr-btn-hover-bg: rgba(var(--tblr-flickr-rgb), .8);--tblr-btn-active-color: var(--tblr-flickr-fg);--tblr-btn-active-bg: rgba(var(--tblr-flickr-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-flickr);--tblr-btn-disabled-color: var(--tblr-flickr-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-flickr);--tblr-btn-disabled-border-color: var(--tblr-flickr)}.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);--tblr-btn-bg: var(--tblr-bitbucket);--tblr-btn-hover-color: var(--tblr-bitbucket-fg);--tblr-btn-hover-bg: rgba(var(--tblr-bitbucket-rgb), .8);--tblr-btn-active-color: var(--tblr-bitbucket-fg);--tblr-btn-active-bg: rgba(var(--tblr-bitbucket-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-bitbucket);--tblr-btn-disabled-color: var(--tblr-bitbucket-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-bitbucket);--tblr-btn-disabled-border-color: var(--tblr-bitbucket)}.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);--tblr-btn-bg: var(--tblr-tabler);--tblr-btn-hover-color: var(--tblr-tabler-fg);--tblr-btn-hover-bg: rgba(var(--tblr-tabler-rgb), .8);--tblr-btn-active-color: var(--tblr-tabler-fg);--tblr-btn-active-bg: rgba(var(--tblr-tabler-rgb), .8);--tblr-btn-disabled-bg: var(--tblr-tabler);--tblr-btn-disabled-color: var(--tblr-tabler-fg);--tblr-btn-box-shadow: var(--tblr-box-shadow-input)}.btn-outline-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-disabled-color: var(--tblr-tabler);--tblr-btn-disabled-border-color: var(--tblr-tabler)}.btn-ghost-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-ghost-secondary{--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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-light{--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-ghost-dark{--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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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-ghost-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.5;--tblr-btn-icon-size: .75rem}.btn-lg,.btn-group-lg>.btn{--tblr-btn-line-height: 1.5;--tblr-btn-icon-size: 2rem}.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{min-width:calc(var(--tblr-btn-line-height) * var(--tblr-btn-font-size) + var(--tblr-btn-padding-y) * 2 + var(--tblr-btn-border-width) * 2);min-height:calc(var(--tblr-btn-line-height) * var(--tblr-btn-font-size) + var(--tblr-btn-padding-y) * 2 + var(--tblr-btn-border-width) * 2);padding-left:0;padding-right:0}.btn-icon .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:1.5rem;right:1.5rem;border-radius:100rem}.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{padding:0;border:0;color:var(--tblr-secondary);display:inline-flex;width:2rem;height:2rem;align-items:center;justify-content:center;border-radius:var(--tblr-border-radius);background:transparent}.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)}.btn-action.show{color:var(--tblr-primary)}.btn-action .icon{margin:0;width:1.25rem;height:1.25rem;font-size:1.25rem;stroke-width:1}.btn-actions{display:flex}.btn-group,.btn-group-vertical{box-shadow:var(--tblr-box-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,#182433e6,#18243300)}.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:4px;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) - (var(--tblr-border-width)));border-bottom-left-radius:calc(var(--tblr-border-radius) - (var(--tblr-border-width)))}.card-img,.card-img-end{border-top-right-radius:calc(var(--tblr-border-radius) - (var(--tblr-border-width)));border-bottom-right-radius:calc(var(--tblr-border-radius) - (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;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)}@media (prefers-reduced-motion: reduce){.card-btn{transition:none}}.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:#1824337a}.card-cover:first-child,.card-cover:first-child:before{border-radius:4px 4px 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}.card-header-pills{flex:1;margin-top:-.5rem;margin-bottom:-.5rem}.card-rotate-left{transform:rotate(-1.5deg)}.card-rotate-right{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) var(--tblr-border-radius)}.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{cursor:pointer}.btn-close:focus{outline:none}.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}.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:.625rem;font-weight:var(--tblr-font-weight-bold);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}.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:.625rem;font-weight:var(--tblr-font-weight-bold);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:45rem}.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:3rem}.space-x-6{display:flex;gap:3rem}.space-y-7{display:flex;flex-direction:column;gap:5rem}.space-x-7{display:flex;gap:5rem}.space-y-8{display:flex;flex-direction:column;gap:8rem}.space-x-8{display:flex;gap:8rem}.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:3rem!important}.divide-y-6>:not(template):not(:last-child){padding-bottom:3rem!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:3rem!important}.divide-x-6>:not(template):not(:last-child){padding-right:3rem!important}.divide-y-7>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-7>:not(template):not(:first-child){padding-top:5rem!important}.divide-y-7>:not(template):not(:last-child){padding-bottom:5rem!important}.divide-x-7>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-7>:not(template):not(:first-child){padding-left:5rem!important}.divide-x-7>:not(template):not(:last-child){padding-right:5rem!important}.divide-y-8>:not(template)~:not(template){border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-y-8>:not(template):not(:first-child){padding-top:8rem!important}.divide-y-8>:not(template):not(:last-child){padding-bottom:8rem!important}.divide-x-8>:not(template)~:not(template){border-left:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color-translucent)!important}.divide-x-8>:not(template):not(:first-child){padding-left:8rem!important}.divide-x-8>:not(template):not(:last-child){padding-right:8rem!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-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%}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-gray-500)}.form-hint{display:block;color:var(--tblr-gray-500)}.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-gray-500)}.form-select:-moz-focusring{color:var(--tblr-body-color)}.form-control:-webkit-autofill{box-shadow:0 0 0 1000px var(--tblr-body-bg) inset;color:var(--tblr-body-color);-webkit-text-fill-color:var(--tblr-body-color)}.form-control:disabled,.form-control.disabled{color:var(--tblr-gray-500);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-body-bg);border:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color);border-radius:var(--tblr-border-radius)}.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-gray-500);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-box-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}.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:3px;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='%23182433' 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{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:3px}.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:2px;border-top-right-radius:2px}.form-imagecheck-image:last-child{border-bottom-right-radius:2px;border-bottom-left-radius:2px}.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.4285714286em + 1.125rem + calc(var(--tblr-border-width) * 2));margin:0;padding:.5625rem .75rem;font-size:.875rem;line-height:1.4285714286;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:3px;box-shadow:var(--tblr-box-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-box-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-box-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:active,.list-group-item:focus,.list-group-item:hover{background-color:rgba(var(--tblr-secondary-rgb),.08)}.list-group-item.disabled,.list-group-item:disabled{color:#6c7a91;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-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-item{padding:1rem 0}.list-separated-item:first-child{padding-top:0}.list-separated-item:last-child{padding-bottom:0}.list-separated-item+.list-separated-item{border-top:var(--tblr-border-width) var(--tblr-border-style) var(--tblr-border-color)}.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:rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.16)}.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 rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.16)}.modal-body::-webkit-scrollbar-track{background:transparent}.modal-body:hover::-webkit-scrollbar-thumb{box-shadow:inset 0 0 0 1rem rgba(var(--tblr-scrollbar-color, var(--tblr-body-color-rgb)),.32)}.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-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)}.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 -var(--tblr-border-width);border:0;border-bottom:2px var(--tblr-border-style) transparent;color:var(--tblr-secondary)}.nav-bordered .nav-link.active,.nav-bordered .nav-item.show .nav-link{color:var(--tblr-primary);border-color:var(--tblr-primary)}.nav-link{display:flex;transition:color .3s;align-items:center}@media (prefers-reduced-motion: reduce){.nav-link{transition:none}}.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:var(--tblr-icon-color)}.nav-link-icon svg{display:block;height:100%}.nav-fill .nav-item .nav-link{justify-content:center}.stars{display:inline-flex;color:#8a97ab;font-size:.75rem}.stars .star:not(:first-child){margin-left:.25rem}.pagination{user-select:none}.page-link{min-width:1.75rem;border-radius:var(--tblr-border-radius)}.page-item{text-align:center}.page-item:not(.active) .page-link:hover{background:transparent}.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)}@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:1.7142857143}.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>blockquote{font-size:1rem;margin:1.5rem 0;padding:.5rem 1.5rem}.markdown>img{border-radius:var(--tblr-border-radius)}.placeholder:not(.btn):not([class*=bg-]){background-color:currentColor!important}.placeholder:not(.avatar):not([class*=card-img-]){border-radius:var(--tblr-border-radius)}.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-azure{--tblr-steps-color: var(--tblr-azure)}.steps-indigo{--tblr-steps-color: var(--tblr-indigo)}.steps-purple{--tblr-steps-color: var(--tblr-purple)}.steps-pink{--tblr-steps-color: var(--tblr-pink)}.steps-red{--tblr-steps-color: var(--tblr-red)}.steps-orange{--tblr-steps-color: var(--tblr-orange)}.steps-yellow{--tblr-steps-color: var(--tblr-yellow)}.steps-lime{--tblr-steps-color: var(--tblr-lime)}.steps-green{--tblr-steps-color: var(--tblr-green)}.steps-teal{--tblr-steps-color: var(--tblr-teal)}.steps-cyan{--tblr-steps-color: var(--tblr-cyan)}.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: #6c7a91;--tblr-status-color-rgb: 108, 122, 145;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: #6c7a91;--tblr-status-color-rgb: 108, 122, 145}.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: #f76707;--tblr-status-color-rgb: 247, 103, 7}.status-danger{--tblr-status-color: #d63939;--tblr-status-color-rgb: 214, 57, 57}.status-light{--tblr-status-color: #f6f8fb;--tblr-status-color-rgb: 246, 248, 251}.status-dark{--tblr-status-color: #182433;--tblr-status-color-rgb: 24, 36, 51}.status-muted{--tblr-status-color: #6c7a91;--tblr-status-color-rgb: 108, 122, 145}.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-x{--tblr-status-color: #000000;--tblr-status-color-rgb: 0, 0, 0}.status-facebook{--tblr-status-color: #1877f2;--tblr-status-color-rgb: 24, 119, 242}.status-twitter{--tblr-status-color: #1da1f2;--tblr-status-color-rgb: 29, 161, 242}.status-linkedin{--tblr-status-color: #0a66c2;--tblr-status-color-rgb: 10, 102, 194}.status-google{--tblr-status-color: #dc4e41;--tblr-status-color-rgb: 220, 78, 65}.status-youtube{--tblr-status-color: #ff0000;--tblr-status-color-rgb: 255, 0, 0}.status-vimeo{--tblr-status-color: #1ab7ea;--tblr-status-color-rgb: 26, 183, 234}.status-dribbble{--tblr-status-color: #ea4c89;--tblr-status-color-rgb: 234, 76, 137}.status-github{--tblr-status-color: #181717;--tblr-status-color-rgb: 24, 23, 23}.status-instagram{--tblr-status-color: #e4405f;--tblr-status-color-rgb: 228, 64, 95}.status-pinterest{--tblr-status-color: #bd081c;--tblr-status-color-rgb: 189, 8, 28}.status-vk{--tblr-status-color: #6383a8;--tblr-status-color-rgb: 99, 131, 168}.status-rss{--tblr-status-color: #ffa500;--tblr-status-color-rgb: 255, 165, 0}.status-flickr{--tblr-status-color: #0063dc;--tblr-status-color-rgb: 0, 99, 220}.status-bitbucket{--tblr-status-color: #0052cc;--tblr-status-color-rgb: 0, 82, 204}.status-tabler{--tblr-status-color: #066fd1;--tblr-status-color-rgb: 6, 111, 209}.status-dot{--tblr-status-dot-color: var(--tblr-status-color, #6c7a91);--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, #6c7a91);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-right,.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-right .switch-icon-a,.switch-icon-slide-right .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-right .switch-icon-a,.switch-icon-slide-right .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-right .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-right .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-right.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-right.active .switch-icon-b,.switch-icon-slide-down.active .switch-icon-b{transform:translateY(0)}.switch-icon-slide-left .switch-icon-a{transform:translate(0)}.switch-icon-slide-left .switch-icon-b{transform:translate(100%)}.switch-icon-slide-left.active .switch-icon-a{transform:translate(-100%)}.switch-icon-slide-left.active .switch-icon-b,.switch-icon-slide-right .switch-icon-a{transform:translate(0)}.switch-icon-slide-right .switch-icon-b{transform:translate(-100%)}.switch-icon-slide-right.active .switch-icon-a{transform:translate(100%)}.switch-icon-slide-right.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{color:var(--tblr-gray-500);background:var(--tblr-bg-surface-tertiary);font-size:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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:.625rem;font-weight:var(--tblr-font-weight-bold);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;margin:-.5rem;padding:.5rem}@media (prefers-reduced-motion: reduce){.table-sort{transition:none}}.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}.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-box-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:#1824330a 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: #6c7a91}.toast-success{--tblr-toast-color: #2fb344}.toast-info{--tblr-toast-color: #4299e1}.toast-warning{--tblr-toast-color: #f76707}.toast-danger{--tblr-toast-color: #d63939}.toast-light{--tblr-toast-color: #f6f8fb}.toast-dark{--tblr-toast-color: #182433}.toast-muted{--tblr-toast-color: #6c7a91}.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}.toast-x{--tblr-toast-color: #000000}.toast-facebook{--tblr-toast-color: #1877f2}.toast-twitter{--tblr-toast-color: #1da1f2}.toast-linkedin{--tblr-toast-color: #0a66c2}.toast-google{--tblr-toast-color: #dc4e41}.toast-youtube{--tblr-toast-color: #ff0000}.toast-vimeo{--tblr-toast-color: #1ab7ea}.toast-dribbble{--tblr-toast-color: #ea4c89}.toast-github{--tblr-toast-color: #181717}.toast-instagram{--tblr-toast-color: #e4405f}.toast-pinterest{--tblr-toast-color: #bd081c}.toast-vk{--tblr-toast-color: #6383a8}.toast-rss{--tblr-toast-color: #ffa500}.toast-flickr{--tblr-toast-color: #0063dc}.toast-bitbucket{--tblr-toast-color: #0052cc}.toast-tabler{--tblr-toast-color: #066fd1}.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-gray-200);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:.625rem;font-weight:var(--tblr-font-weight-bold);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{content:none}.hr-text.hr-text-left>*:first-child{padding-right:.5rem;padding-left:.5rem}.hr-text.hr-text-right:before{content:""}.hr-text.hr-text-right:after{content:none}.hr-text.hr-text-right>*:first-child{padding-right:0;padding-left:.5rem}.card>.hr-text{margin:0}.hr-text-spaceless{margin:-.5rem 0}.lead{line-height:1.4}a{text-decoration-skip-ink:auto}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)}strong,.strong,b{font-weight:var(--tblr-font-weight-bold)}blockquote{padding-left: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{padding:1rem;background:var(--tblr-bg-surface-dark);color:var(--tblr-light);border-radius:var(--tblr-border-radius)}pre code{background:transparent}code{background:var(--tblr-code-bg);padding:2px 4px;border-radius:var(--tblr-border-radius)}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,svg{max-width:100%;height:auto}.list-unstyled{margin-left:0}::selection{background-color:rgba(var(--tblr-primary-rgb),.16)}[class^=link-].disabled,[class*=" link-"].disabled{color:var(--tblr-disabled-color)!important;pointer-events:none}.subheader{font-size:.625rem;font-weight:var(--tblr-font-weight-bold);text-transform:uppercase;letter-spacing:.04em;line-height:1rem;color:var(--tblr-secondary)}.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) rgba(4,32,69,.1)}.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}.bg-white-overlay{color:#fff;background-color:#f6f8fb3d}.bg-dark-overlay{color:#fff;background-color:#1824333d}.bg-cover{background-repeat:no-repeat;background-size:cover;background-position:center}.bg-primary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-primary-rgb),var(--tblr-bg-opacity))!important}.bg-primary-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-primary-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-primary-lt-rgb),var(--tblr-bg-opacity))!important}.border-primary{border-color:#00857d!important}.bg-secondary{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-secondary-rgb),var(--tblr-bg-opacity))!important}.bg-secondary-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-secondary-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-secondary-lt-rgb),var(--tblr-bg-opacity))!important}.border-secondary{border-color:#6c7a91!important}.bg-success{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-success-rgb),var(--tblr-bg-opacity))!important}.bg-success-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-success-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-success-lt-rgb),var(--tblr-bg-opacity))!important}.border-success{border-color:#2fb344!important}.bg-info{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-info-rgb),var(--tblr-bg-opacity))!important}.bg-info-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-info-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-info-lt-rgb),var(--tblr-bg-opacity))!important}.border-info{border-color:#4299e1!important}.bg-warning{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-warning-rgb),var(--tblr-bg-opacity))!important}.bg-warning-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-warning-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-warning-lt-rgb),var(--tblr-bg-opacity))!important}.border-warning{border-color:#f76707!important}.bg-danger{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-danger-rgb),var(--tblr-bg-opacity))!important}.bg-danger-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-danger-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-danger-lt-rgb),var(--tblr-bg-opacity))!important}.border-danger{border-color:#d63939!important}.bg-light{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-light-rgb),var(--tblr-bg-opacity))!important}.bg-light-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-light-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-light-lt-rgb),var(--tblr-bg-opacity))!important}.border-light{border-color:#f6f8fb!important}.bg-dark{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-dark-rgb),var(--tblr-bg-opacity))!important}.bg-dark-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-dark-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-dark-lt-rgb),var(--tblr-bg-opacity))!important}.border-dark{border-color:#182433!important}.bg-muted{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-muted-rgb),var(--tblr-bg-opacity))!important}.bg-muted-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-muted-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-muted-lt-rgb),var(--tblr-bg-opacity))!important}.border-muted{border-color:#6c7a91!important}.bg-blue{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-blue-rgb),var(--tblr-bg-opacity))!important}.bg-blue-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-blue-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-blue-lt-rgb),var(--tblr-bg-opacity))!important}.border-blue{border-color:#066fd1!important}.bg-azure{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-azure-rgb),var(--tblr-bg-opacity))!important}.bg-azure-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-azure-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-azure-lt-rgb),var(--tblr-bg-opacity))!important}.border-azure{border-color:#4299e1!important}.bg-indigo{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-indigo-rgb),var(--tblr-bg-opacity))!important}.bg-indigo-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-indigo-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-indigo-lt-rgb),var(--tblr-bg-opacity))!important}.border-indigo{border-color:#4263eb!important}.bg-purple{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-purple-rgb),var(--tblr-bg-opacity))!important}.bg-purple-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-purple-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-purple-lt-rgb),var(--tblr-bg-opacity))!important}.border-purple{border-color:#ae3ec9!important}.bg-pink{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-pink-rgb),var(--tblr-bg-opacity))!important}.bg-pink-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-pink-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-pink-lt-rgb),var(--tblr-bg-opacity))!important}.border-pink{border-color:#d6336c!important}.bg-red{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-red-rgb),var(--tblr-bg-opacity))!important}.bg-red-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-red-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-red-lt-rgb),var(--tblr-bg-opacity))!important}.border-red{border-color:#d63939!important}.bg-orange{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-orange-rgb),var(--tblr-bg-opacity))!important}.bg-orange-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-orange-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-orange-lt-rgb),var(--tblr-bg-opacity))!important}.border-orange{border-color:#f76707!important}.bg-yellow{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-yellow-rgb),var(--tblr-bg-opacity))!important}.bg-yellow-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-yellow-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-yellow-lt-rgb),var(--tblr-bg-opacity))!important}.border-yellow{border-color:#f59f00!important}.bg-lime{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-lime-rgb),var(--tblr-bg-opacity))!important}.bg-lime-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-lime-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-lime-lt-rgb),var(--tblr-bg-opacity))!important}.border-lime{border-color:#74b816!important}.bg-green{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-green-rgb),var(--tblr-bg-opacity))!important}.bg-green-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-green-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-green-lt-rgb),var(--tblr-bg-opacity))!important}.border-green{border-color:#2fb344!important}.bg-teal{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-teal-rgb),var(--tblr-bg-opacity))!important}.bg-teal-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-teal-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-teal-lt-rgb),var(--tblr-bg-opacity))!important}.border-teal{border-color:#0ca678!important}.bg-cyan{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-cyan-rgb),var(--tblr-bg-opacity))!important}.bg-cyan-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-cyan-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-cyan-lt-rgb),var(--tblr-bg-opacity))!important}.border-cyan{border-color:#17a2b8!important}.bg-x{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-x-rgb),var(--tblr-bg-opacity))!important}.bg-x-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-x-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-x-lt-rgb),var(--tblr-bg-opacity))!important}.border-x{border-color:#000!important}.bg-facebook{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-facebook-rgb),var(--tblr-bg-opacity))!important}.bg-facebook-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-facebook-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-facebook-lt-rgb),var(--tblr-bg-opacity))!important}.border-facebook{border-color:#1877f2!important}.bg-twitter{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-twitter-rgb),var(--tblr-bg-opacity))!important}.bg-twitter-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-twitter-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-twitter-lt-rgb),var(--tblr-bg-opacity))!important}.border-twitter{border-color:#1da1f2!important}.bg-linkedin{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-linkedin-rgb),var(--tblr-bg-opacity))!important}.bg-linkedin-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-linkedin-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-linkedin-lt-rgb),var(--tblr-bg-opacity))!important}.border-linkedin{border-color:#0a66c2!important}.bg-google{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-google-rgb),var(--tblr-bg-opacity))!important}.bg-google-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-google-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-google-lt-rgb),var(--tblr-bg-opacity))!important}.border-google{border-color:#dc4e41!important}.bg-youtube{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-youtube-rgb),var(--tblr-bg-opacity))!important}.bg-youtube-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-youtube-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-youtube-lt-rgb),var(--tblr-bg-opacity))!important}.border-youtube{border-color:red!important}.bg-vimeo{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-vimeo-rgb),var(--tblr-bg-opacity))!important}.bg-vimeo-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-vimeo-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-vimeo-lt-rgb),var(--tblr-bg-opacity))!important}.border-vimeo{border-color:#1ab7ea!important}.bg-dribbble{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-dribbble-rgb),var(--tblr-bg-opacity))!important}.bg-dribbble-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-dribbble-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-dribbble-lt-rgb),var(--tblr-bg-opacity))!important}.border-dribbble{border-color:#ea4c89!important}.bg-github{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-github-rgb),var(--tblr-bg-opacity))!important}.bg-github-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-github-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-github-lt-rgb),var(--tblr-bg-opacity))!important}.border-github{border-color:#181717!important}.bg-instagram{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-instagram-rgb),var(--tblr-bg-opacity))!important}.bg-instagram-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-instagram-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-instagram-lt-rgb),var(--tblr-bg-opacity))!important}.border-instagram{border-color:#e4405f!important}.bg-pinterest{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-pinterest-rgb),var(--tblr-bg-opacity))!important}.bg-pinterest-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-pinterest-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-pinterest-lt-rgb),var(--tblr-bg-opacity))!important}.border-pinterest{border-color:#bd081c!important}.bg-vk{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-vk-rgb),var(--tblr-bg-opacity))!important}.bg-vk-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-vk-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-vk-lt-rgb),var(--tblr-bg-opacity))!important}.border-vk{border-color:#6383a8!important}.bg-rss{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-rss-rgb),var(--tblr-bg-opacity))!important}.bg-rss-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-rss-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-rss-lt-rgb),var(--tblr-bg-opacity))!important}.border-rss{border-color:orange!important}.bg-flickr{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-flickr-rgb),var(--tblr-bg-opacity))!important}.bg-flickr-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-flickr-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-flickr-lt-rgb),var(--tblr-bg-opacity))!important}.border-flickr{border-color:#0063dc!important}.bg-bitbucket{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-bg-opacity))!important}.bg-bitbucket-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-bitbucket-lt-rgb),var(--tblr-bg-opacity))!important}.border-bitbucket{border-color:#0052cc!important}.bg-tabler{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-tabler-rgb),var(--tblr-bg-opacity))!important}.bg-tabler-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-tabler-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-tabler-lt-rgb),var(--tblr-bg-opacity))!important}.border-tabler{border-color:#066fd1!important}.bg-white{--tblr-bg-opacity: 1;background-color:rgba(var(--tblr-white-rgb),var(--tblr-bg-opacity))!important}.bg-white-lt{--tblr-bg-opacity: 1;--tblr-text-opacity: 1;color:rgba(var(--tblr-white-rgb),var(--tblr-text-opacity))!important;background-color:rgba(var(--tblr-white-lt-rgb),var(--tblr-bg-opacity))!important}.border-white{border-color:#fff!important}.text-primary{--tblr-text-opacity: 1;color:rgba(var(--tblr-primary-rgb),var(--tblr-text-opacity))!important}.text-primary-fg{color:var(--tblr-primary-fg)!important}.text-secondary{--tblr-text-opacity: 1;color:rgba(var(--tblr-secondary-rgb),var(--tblr-text-opacity))!important}.text-secondary-fg{color:var(--tblr-secondary-fg)!important}.text-success{--tblr-text-opacity: 1;color:rgba(var(--tblr-success-rgb),var(--tblr-text-opacity))!important}.text-success-fg{color:var(--tblr-success-fg)!important}.text-info{--tblr-text-opacity: 1;color:rgba(var(--tblr-info-rgb),var(--tblr-text-opacity))!important}.text-info-fg{color:var(--tblr-info-fg)!important}.text-warning{--tblr-text-opacity: 1;color:rgba(var(--tblr-warning-rgb),var(--tblr-text-opacity))!important}.text-warning-fg{color:var(--tblr-warning-fg)!important}.text-danger{--tblr-text-opacity: 1;color:rgba(var(--tblr-danger-rgb),var(--tblr-text-opacity))!important}.text-danger-fg{color:var(--tblr-danger-fg)!important}.text-light{--tblr-text-opacity: 1;color:rgba(var(--tblr-light-rgb),var(--tblr-text-opacity))!important}.text-light-fg{color:var(--tblr-light-fg)!important}.text-dark{--tblr-text-opacity: 1;color:rgba(var(--tblr-dark-rgb),var(--tblr-text-opacity))!important}.text-dark-fg{color:var(--tblr-dark-fg)!important}.text-muted{--tblr-text-opacity: 1;color:rgba(var(--tblr-muted-rgb),var(--tblr-text-opacity))!important}.text-muted-fg{color:var(--tblr-muted-fg)!important}.text-blue{--tblr-text-opacity: 1;color:rgba(var(--tblr-blue-rgb),var(--tblr-text-opacity))!important}.text-blue-fg{color:var(--tblr-blue-fg)!important}.text-azure{--tblr-text-opacity: 1;color:rgba(var(--tblr-azure-rgb),var(--tblr-text-opacity))!important}.text-azure-fg{color:var(--tblr-azure-fg)!important}.text-indigo{--tblr-text-opacity: 1;color:rgba(var(--tblr-indigo-rgb),var(--tblr-text-opacity))!important}.text-indigo-fg{color:var(--tblr-indigo-fg)!important}.text-purple{--tblr-text-opacity: 1;color:rgba(var(--tblr-purple-rgb),var(--tblr-text-opacity))!important}.text-purple-fg{color:var(--tblr-purple-fg)!important}.text-pink{--tblr-text-opacity: 1;color:rgba(var(--tblr-pink-rgb),var(--tblr-text-opacity))!important}.text-pink-fg{color:var(--tblr-pink-fg)!important}.text-red{--tblr-text-opacity: 1;color:rgba(var(--tblr-red-rgb),var(--tblr-text-opacity))!important}.text-red-fg{color:var(--tblr-red-fg)!important}.text-orange{--tblr-text-opacity: 1;color:rgba(var(--tblr-orange-rgb),var(--tblr-text-opacity))!important}.text-orange-fg{color:var(--tblr-orange-fg)!important}.text-yellow{--tblr-text-opacity: 1;color:rgba(var(--tblr-yellow-rgb),var(--tblr-text-opacity))!important}.text-yellow-fg{color:var(--tblr-yellow-fg)!important}.text-lime{--tblr-text-opacity: 1;color:rgba(var(--tblr-lime-rgb),var(--tblr-text-opacity))!important}.text-lime-fg{color:var(--tblr-lime-fg)!important}.text-green{--tblr-text-opacity: 1;color:rgba(var(--tblr-green-rgb),var(--tblr-text-opacity))!important}.text-green-fg{color:var(--tblr-green-fg)!important}.text-teal{--tblr-text-opacity: 1;color:rgba(var(--tblr-teal-rgb),var(--tblr-text-opacity))!important}.text-teal-fg{color:var(--tblr-teal-fg)!important}.text-cyan{--tblr-text-opacity: 1;color:rgba(var(--tblr-cyan-rgb),var(--tblr-text-opacity))!important}.text-cyan-fg{color:var(--tblr-cyan-fg)!important}.text-x{--tblr-text-opacity: 1;color:rgba(var(--tblr-x-rgb),var(--tblr-text-opacity))!important}.text-x-fg{color:var(--tblr-x-fg)!important}.text-facebook{--tblr-text-opacity: 1;color:rgba(var(--tblr-facebook-rgb),var(--tblr-text-opacity))!important}.text-facebook-fg{color:var(--tblr-facebook-fg)!important}.text-twitter{--tblr-text-opacity: 1;color:rgba(var(--tblr-twitter-rgb),var(--tblr-text-opacity))!important}.text-twitter-fg{color:var(--tblr-twitter-fg)!important}.text-linkedin{--tblr-text-opacity: 1;color:rgba(var(--tblr-linkedin-rgb),var(--tblr-text-opacity))!important}.text-linkedin-fg{color:var(--tblr-linkedin-fg)!important}.text-google{--tblr-text-opacity: 1;color:rgba(var(--tblr-google-rgb),var(--tblr-text-opacity))!important}.text-google-fg{color:var(--tblr-google-fg)!important}.text-youtube{--tblr-text-opacity: 1;color:rgba(var(--tblr-youtube-rgb),var(--tblr-text-opacity))!important}.text-youtube-fg{color:var(--tblr-youtube-fg)!important}.text-vimeo{--tblr-text-opacity: 1;color:rgba(var(--tblr-vimeo-rgb),var(--tblr-text-opacity))!important}.text-vimeo-fg{color:var(--tblr-vimeo-fg)!important}.text-dribbble{--tblr-text-opacity: 1;color:rgba(var(--tblr-dribbble-rgb),var(--tblr-text-opacity))!important}.text-dribbble-fg{color:var(--tblr-dribbble-fg)!important}.text-github{--tblr-text-opacity: 1;color:rgba(var(--tblr-github-rgb),var(--tblr-text-opacity))!important}.text-github-fg{color:var(--tblr-github-fg)!important}.text-instagram{--tblr-text-opacity: 1;color:rgba(var(--tblr-instagram-rgb),var(--tblr-text-opacity))!important}.text-instagram-fg{color:var(--tblr-instagram-fg)!important}.text-pinterest{--tblr-text-opacity: 1;color:rgba(var(--tblr-pinterest-rgb),var(--tblr-text-opacity))!important}.text-pinterest-fg{color:var(--tblr-pinterest-fg)!important}.text-vk{--tblr-text-opacity: 1;color:rgba(var(--tblr-vk-rgb),var(--tblr-text-opacity))!important}.text-vk-fg{color:var(--tblr-vk-fg)!important}.text-rss{--tblr-text-opacity: 1;color:rgba(var(--tblr-rss-rgb),var(--tblr-text-opacity))!important}.text-rss-fg{color:var(--tblr-rss-fg)!important}.text-flickr{--tblr-text-opacity: 1;color:rgba(var(--tblr-flickr-rgb),var(--tblr-text-opacity))!important}.text-flickr-fg{color:var(--tblr-flickr-fg)!important}.text-bitbucket{--tblr-text-opacity: 1;color:rgba(var(--tblr-bitbucket-rgb),var(--tblr-text-opacity))!important}.text-bitbucket-fg{color:var(--tblr-bitbucket-fg)!important}.text-tabler{--tblr-text-opacity: 1;color:rgba(var(--tblr-tabler-rgb),var(--tblr-text-opacity))!important}.text-tabler-fg{color:var(--tblr-tabler-fg)!important}.bg-gray-50{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-50-rgb),var(--tblr-bg-opacity))!important}.text-gray-50-fg{color:#182433!important}.bg-gray-100{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-100-rgb),var(--tblr-bg-opacity))!important}.text-gray-100-fg{color:#182433!important}.bg-gray-200{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-200-rgb),var(--tblr-bg-opacity))!important}.text-gray-200-fg{color:#182433!important}.bg-gray-300{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-300-rgb),var(--tblr-bg-opacity))!important}.text-gray-300-fg{color:#f6f8fb!important}.bg-gray-400{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-400-rgb),var(--tblr-bg-opacity))!important}.text-gray-400-fg{color:#f6f8fb!important}.bg-gray-500{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-500-rgb),var(--tblr-bg-opacity))!important}.text-gray-500-fg{color:#f6f8fb!important}.bg-gray-600{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-600-rgb),var(--tblr-bg-opacity))!important}.text-gray-600-fg{color:#f6f8fb!important}.bg-gray-700{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-700-rgb),var(--tblr-bg-opacity))!important}.text-gray-700-fg{color:#f6f8fb!important}.bg-gray-800{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-800-rgb),var(--tblr-bg-opacity))!important}.text-gray-800-fg{color:#f6f8fb!important}.bg-gray-900{--tblr-bg-opacity: .1;background-color:rgba(var(--tblr-gray-900-rgb),var(--tblr-bg-opacity))!important}.text-gray-900-fg{color:#f6f8fb!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:3rem!important}.h-6{height:3rem!important}.w-7{width:5rem!important}.h-7{height:5rem!important}.w-8{width:8rem!important}.h-8{height:8rem!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:.35!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:.7!important}.opacity-75{opacity:.75!important}.opacity-80{opacity:.8!important}.opacity-85{opacity:.85!important}.opacity-90{opacity:.9!important}.opacity-95{opacity:.95!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}.ts-control{border:1px solid var(--tblr-border-color);padding:.5625rem .75rem;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 + -0) .75rem calc(.5625rem - 4px + -0)}.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:#182433;border:0 solid #dce1e7}.ts-wrapper.multi .ts-control>div.active{background:#00857d;color:#fff;border:0 solid rgba(0,0,0,0)}.ts-wrapper.multi.disabled .ts-control>div,.ts-wrapper.multi.disabled .ts-control>div.active{color:#727272;background:#fff;border:0 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(.75rem + 5px);right:auto}.ts-control.rtl .ts-control>input{margin:0 4px 0 -2px!important}.disabled .ts-control{opacity:.5;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:#fff;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 .75rem}.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:#49566c;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:#18243380}.ts-dropdown .spinner{display:inline-block;width:30px;height:30px;margin:3px .75rem}.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(.75rem - 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),.75rem)}.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 .75rem;border-bottom:1px solid #d0d0d0;background:color-mix(#fff,#d0d0d0,85%);border-radius:var(--tblr-border-radius) var(--tblr-border-radius) 0 0}.ts-wrapper .dropdown-header-close{position:absolute;right:.75rem;top:50%;color:#182433;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-box-shadow-input)}.plugin-dropdown_input .dropdown-input{border:1px solid #d0d0d0;border-width:0 0 1px;display:block;padding:.5625rem .75rem;box-shadow:none;width:100%;background:transparent}.plugin-dropdown_input.focus .ts-dropdown .dropdown-input{border-color:#80c2be;outline:0;box-shadow:var(--tblr-box-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 #dce1e7;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 #dce1e7;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: 0;--ts-pr-caret: 0;--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:#182433;font-family:inherit;font-size:inherit;line-height:1.4285714286}.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:#fff;border:1px solid var(--tblr-border-color-translucent);border-radius:4px;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:-.75rem;margin-right:-.75rem}.ts-dropdown .create{padding-left:.75rem}.ts-dropdown-content{padding:5px 0}.ts-control{box-shadow:var(--tblr-box-shadow-input);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;display:flex;align-items:center}@media (prefers-reduced-motion: reduce){.ts-control{transition:none}}.focus .ts-control{border-color:#80c2be;outline:0;box-shadow:var(--tblr-box-shadow-input),0 0 0 .25rem rgba(var(--tblr-primary-rgb),.25)}.ts-control .item{display:flex;align-items:center}.ts-wrapper.is-invalid,.was-validated .invalid,.was-validated :invalid+.ts-wrapper{border-color:var(--tblr-form-invalid-color)}.ts-wrapper.is-invalid: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 calc(.3571428572em + .28125rem) center;background-size:calc(.7142857143em + .5625rem) calc(.7142857143em + .5625rem);background-repeat:no-repeat}.ts-wrapper.is-invalid.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='%238a97ab' 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 .75rem center,center right 2.25rem;background-size:16px 12px,calc(.7142857143em + .5625rem) calc(.7142857143em + .5625rem);background-repeat:no-repeat}.ts-wrapper.is-invalid.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 calc(.3571428572em + .28125rem) center;background-size:calc(.7142857143em + .5625rem) calc(.7142857143em + .5625rem);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='%238a97ab' 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 .75rem center,center right 2.25rem;background-size:16px 12px,calc(.7142857143em + .5625rem) calc(.7142857143em + .5625rem);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.4285714286em + 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.4285714286em + .25rem + 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;padding-bottom:0}.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.4285714286em + .25rem + calc(var(--tblr-border-width) * 2)) - 1.4285714286 * .75rem - 4px) / 2)!important}.ts-wrapper.multi.has-items .ts-control{padding-left:calc(.75rem - 5px);--ts-pr-min: calc(.75rem - 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.4285714286em + 1rem + 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:1.25rem}.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='%238a97ab' 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 .75rem center;background-size:16px 12px}.ts-wrapper.form-select,.ts-wrapper.single{--ts-pr-caret: 2.25rem}.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}:root{--ts-pr-clear-button: 0rem;--ts-pr-caret: 0rem}.ts-input,.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-box-shadow-input)}.ts-wrapper.is-invalid .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-box-shadow-dropdown)}.ts-dropdown .option{padding:.5rem .75rem}.ts-control,.ts-control input{color:var(--tblr-body-color)}.ts-control input::placeholder{color:#8a97ab}.ts-wrapper.multi .ts-control>div{background:var(--tblr-bg-surface-secondary);border:1px solid var(--tblr-border-color);color:var(--tblr-body-color)}html{scroll-behavior:auto!important}.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}pre{background-color:transparent;color:inherit}.alert{background:var(--tblr-bg-surface)}.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:4px}.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}.page-body .card .card-header{background:var(--tblr-bg-surface-secondary)!important}[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: #8a97ab;--tblr-primary: #00F2D4;--tblr-primary-fg: #001423;--tblr-primary-rgb: 0,242,212;--tblr-btn-active-color: #001423}body[data-bs-theme=dark],body[data-bs-theme=dark] body[data-bs-theme=light]{background-color:#001423}body[data-bs-theme=dark] ::selection,body[data-bs-theme=dark] body[data-bs-theme=light] ::selection{background-color:rgba(var(--tblr-primary-rgb),.48)}body[data-bs-theme=dark] .btn-primary,body[data-bs-theme=dark] .bg-primary .card-title,body[data-bs-theme=dark] .bg-primary a,body[data-bs-theme=dark] .bg-primary i,body[data-bs-theme=dark] .text-bg-primary{color:#001423!important}body[data-bs-theme=dark] .card{background:#001423!important}body[data-bs-theme=dark] .navbar,body[data-bs-theme=dark] .page-header{background-color:#001423}body[data-bs-theme=dark] .page,body[data-bs-theme=dark] .page-tabs .nav-tabs .nav-link.active{background-color:#081b2a!important}body[data-bs-theme=dark] .page-link.active,body[data-bs-theme=dark] .active>.page-link{color:#001423}body[data-bs-theme=dark] .text-bg-primary{color:#001423!important}body[data-bs-theme=dark] .text-muted{color:var(--tblr-secondary-color)!important}body[data-bs-theme=dark] .text-secondary{color:#8a97ab!important}body[data-bs-theme=dark] .footer .text-primary{color:#fff!important}body[data-bs-theme=dark] .toast{color:var(--tblr-body-color)}body[data-bs-theme=dark] .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}.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}.page{background-color:var(--tblr-bg-surface-secondary)}.page-header{background-color:var(--tblr-bg-surface);min-height:0}@media (min-width: 992px){.navbar-vertical.navbar-expand-lg{padding-bottom:2rem}}.navbar-vertical.navbar-expand-lg .navbar-collapse .nav-link-icon{color:var(--tblr-nav-link-color)!important}.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{background-color:var(--tblr-navbar-active-bg)}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:hover a{text-decoration:none}.navbar-vertical.navbar-expand-lg .navbar-collapse .dropdown-menu .dropdown-item:hover .btn-group{visibility:visible}.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.active a{color:#001423}.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}}body[data-bs-theme=light] .navbar-vertical.navbar-expand-lg{background:linear-gradient(180deg,#00857d00,#00857d1a),#fff}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg{background:linear-gradient(180deg,#00f2d400,#00f2d41a),#001423}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .nav-item.dropdown.active:after{border-color:#00f2d4!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item a{color:#fff!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item.active{background-color:#ffffff0f!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item.active a{color:#fff!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item:hover{background-color:#ffffff0f!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .dropdown-item .nav-link-title{color:#fff!important}body[data-bs-theme=dark] .navbar-vertical.navbar-expand-lg .text-secondary{color:#00f2d4!important}body[data-bs-theme=dark] .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.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)}body[data-bs-theme=dark] .table thead th,body[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}pre.change-data>span{display:block;padding-right:1rem;padding-left:1rem}pre.change-data>span.added{background-color:#2fb344}pre.change-data>span.removed{background-color:#d63939}pre.change-diff{border-color:transparent}pre.change-diff.change-added{background-color:#2fb344}pre.change-diff.change-removed{background-color:#d63939}pre.block{padding:1rem;border:1px solid #dce1e7;border-radius:4px}.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:#d6393926}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-of-type{margin-bottom:0}td>.rendered-markdown{max-height:200px;overflow-y:scroll}.markdown-widget .preview{border:1px solid #dce1e7;border-radius:4px;min-height:200px}span.color-label{display:inline-block;width:5rem;height:1rem;padding:.25em .5em;border:1px solid #303030;border-radius:4px}.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}.column-filter{position:static;display:inline}.column-filter.dropdown>.dropdown-toggle>.mdi-filter-settings{font-size:.625rem}.column-filter.dropdown>.dropdown-menu{max-width:300px}.column-filter.dropdown-toggle:after{content:none}.netbox-edition{letter-spacing:.15rem}.btn-float-group,.btn-float-group-right,.btn-float-group-left{position:sticky;bottom:10px;z-index:2}.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}tr[data-read=True] td{background-color:var(--tblr-bg-surface-secondary);color:var(--tblr-secondary-color)} diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 969d5c73a..beee9c578 100644 --- a/netbox/project-static/dist/netbox.js +++ b/netbox/project-static/dist/netbox.js @@ -1,21 +1,14 @@ -"use strict";(()=>{var sh=Object.create;var Ua=Object.defineProperty,oh=Object.defineProperties,ah=Object.getOwnPropertyDescriptor,lh=Object.getOwnPropertyDescriptors,ch=Object.getOwnPropertyNames,du=Object.getOwnPropertySymbols,uh=Object.getPrototypeOf,fu=Object.prototype.hasOwnProperty,dh=Object.prototype.propertyIsEnumerable;var tc=(ii,ti,ei)=>ti in ii?Ua(ii,ti,{enumerable:!0,configurable:!0,writable:!0,value:ei}):ii[ti]=ei,Ui=(ii,ti)=>{for(var ei in ti||(ti={}))fu.call(ti,ei)&&tc(ii,ei,ti[ei]);if(du)for(var ei of du(ti))dh.call(ti,ei)&&tc(ii,ei,ti[ei]);return ii},Hn=(ii,ti)=>oh(ii,lh(ti));var Ya=(ii,ti)=>()=>(ti||ii((ti={exports:{}}).exports,ti),ti.exports),hu=(ii,ti)=>{for(var ei in ti)Ua(ii,ei,{get:ti[ei],enumerable:!0})},fh=(ii,ti,ei,ni)=>{if(ti&&typeof ti=="object"||typeof ti=="function")for(let ri of ch(ti))!fu.call(ii,ri)&&ri!==ei&&Ua(ii,ri,{get:()=>ti[ri],enumerable:!(ni=ah(ti,ri))||ni.enumerable});return ii};var zo=(ii,ti,ei)=>(ei=ii!=null?sh(uh(ii)):{},fh(ti||!ii||!ii.__esModule?Ua(ei,"default",{value:ii,enumerable:!0}):ei,ii));var Rn=(ii,ti,ei)=>tc(ii,typeof ti!="symbol"?ti+"":ti,ei);var ks=(ii,ti,ei)=>new Promise((ni,ri)=>{var si=di=>{try{li(ei.next(di))}catch(mi){ri(mi)}},ai=di=>{try{li(ei.throw(di))}catch(mi){ri(mi)}},li=di=>di.done?ni(di.value):Promise.resolve(di.value).then(si,ai);li((ei=ei.apply(ii,ti)).next())});var Id=Ya((exports,module)=>{(function(ii,ti){typeof define=="function"&&define.amd?define([],ti):typeof module=="object"&&module.exports?module.exports=ti():ii.htmx=ii.htmx||ti()})(typeof self!="undefined"?self:exports,function(){return function(){"use strict";var Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(ii,ti){var ei=dr(ii,ti||"post");return ei.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,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:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:!1,scrollBehavior:"smooth",defaultFocusScroll:!1,getCacheBusterParam:!1,globalViewTransitions:!1,methodsThatUseUrlParams:["get"],selfRequestsOnly:!1,ignoreTitle:!1,scrollIntoViewOnBoost:!0,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(ii){return new EventSource(ii,{withCredentials:!0})},createWebSocket:function(ii){var ti=new WebSocket(ii,[]);return ti.binaryType=Q.config.wsBinaryType,ti},version:"1.9.12"},r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R},w=["get","post","put","delete","patch"],i=w.map(function(ii){return"[hx-"+ii+"], [data-hx-"+ii+"]"}).join(", "),S=e("head"),q=e("title"),H=e("svg",!0);function e(ii,ti){return new RegExp("<"+ii+"(\\s[^>]*>|>)([\\s\\S]*?)<\\/"+ii+">",ti?"gim":"im")}function d(ii){if(ii==null)return;let ti=NaN;return ii.slice(-2)=="ms"?ti=parseFloat(ii.slice(0,-2)):ii.slice(-1)=="s"?ti=parseFloat(ii.slice(0,-1))*1e3:ii.slice(-1)=="m"?ti=parseFloat(ii.slice(0,-1))*1e3*60:ti=parseFloat(ii),isNaN(ti)?void 0:ti}function ee(ii,ti){return ii.getAttribute&&ii.getAttribute(ti)}function o(ii,ti){return ii.hasAttribute&&(ii.hasAttribute(ti)||ii.hasAttribute("data-"+ti))}function te(ii,ti){return ee(ii,ti)||ee(ii,"data-"+ti)}function u(ii){return ii.parentElement}function re(){return document}function c(ii,ti){for(;ii&&!ti(ii);)ii=u(ii);return ii||null}function L(ii,ti,ei){var ni=te(ti,ei),ri=te(ti,"hx-disinherit");return ii!==ti&&ri&&(ri==="*"||ri.split(" ").indexOf(ei)>=0)?"unset":ni}function ne(ii,ti){var ei=null;if(c(ii,function(ni){return ei=L(ii,ni,ti)}),ei!=="unset")return ei}function h(ii,ti){var ei=ii.matches||ii.matchesSelector||ii.msMatchesSelector||ii.mozMatchesSelector||ii.webkitMatchesSelector||ii.oMatchesSelector;return ei&&ei.call(ii,ti)}function A(ii){var ti=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,ei=ti.exec(ii);return ei?ei[1].toLowerCase():""}function s(ii,ti){for(var ei=new DOMParser,ni=ei.parseFromString(ii,"text/html"),ri=ni.body;ti>0;)ti--,ri=ri.firstChild;return ri==null&&(ri=re().createDocumentFragment()),ri}function N(ii){return/",0),si=ri.querySelector("template").content;return Q.config.allowScriptTags?oe(si.querySelectorAll("script"),function(ai){Q.config.inlineScriptNonce&&(ai.nonce=Q.config.inlineScriptNonce),ai.htmxExecuted=navigator.userAgent.indexOf("Firefox")===-1}):oe(si.querySelectorAll("script"),function(ai){_(ai)}),si}switch(ei){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return s(""+ni+"
    ",1);case"col":return s(""+ni+"
    ",2);case"tr":return s(""+ni+"
    ",2);case"td":case"th":return s(""+ni+"
    ",3);case"script":case"style":return s("
    "+ni+"
    ",1);default:return s(ni,0)}}function ie(ii){ii&&ii()}function I(ii,ti){return Object.prototype.toString.call(ii)==="[object "+ti+"]"}function k(ii){return I(ii,"Function")}function P(ii){return I(ii,"Object")}function ae(ii){var ti="htmx-internal-data",ei=ii[ti];return ei||(ei=ii[ti]={}),ei}function M(ii){var ti=[];if(ii)for(var ei=0;ei=0}function se(ii){return ii.getRootNode&&ii.getRootNode()instanceof window.ShadowRoot?re().body.contains(ii.getRootNode().host):re().body.contains(ii)}function D(ii){return ii.trim().split(/\s+/)}function le(ii,ti){for(var ei in ti)ti.hasOwnProperty(ei)&&(ii[ei]=ti[ei]);return ii}function E(ii){try{return JSON.parse(ii)}catch(ti){return b(ti),null}}function U(){var ii="htmx:localStorageTest";try{return localStorage.setItem(ii,ii),localStorage.removeItem(ii),!0}catch(ti){return!1}}function B(ii){try{var ti=new URL(ii);return ti&&(ii=ti.pathname+ti.search),/^\/$/.test(ii)||(ii=ii.replace(/\/+$/,"")),ii}catch(ei){return ii}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(ii){var ti=Q.on("htmx:load",function(ei){ii(ei.detail.elt)});return ti}function V(){Q.logger=function(ii,ti,ei){console&&console.log(ti,ii,ei)}}function j(){Q.logger=null}function C(ii,ti){return ti?ii.querySelector(ti):C(re(),ii)}function f(ii,ti){return ti?ii.querySelectorAll(ti):f(re(),ii)}function _(ii,ti){ii=p(ii),ti?setTimeout(function(){_(ii),ii=null},ti):ii.parentElement.removeChild(ii)}function z(ii,ti,ei){ii=p(ii),ei?setTimeout(function(){z(ii,ti),ii=null},ei):ii.classList&&ii.classList.add(ti)}function n(ii,ti,ei){ii=p(ii),ei?setTimeout(function(){n(ii,ti),ii=null},ei):ii.classList&&(ii.classList.remove(ti),ii.classList.length===0&&ii.removeAttribute("class"))}function $(ii,ti){ii=p(ii),ii.classList.toggle(ti)}function W(ii,ti){ii=p(ii),oe(ii.parentElement.children,function(ei){n(ei,ti)}),z(ii,ti)}function v(ii,ti){if(ii=p(ii),ii.closest)return ii.closest(ti);do if(ii==null||h(ii,ti))return ii;while(ii=ii&&u(ii));return null}function g(ii,ti){return ii.substring(0,ti.length)===ti}function G(ii,ti){return ii.substring(ii.length-ti.length)===ti}function J(ii){var ti=ii.trim();return g(ti,"<")&&G(ti,"/>")?ti.substring(1,ti.length-2):ti}function Z(ii,ti){return ti.indexOf("closest ")===0?[v(ii,J(ti.substr(8)))]:ti.indexOf("find ")===0?[C(ii,J(ti.substr(5)))]:ti==="next"?[ii.nextElementSibling]:ti.indexOf("next ")===0?[K(ii,J(ti.substr(5)))]:ti==="previous"?[ii.previousElementSibling]:ti.indexOf("previous ")===0?[Y(ii,J(ti.substr(9)))]:ti==="document"?[document]:ti==="window"?[window]:ti==="body"?[document.body]:re().querySelectorAll(J(ti))}var K=function(ii,ti){for(var ei=re().querySelectorAll(ti),ni=0;ni=0;ni--){var ri=ei[ni];if(ri.compareDocumentPosition(ii)===Node.DOCUMENT_POSITION_FOLLOWING)return ri}};function ue(ii,ti){return ti?Z(ii,ti)[0]:Z(re().body,ii)[0]}function p(ii){return I(ii,"String")?C(ii):ii}function ve(ii,ti,ei){return k(ti)?{target:re().body,event:ii,listener:ti}:{target:p(ii),event:ti,listener:ei}}function de(ii,ti,ei){jr(function(){var ri=ve(ii,ti,ei);ri.target.addEventListener(ri.event,ri.listener)});var ni=k(ti);return ni?ti:ei}function ge(ii,ti,ei){return jr(function(){var ni=ve(ii,ti,ei);ni.target.removeEventListener(ni.event,ni.listener)}),k(ti)?ti:ei}var pe=re().createElement("output");function me(ii,ti){var ei=ne(ii,ti);if(ei){if(ei==="this")return[xe(ii,ti)];var ni=Z(ii,ei);return ni.length===0?(b('The selector "'+ei+'" on '+ti+" returned no matches!"),[pe]):ni}}function xe(ii,ti){return c(ii,function(ei){return te(ei,ti)!=null})}function ye(ii){var ti=ne(ii,"hx-target");if(ti)return ti==="this"?xe(ii,"hx-target"):ue(ii,ti);var ei=ae(ii);return ei.boosted?re().body:ii}function be(ii){for(var ti=Q.config.attributesToSettle,ei=0;ei0?(ri=ii.substr(0,ii.indexOf(":")),ni=ii.substr(ii.indexOf(":")+1,ii.length)):ri=ii);var si=re().querySelectorAll(ni);return si?(oe(si,function(ai){var li,di=ti.cloneNode(!0);li=re().createDocumentFragment(),li.appendChild(di),Se(ri,ai)||(li=di);var mi={shouldSwap:!0,target:ai,fragment:li};ce(ai,"htmx:oobBeforeSwap",mi)&&(ai=mi.target,mi.shouldSwap&&Fe(ri,ai,ai,li,ei),oe(ei.elts,function(hi){ce(hi,"htmx:oobAfterSwap",mi)}))}),ti.parentNode.removeChild(ti)):(ti.parentNode.removeChild(ti),fe(re().body,"htmx:oobErrorNoTarget",{content:ti})),ii}function Ce(ii,ti,ei){var ni=ne(ii,"hx-select-oob");if(ni)for(var ri=ni.split(","),si=0;si0){var si=ri.replace("'","\\'"),ai=ni.tagName.replace(":","\\:"),li=ii.querySelector(ai+"[id='"+si+"']");if(li&&li!==ii){var di=ni.cloneNode();we(ni,li),ei.tasks.push(function(){we(ni,di)})}}})}function Oe(ii){return function(){n(ii,Q.config.addedClass),zt(ii),Nt(ii),qe(ii),ce(ii,"htmx:load")}}function qe(ii){var ti="[autofocus]",ei=h(ii,ti)?ii:ii.querySelector(ti);ei!=null&&ei.focus()}function a(ii,ti,ei,ni){for(Te(ii,ei,ni);ei.childNodes.length>0;){var ri=ei.firstChild;z(ri,Q.config.addedClass),ii.insertBefore(ri,ti),ri.nodeType!==Node.TEXT_NODE&&ri.nodeType!==Node.COMMENT_NODE&&ni.tasks.push(Oe(ri))}}function He(ii,ti){for(var ei=0;ei-1){var ti=ii.replace(H,""),ei=ti.match(q);if(ei)return ei[2]}}function je(ii,ti,ei,ni,ri,si){ri.title=Ve(ni);var ai=l(ni);if(ai)return Ce(ei,ai,ri),ai=Be(ei,ai,si),Re(ai),Fe(ii,ei,ti,ai,ri)}function _e(ii,ti,ei){var ni=ii.getResponseHeader(ti);if(ni.indexOf("{")===0){var ri=E(ni);for(var si in ri)if(ri.hasOwnProperty(si)){var ai=ri[si];P(ai)||(ai={value:ai}),ce(ei,si,ai)}}else for(var li=ni.split(","),di=0;di0;){var ai=ti[0];if(ai==="]"){if(ni--,ni===0){si===null&&(ri=ri+"true"),ti.shift(),ri+=")})";try{var li=Tr(ii,function(){return Function(ri)()},function(){return!0});return li.source=ri,li}catch(di){return fe(re().body,"htmx:syntax:error",{error:di,source:ri}),null}}}else ai==="["&&ni++;Qe(ai,si,ei)?ri+="(("+ei+"."+ai+") ? ("+ei+"."+ai+") : (window."+ai+"))":ri=ri+ai,si=ti.shift()}}}function y(ii,ti){for(var ei="";ii.length>0&&!ti.test(ii[0]);)ei+=ii.shift();return ei}function tt(ii){var ti;return ii.length>0&&Ze.test(ii[0])?(ii.shift(),ti=y(ii,Ke).trim(),ii.shift()):ti=y(ii,x),ti}var rt="input, textarea, select";function nt(ii,ti,ei){var ni=[],ri=Ye(ti);do{y(ri,Je);var si=ri.length,ai=y(ri,/[,\[\s]/);if(ai!=="")if(ai==="every"){var li={trigger:"every"};y(ri,Je),li.pollInterval=d(y(ri,/[,\[\s]/)),y(ri,Je);var di=et(ii,ri,"event");di&&(li.eventFilter=di),ni.push(li)}else if(ai.indexOf("sse:")===0)ni.push({trigger:"sse",sseEvent:ai.substr(4)});else{var mi={trigger:ai},di=et(ii,ri,"event");for(di&&(mi.eventFilter=di);ri.length>0&&ri[0]!==",";){y(ri,Je);var hi=ri.shift();if(hi==="changed")mi.changed=!0;else if(hi==="once")mi.once=!0;else if(hi==="consume")mi.consume=!0;else if(hi==="delay"&&ri[0]===":")ri.shift(),mi.delay=d(y(ri,x));else if(hi==="from"&&ri[0]===":"){if(ri.shift(),Ze.test(ri[0]))var _i=tt(ri);else{var _i=y(ri,x);if(_i==="closest"||_i==="find"||_i==="next"||_i==="previous"){ri.shift();var Ei=tt(ri);Ei.length>0&&(_i+=" "+Ei)}}mi.from=_i}else hi==="target"&&ri[0]===":"?(ri.shift(),mi.target=tt(ri)):hi==="throttle"&&ri[0]===":"?(ri.shift(),mi.throttle=d(y(ri,x))):hi==="queue"&&ri[0]===":"?(ri.shift(),mi.queue=y(ri,x)):hi==="root"&&ri[0]===":"?(ri.shift(),mi[hi]=tt(ri)):hi==="threshold"&&ri[0]===":"?(ri.shift(),mi[hi]=y(ri,x)):fe(ii,"htmx:syntax:error",{token:ri.shift()})}ni.push(mi)}ri.length===si&&fe(ii,"htmx:syntax:error",{token:ri.shift()}),y(ri,Je)}while(ri[0]===","&&ri.shift());return ei&&(ei[ti]=ni),ni}function it(ii){var ti=te(ii,"hx-trigger"),ei=[];if(ti){var ni=Q.config.triggerSpecsCache;ei=ni&&ni[ti]||nt(ii,ti,ni)}return ei.length>0?ei:h(ii,"form")?[{trigger:"submit"}]:h(ii,'input[type="button"], input[type="submit"]')?[{trigger:"click"}]:h(ii,rt)?[{trigger:"change"}]:[{trigger:"click"}]}function at(ii){ae(ii).cancelled=!0}function ot(ii,ti,ei){var ni=ae(ii);ni.timeout=setTimeout(function(){se(ii)&&ni.cancelled!==!0&&(ct(ei,ii,Wt("hx:poll:trigger",{triggerSpec:ei,target:ii}))||ti(ii),ot(ii,ti,ei))},ei.pollInterval)}function st(ii){return location.hostname===ii.hostname&&ee(ii,"href")&&ee(ii,"href").indexOf("#")!==0}function lt(ii,ti,ei){if(ii.tagName==="A"&&st(ii)&&(ii.target===""||ii.target==="_self")||ii.tagName==="FORM"){ti.boosted=!0;var ni,ri;if(ii.tagName==="A")ni="get",ri=ee(ii,"href");else{var si=ee(ii,"method");ni=si?si.toLowerCase():"get",ri=ee(ii,"action")}ei.forEach(function(ai){ht(ii,function(li,di){if(v(li,Q.config.disableSelector)){m(li);return}he(ni,ri,li,di)},ti,ai,!0)})}}function ut(ii,ti){return!!((ii.type==="submit"||ii.type==="click")&&(ti.tagName==="FORM"||h(ti,'input[type="submit"], button')&&v(ti,"form")!==null||ti.tagName==="A"&&ti.href&&(ti.getAttribute("href")==="#"||ti.getAttribute("href").indexOf("#")!==0)))}function ft(ii,ti){return ae(ii).boosted&&ii.tagName==="A"&&ti.type==="click"&&(ti.ctrlKey||ti.metaKey)}function ct(ii,ti,ei){var ni=ii.eventFilter;if(ni)try{return ni.call(ti,ei)!==!0}catch(ri){return fe(re().body,"htmx:eventFilter:error",{error:ri,source:ni.source}),!0}return!1}function ht(ii,ti,ei,ni,ri){var si=ae(ii),ai;ni.from?ai=Z(ii,ni.from):ai=[ii],ni.changed&&ai.forEach(function(li){var di=ae(li);di.lastValue=li.value}),oe(ai,function(li){var di=function(mi){if(!se(ii)){li.removeEventListener(ni.trigger,di);return}if(!ft(ii,mi)&&((ri||ut(mi,ii))&&mi.preventDefault(),!ct(ni,ii,mi))){var hi=ae(mi);if(hi.triggerSpec=ni,hi.handledFor==null&&(hi.handledFor=[]),hi.handledFor.indexOf(ii)<0){if(hi.handledFor.push(ii),ni.consume&&mi.stopPropagation(),ni.target&&mi.target&&!h(mi.target,ni.target))return;if(ni.once){if(si.triggeredOnce)return;si.triggeredOnce=!0}if(ni.changed){var _i=ae(li);if(_i.lastValue===li.value)return;_i.lastValue=li.value}if(si.delayed&&clearTimeout(si.delayed),si.throttle)return;ni.throttle>0?si.throttle||(ti(ii,mi),si.throttle=setTimeout(function(){si.throttle=null},ni.throttle)):ni.delay>0?si.delayed=setTimeout(function(){ti(ii,mi)},ni.delay):(ce(ii,"htmx:trigger"),ti(ii,mi))}}};ei.listenerInfos==null&&(ei.listenerInfos=[]),ei.listenerInfos.push({trigger:ni.trigger,listener:di,on:li}),li.addEventListener(ni.trigger,di)})}var vt=!1,dt=null;function gt(){dt||(dt=function(){vt=!0},window.addEventListener("scroll",dt),setInterval(function(){vt&&(vt=!1,oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(ii){pt(ii)}))},200))}function pt(ii){if(!o(ii,"data-hx-revealed")&&X(ii)){ii.setAttribute("data-hx-revealed","true");var ti=ae(ii);ti.initHash?ce(ii,"revealed"):ii.addEventListener("htmx:afterProcessNode",function(ei){ce(ii,"revealed")},{once:!0})}}function mt(ii,ti,ei){for(var ni=D(ei),ri=0;ri=0){var ai=wt(ei);setTimeout(function(){xt(ii,ti,ei+1)},ai)}},ri.onopen=function(si){ei=0},ae(ii).webSocket=ri,ri.addEventListener("message",function(si){if(!yt(ii)){var ai=si.data;R(ii,function(Ei){ai=Ei.transformResponse(ai,null,ii)});for(var li=T(ii),di=l(ai),mi=M(di.children),hi=0;hi0){ce(ii,"htmx:validation:halted",ai);return}ni.send(JSON.stringify(hi)),ut(ei,ii)&&ei.preventDefault()}):fe(ii,"htmx:noWebSocketSourceError")}function wt(ii){var ti=Q.config.wsReconnectDelay;if(typeof ti=="function")return ti(ii);if(ti==="full-jitter"){var ei=Math.min(ii,6),ni=1e3*Math.pow(2,ei);return ni*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(ii,ti,ei){for(var ni=D(ei),ri=0;ri0?setTimeout(ri,ni):ri()}function Ht(ii,ti,ei){var ni=!1;return oe(w,function(ri){if(o(ii,"hx-"+ri)){var si=te(ii,"hx-"+ri);ni=!0,ti.path=si,ti.verb=ri,ei.forEach(function(ai){Lt(ii,ai,ti,function(li,di){if(v(li,Q.config.disableSelector)){m(li);return}he(ri,si,li,di)})})}}),ni}function Lt(ii,ti,ei,ni){if(ti.sseEvent)Rt(ii,ni,ti.sseEvent);else if(ti.trigger==="revealed")gt(),ht(ii,ni,ei,ti),pt(ii);else if(ti.trigger==="intersect"){var ri={};ti.root&&(ri.root=ue(ii,ti.root)),ti.threshold&&(ri.threshold=parseFloat(ti.threshold));var si=new IntersectionObserver(function(ai){for(var li=0;li0?(ei.polling=!0,ot(ii,ni,ti)):ht(ii,ni,ei,ti)}function At(ii){if(!ii.htmxExecuted&&Q.config.allowScriptTags&&(ii.type==="text/javascript"||ii.type==="module"||ii.type==="")){var ti=re().createElement("script");oe(ii.attributes,function(ni){ti.setAttribute(ni.name,ni.value)}),ti.textContent=ii.textContent,ti.async=!1,Q.config.inlineScriptNonce&&(ti.nonce=Q.config.inlineScriptNonce);var ei=ii.parentElement;try{ei.insertBefore(ti,ii)}catch(ni){b(ni)}finally{ii.parentElement&&ii.parentElement.removeChild(ii)}}}function Nt(ii){h(ii,"script")&&At(ii),oe(f(ii,"script"),function(ti){At(ti)})}function It(ii){var ti=ii.attributes;if(!ti)return!1;for(var ei=0;ei0;){var ai=ni.shift(),li=ai.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);si===0&&li?(ai.split(":"),ri=li[1].slice(0,-1),ei[ri]=li[2]):ei[ri]+=ai,si+=Bt(ai)}for(var di in ei)Ft(ii,di,ei[di])}}function jt(ii){Ae(ii);for(var ti=0;tiQ.config.historyCacheSize;)ri.shift();for(;ri.length>0;)try{localStorage.setItem("htmx-history-cache",JSON.stringify(ri));break}catch(li){fe(re().body,"htmx:historyCacheError",{cause:li,cache:ri}),ri.shift()}}}function Yt(ii){if(!U())return null;ii=B(ii);for(var ti=E(localStorage.getItem("htmx-history-cache"))||[],ei=0;ei=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",ei);var ni=l(this.response);ni=ni.querySelector("[hx-history-elt],[data-hx-history-elt]")||ni;var ri=Zt(),si=T(ri),ai=Ve(this.response);if(ai){var li=C("title");li?li.innerHTML=ai:window.document.title=ai}Ue(ri,ni,si),nr(si.tasks),Jt=ii,ce(re().body,"htmx:historyRestore",{path:ii,cacheMiss:!0,serverResponse:this.response})}else fe(re().body,"htmx:historyCacheMissLoadError",ei)},ti.send()}function ar(ii){er(),ii=ii||location.pathname+location.search;var ti=Yt(ii);if(ti){var ei=l(ti.content),ni=Zt(),ri=T(ni);Ue(ni,ei,ri),nr(ri.tasks),document.title=ti.title,setTimeout(function(){window.scrollTo(0,ti.scroll)},0),Jt=ii,ce(re().body,"htmx:historyRestore",{path:ii,item:ti})}else Q.config.refreshOnHistoryMiss?window.location.reload(!0):ir(ii)}function or(ii){var ti=me(ii,"hx-indicator");return ti==null&&(ti=[ii]),oe(ti,function(ei){var ni=ae(ei);ni.requestCount=(ni.requestCount||0)+1,ei.classList.add.call(ei.classList,Q.config.requestClass)}),ti}function sr(ii){var ti=me(ii,"hx-disabled-elt");return ti==null&&(ti=[]),oe(ti,function(ei){var ni=ae(ei);ni.requestCount=(ni.requestCount||0)+1,ei.setAttribute("disabled","")}),ti}function lr(ii,ti){oe(ii,function(ei){var ni=ae(ei);ni.requestCount=(ni.requestCount||0)-1,ni.requestCount===0&&ei.classList.remove.call(ei.classList,Q.config.requestClass)}),oe(ti,function(ei){var ni=ae(ei);ni.requestCount=(ni.requestCount||0)-1,ni.requestCount===0&&ei.removeAttribute("disabled")})}function ur(ii,ti){for(var ei=0;ei=0}function wr(ii,ti){var ei=ti||ne(ii,"hx-swap"),ni={swapStyle:ae(ii).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(ii).boosted&&!br(ii)&&(ni.show="top"),ei){var ri=D(ei);if(ri.length>0)for(var si=0;si0?di.join(":"):null;ni.scroll=mi,ni.scrollTarget=hi}else if(ai.indexOf("show:")===0){var _i=ai.substr(5),di=_i.split(":"),Ei=di.pop(),hi=di.length>0?di.join(":"):null;ni.show=Ei,ni.showTarget=hi}else if(ai.indexOf("focus-scroll:")===0){var Ai=ai.substr(13);ni.focusScroll=Ai=="true"}else si==0?ni.swapStyle=ai:b("Unknown modifier in hx-swap: "+ai)}}return ni}function Sr(ii){return ne(ii,"hx-encoding")==="multipart/form-data"||h(ii,"form")&&ee(ii,"enctype")==="multipart/form-data"}function Er(ii,ti,ei){var ni=null;return R(ti,function(ri){ni==null&&(ni=ri.encodeParameters(ii,ei,ti))}),ni!=null?ni:Sr(ti)?mr(ei):pr(ei)}function T(ii){return{tasks:[],elts:[ii]}}function Cr(ii,ti){var ei=ii[0],ni=ii[ii.length-1];if(ti.scroll){var ri=null;ti.scrollTarget&&(ri=ue(ei,ti.scrollTarget)),ti.scroll==="top"&&(ei||ri)&&(ri=ri||ei,ri.scrollTop=0),ti.scroll==="bottom"&&(ni||ri)&&(ri=ri||ni,ri.scrollTop=ri.scrollHeight)}if(ti.show){var ri=null;if(ti.showTarget){var si=ti.showTarget;ti.showTarget==="window"&&(si="body"),ri=ue(ei,si)}ti.show==="top"&&(ei||ri)&&(ri=ri||ei,ri.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})),ti.show==="bottom"&&(ni||ri)&&(ri=ri||ni,ri.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior}))}}function Rr(ii,ti,ei,ni){if(ni==null&&(ni={}),ii==null)return ni;var ri=te(ii,ti);if(ri){var si=ri.trim(),ai=ei;if(si==="unset")return null;si.indexOf("javascript:")===0?(si=si.substr(11),ai=!0):si.indexOf("js:")===0&&(si=si.substr(3),ai=!0),si.indexOf("{")!==0&&(si="{"+si+"}");var li;ai?li=Tr(ii,function(){return Function("return ("+si+")")()},{}):li=E(si);for(var di in li)li.hasOwnProperty(di)&&ni[di]==null&&(ni[di]=li[di])}return Rr(u(ii),ti,ei,ni)}function Tr(ii,ti,ei){return Q.config.allowEval?ti():(fe(ii,"htmx:evalDisallowedError"),ei)}function Or(ii,ti){return Rr(ii,"hx-vars",!0,ti)}function qr(ii,ti){return Rr(ii,"hx-vals",!1,ti)}function Hr(ii){return le(Or(ii),qr(ii))}function Lr(ii,ti,ei){if(ei!==null)try{ii.setRequestHeader(ti,ei)}catch(ni){ii.setRequestHeader(ti,encodeURIComponent(ei)),ii.setRequestHeader(ti+"-URI-AutoEncoded","true")}}function Ar(ii){if(ii.responseURL&&typeof URL!="undefined")try{var ti=new URL(ii.responseURL);return ti.pathname+ti.search}catch(ei){fe(re().body,"htmx:badResponseUrl",{url:ii.responseURL})}}function O(ii,ti){return ti.test(ii.getAllResponseHeaders())}function Nr(ii,ti,ei){return ii=ii.toLowerCase(),ei?ei instanceof Element||I(ei,"String")?he(ii,ti,null,null,{targetOverride:p(ei),returnPromise:!0}):he(ii,ti,p(ei.source),ei.event,{handler:ei.handler,headers:ei.headers,values:ei.values,targetOverride:p(ei.target),swapOverride:ei.swap,select:ei.select,returnPromise:!0}):he(ii,ti,null,null,{returnPromise:!0})}function Ir(ii){for(var ti=[];ii;)ti.push(ii),ii=ii.parentElement;return ti}function kr(ii,ti,ei){var ni,ri;if(typeof URL=="function"){ri=new URL(ti,document.location.href);var si=document.location.origin;ni=si===ri.origin}else ri=ti,ni=g(ti,document.location.origin);return Q.config.selfRequestsOnly&&!ni?!1:ce(ii,"htmx:validateUrl",le({url:ri,sameHost:ni},ei))}function he(ii,ti,ei,ni,ri,si){var ai=null,li=null;if(ri=ri!=null?ri:{},ri.returnPromise&&typeof Promise!="undefined")var di=new Promise(function(qn,yn){ai=qn,li=yn});ei==null&&(ei=re().body);var mi=ri.handler||Mr,hi=ri.select||null;if(!se(ei))return ie(ai),di;var _i=ri.targetOverride||ye(ei);if(_i==null||_i==pe)return fe(ei,"htmx:targetError",{target:te(ei,"hx-target")}),ie(li),di;var Ei=ae(ei),Ai=Ei.lastButtonClicked;if(Ai){var ki=ee(Ai,"formaction");ki!=null&&(ti=ki);var Ti=ee(Ai,"formmethod");Ti!=null&&Ti.toLowerCase()!=="dialog"&&(ii=Ti)}var Fi=ne(ei,"hx-confirm");if(si===void 0){var en=function(qn){return he(ii,ti,ei,ni,ri,!!qn)},rn={target:_i,elt:ei,path:ti,verb:ii,triggeringEvent:ni,etc:ri,issueRequest:en,question:Fi};if(ce(ei,"htmx:confirm",rn)===!1)return ie(ai),di}var ln=ei,Yi=ne(ei,"hx-sync"),on=null,an=!1;if(Yi){var bn=Yi.split(":"),hn=bn[0].trim();if(hn==="this"?ln=xe(ei,"hx-sync"):ln=ue(ei,hn),Yi=(bn[1]||"drop").trim(),Ei=ae(ln),Yi==="drop"&&Ei.xhr&&Ei.abortable!==!0)return ie(ai),di;if(Yi==="abort"){if(Ei.xhr)return ie(ai),di;an=!0}else if(Yi==="replace")ce(ln,"htmx:abort");else if(Yi.indexOf("queue")===0){var pn=Yi.split(" ");on=(pn[1]||"last").trim()}}if(Ei.xhr)if(Ei.abortable)ce(ln,"htmx:abort");else{if(on==null){if(ni){var gn=ae(ni);gn&&gn.triggerSpec&&gn.triggerSpec.queue&&(on=gn.triggerSpec.queue)}on==null&&(on="last")}return Ei.queuedRequests==null&&(Ei.queuedRequests=[]),on==="first"&&Ei.queuedRequests.length===0?Ei.queuedRequests.push(function(){he(ii,ti,ei,ni,ri)}):on==="all"?Ei.queuedRequests.push(function(){he(ii,ti,ei,ni,ri)}):on==="last"&&(Ei.queuedRequests=[],Ei.queuedRequests.push(function(){he(ii,ti,ei,ni,ri)})),ie(ai),di}var un=new XMLHttpRequest;Ei.xhr=un,Ei.abortable=an;var vn=function(){if(Ei.xhr=null,Ei.abortable=!1,Ei.queuedRequests!=null&&Ei.queuedRequests.length>0){var qn=Ei.queuedRequests.shift();qn()}},Tn=ne(ei,"hx-prompt");if(Tn){var Ni=prompt(Tn);if(Ni===null||!ce(ei,"htmx:prompt",{prompt:Ni,target:_i}))return ie(ai),vn(),di}if(Fi&&!si&&!confirm(Fi))return ie(ai),vn(),di;var Hi=xr(ei,_i,Ni);ii!=="get"&&!Sr(ei)&&(Hi["Content-Type"]="application/x-www-form-urlencoded"),ri.headers&&(Hi=le(Hi,ri.headers));var Pi=dr(ei,ii),$i=Pi.errors,mn=Pi.values;ri.values&&(mn=le(mn,ri.values));var tn=Hr(ei),zi=le(mn,tn),Li=yr(zi,ei);Q.config.getCacheBusterParam&&ii==="get"&&(Li["org.htmx.cache-buster"]=ee(_i,"id")||"true"),(ti==null||ti==="")&&(ti=re().location.href);var ji=Rr(ei,"hx-request"),Ji=ae(ei).boosted,Vi=Q.config.methodsThatUseUrlParams.indexOf(ii)>=0,Ii={boosted:Ji,useUrlParams:Vi,parameters:Li,unfilteredParameters:zi,headers:Hi,target:_i,verb:ii,errors:$i,withCredentials:ri.credentials||ji.credentials||Q.config.withCredentials,timeout:ri.timeout||ji.timeout||Q.config.timeout,path:ti,triggeringEvent:ni};if(!ce(ei,"htmx:configRequest",Ii))return ie(ai),vn(),di;if(ti=Ii.path,ii=Ii.verb,Hi=Ii.headers,Li=Ii.parameters,$i=Ii.errors,Vi=Ii.useUrlParams,$i&&$i.length>0)return ce(ei,"htmx:validation:halted",Ii),ie(ai),vn(),di;var Sn=ti.split("#"),Yn=Sn[0],Fn=Sn[1],Bn=ti;if(Vi){Bn=Yn;var Gn=Object.keys(Li).length!==0;Gn&&(Bn.indexOf("?")<0?Bn+="?":Bn+="&",Bn+=pr(Li),Fn&&(Bn+="#"+Fn))}if(!kr(ei,Bn,Ii))return fe(ei,"htmx:invalidPath",Ii),ie(li),di;if(un.open(ii.toUpperCase(),Bn,!0),un.overrideMimeType("text/html"),un.withCredentials=Ii.withCredentials,un.timeout=Ii.timeout,!ji.noHeaders){for(var Qn in Hi)if(Hi.hasOwnProperty(Qn)){var Ts=Hi[Qn];Lr(un,Qn,Ts)}}var zn={xhr:un,target:_i,requestConfig:Ii,etc:ri,boosted:Ji,select:hi,pathInfo:{requestPath:ti,finalRequestPath:Bn,anchor:Fn}};if(un.onload=function(){try{var qn=Ir(ei);if(zn.pathInfo.responsePath=Ar(un),mi(ei,zn),lr(hs,ms),ce(ei,"htmx:afterRequest",zn),ce(ei,"htmx:afterOnLoad",zn),!se(ei)){for(var yn=null;qn.length>0&&yn==null;){var Kr=qn.shift();se(Kr)&&(yn=Kr)}yn&&(ce(yn,"htmx:afterRequest",zn),ce(yn,"htmx:afterOnLoad",zn))}ie(ai),vn()}catch(as){throw fe(ei,"htmx:onLoadError",le({error:as},zn)),as}},un.onerror=function(){lr(hs,ms),fe(ei,"htmx:afterRequest",zn),fe(ei,"htmx:sendError",zn),ie(li),vn()},un.onabort=function(){lr(hs,ms),fe(ei,"htmx:afterRequest",zn),fe(ei,"htmx:sendAbort",zn),ie(li),vn()},un.ontimeout=function(){lr(hs,ms),fe(ei,"htmx:afterRequest",zn),fe(ei,"htmx:timeout",zn),ie(li),vn()},!ce(ei,"htmx:beforeRequest",zn))return ie(ai),vn(),di;var hs=or(ei),ms=sr(ei);oe(["loadstart","loadend","progress","abort"],function(qn){oe([un,un.upload],function(yn){yn.addEventListener(qn,function(Kr){ce(ei,"htmx:xhr:"+qn,{lengthComputable:Kr.lengthComputable,loaded:Kr.loaded,total:Kr.total})})})}),ce(ei,"htmx:beforeSend",zn);var qs=Vi?null:Er(un,ei,Li);return un.send(qs),di}function Pr(ii,ti){var ei=ti.xhr,ni=null,ri=null;if(O(ei,/HX-Push:/i)?(ni=ei.getResponseHeader("HX-Push"),ri="push"):O(ei,/HX-Push-Url:/i)?(ni=ei.getResponseHeader("HX-Push-Url"),ri="push"):O(ei,/HX-Replace-Url:/i)&&(ni=ei.getResponseHeader("HX-Replace-Url"),ri="replace"),ni)return ni==="false"?{}:{type:ri,path:ni};var si=ti.pathInfo.finalRequestPath,ai=ti.pathInfo.responsePath,li=ne(ii,"hx-push-url"),di=ne(ii,"hx-replace-url"),mi=ae(ii).boosted,hi=null,_i=null;return li?(hi="push",_i=li):di?(hi="replace",_i=di):mi&&(hi="push",_i=ai||si),_i?_i==="false"?{}:(_i==="true"&&(_i=ai||si),ti.pathInfo.anchor&&_i.indexOf("#")===-1&&(_i=_i+"#"+ti.pathInfo.anchor),{type:hi,path:_i}):{}}function Mr(ii,ti){var ei=ti.xhr,ni=ti.target,ri=ti.etc,si=ti.requestConfig,ai=ti.select;if(ce(ii,"htmx:beforeOnLoad",ti)){if(O(ei,/HX-Trigger:/i)&&_e(ei,"HX-Trigger",ii),O(ei,/HX-Location:/i)){er();var li=ei.getResponseHeader("HX-Location"),di;li.indexOf("{")===0&&(di=E(li),li=di.path,delete di.path),Nr("GET",li,di).then(function(){tr(li)});return}var mi=O(ei,/HX-Refresh:/i)&&ei.getResponseHeader("HX-Refresh")==="true";if(O(ei,/HX-Redirect:/i)){location.href=ei.getResponseHeader("HX-Redirect"),mi&&location.reload();return}if(mi){location.reload();return}O(ei,/HX-Retarget:/i)&&(ei.getResponseHeader("HX-Retarget")==="this"?ti.target=ii:ti.target=ue(ii,ei.getResponseHeader("HX-Retarget")));var hi=Pr(ii,ti),_i=ei.status>=200&&ei.status<400&&ei.status!==204,Ei=ei.response,Ai=ei.status>=400,ki=Q.config.ignoreTitle,Ti=le({shouldSwap:_i,serverResponse:Ei,isError:Ai,ignoreTitle:ki},ti);if(ce(ni,"htmx:beforeSwap",Ti)){if(ni=Ti.target,Ei=Ti.serverResponse,Ai=Ti.isError,ki=Ti.ignoreTitle,ti.target=ni,ti.failed=Ai,ti.successful=!Ai,Ti.shouldSwap){ei.status===286&&at(ii),R(ii,function(hn){Ei=hn.transformResponse(Ei,ei,ii)}),hi.type&&er();var Fi=ri.swapOverride;O(ei,/HX-Reswap:/i)&&(Fi=ei.getResponseHeader("HX-Reswap"));var di=wr(ii,Fi);di.hasOwnProperty("ignoreTitle")&&(ki=di.ignoreTitle),ni.classList.add(Q.config.swappingClass);var en=null,rn=null,ln=function(){try{var hn=document.activeElement,pn={};try{pn={elt:hn,start:hn?hn.selectionStart:null,end:hn?hn.selectionEnd:null}}catch(Pi){}var gn;ai&&(gn=ai),O(ei,/HX-Reselect:/i)&&(gn=ei.getResponseHeader("HX-Reselect")),hi.type&&(ce(re().body,"htmx:beforeHistoryUpdate",le({history:hi},ti)),hi.type==="push"?(tr(hi.path),ce(re().body,"htmx:pushedIntoHistory",{path:hi.path})):(rr(hi.path),ce(re().body,"htmx:replacedInHistory",{path:hi.path})));var un=T(ni);if(je(di.swapStyle,ni,ii,Ei,un,gn),pn.elt&&!se(pn.elt)&&ee(pn.elt,"id")){var vn=document.getElementById(ee(pn.elt,"id")),Tn={preventScroll:di.focusScroll!==void 0?!di.focusScroll:!Q.config.defaultFocusScroll};if(vn){if(pn.start&&vn.setSelectionRange)try{vn.setSelectionRange(pn.start,pn.end)}catch(Pi){}vn.focus(Tn)}}if(ni.classList.remove(Q.config.swappingClass),oe(un.elts,function(Pi){Pi.classList&&Pi.classList.add(Q.config.settlingClass),ce(Pi,"htmx:afterSwap",ti)}),O(ei,/HX-Trigger-After-Swap:/i)){var Ni=ii;se(ii)||(Ni=re().body),_e(ei,"HX-Trigger-After-Swap",Ni)}var Hi=function(){if(oe(un.tasks,function(tn){tn.call()}),oe(un.elts,function(tn){tn.classList&&tn.classList.remove(Q.config.settlingClass),ce(tn,"htmx:afterSettle",ti)}),ti.pathInfo.anchor){var Pi=re().getElementById(ti.pathInfo.anchor);Pi&&Pi.scrollIntoView({block:"start",behavior:"auto"})}if(un.title&&!ki){var $i=C("title");$i?$i.innerHTML=un.title:window.document.title=un.title}if(Cr(un.elts,di),O(ei,/HX-Trigger-After-Settle:/i)){var mn=ii;se(ii)||(mn=re().body),_e(ei,"HX-Trigger-After-Settle",mn)}ie(en)};di.settleDelay>0?setTimeout(Hi,di.settleDelay):Hi()}catch(Pi){throw fe(ii,"htmx:swapError",ti),ie(rn),Pi}},Yi=Q.config.globalViewTransitions;if(di.hasOwnProperty("transition")&&(Yi=di.transition),Yi&&ce(ii,"htmx:beforeTransition",ti)&&typeof Promise!="undefined"&&document.startViewTransition){var on=new Promise(function(hn,pn){en=hn,rn=pn}),an=ln;ln=function(){document.startViewTransition(function(){return an(),on})}}di.swapDelay>0?setTimeout(ln,di.swapDelay):ln()}Ai&&fe(ii,"htmx:responseError",le({error:"Response Status Error Code "+ei.status+" from "+ti.pathInfo.requestPath},ti))}}}var Xr={};function Dr(){return{init:function(ii){return null},onEvent:function(ii,ti){return!0},transformResponse:function(ii,ti,ei){return ii},isInlineSwap:function(ii){return!1},handleSwap:function(ii,ti,ei,ni){return!1},encodeParameters:function(ii,ti,ei){return null}}}function Ur(ii,ti){ti.init&&ti.init(r),Xr[ii]=le(Dr(),ti)}function Br(ii){delete Xr[ii]}function Fr(ii,ti,ei){if(ii==null)return ti;ti==null&&(ti=[]),ei==null&&(ei=[]);var ni=te(ii,"hx-ext");return ni&&oe(ni.split(","),function(ri){if(ri=ri.replace(/ /g,""),ri.slice(0,7)=="ignore:"){ei.push(ri.slice(7));return}if(ei.indexOf(ri)<0){var si=Xr[ri];si&&ti.indexOf(si)<0&&ti.push(si)}}),Fr(u(ii),ti,ei)}var Vr=!1;re().addEventListener("DOMContentLoaded",function(){Vr=!0});function jr(ii){Vr||re().readyState==="complete"?ii():re().addEventListener("DOMContentLoaded",ii)}function _r(){Q.config.includeIndicatorStyles!==!1&&re().head.insertAdjacentHTML("beforeend","")}function zr(){var ii=re().querySelector('meta[name="htmx-config"]');return ii?E(ii.content):null}function $r(){var ii=zr();ii&&(Q.config=le(Q.config,ii))}return jr(function(){$r(),_r();var ii=re().body;zt(ii);var ti=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");ii.addEventListener("htmx:abort",function(ni){var ri=ni.target,si=ae(ri);si&&si.xhr&&si.xhr.abort()});let ei=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(ni){ni.state&&ni.state.htmx?(ar(),oe(ti,function(ri){ce(ri,"htmx:restored",{document:re(),triggerEvent:ce})})):ei&&ei(ni)},setTimeout(function(){ce(ii,"htmx:load",{}),ii=null},0)}),Q}()})});var El=Ya((Bc,zc)=>{(function(ii,ti){typeof Bc=="object"&&typeof zc!="undefined"?zc.exports=ti():typeof define=="function"&&define.amd?define(ti):(ii=typeof globalThis!="undefined"?globalThis:ii||self,ii.TomSelect=ti())})(Bc,function(){"use strict";function ii(fi,oi){fi.split(/\s+/).forEach(ci=>{oi(ci)})}class ti{constructor(){this._events=void 0,this._events={}}on(oi,ci){ii(oi,ui=>{let gi=this._events[ui]||[];gi.push(ci),this._events[ui]=gi})}off(oi,ci){var ui=arguments.length;if(ui===0){this._events={};return}ii(oi,gi=>{if(ui===1){delete this._events[gi];return}let bi=this._events[gi];bi!==void 0&&(bi.splice(bi.indexOf(ci),1),this._events[gi]=bi)})}trigger(oi,...ci){var ui=this;ii(oi,gi=>{let bi=ui._events[gi];bi!==void 0&&bi.forEach(yi=>{yi.apply(ui,ci)})})}}function ei(fi){return fi.plugins={},class extends fi{constructor(...oi){super(...oi),this.plugins={names:[],settings:{},requested:{},loaded:{}}}static define(oi,ci){fi.plugins[oi]={name:oi,fn:ci}}initializePlugins(oi){var ci,ui;let gi=this,bi=[];if(Array.isArray(oi))oi.forEach(yi=>{typeof yi=="string"?bi.push(yi):(gi.plugins.settings[yi.name]=yi.options,bi.push(yi.name))});else if(oi)for(ci in oi)oi.hasOwnProperty(ci)&&(gi.plugins.settings[ci]=oi[ci],bi.push(ci));for(;ui=bi.shift();)gi.require(ui)}loadPlugin(oi){var ci=this,ui=ci.plugins,gi=fi.plugins[oi];if(!fi.plugins.hasOwnProperty(oi))throw new Error('Unable to find "'+oi+'" plugin');ui.requested[oi]=!0,ui.loaded[oi]=gi.fn.apply(ci,[ci.plugins.settings[oi]||{}]),ui.names.push(oi)}require(oi){var ci=this,ui=ci.plugins;if(!ci.plugins.loaded.hasOwnProperty(oi)){if(ui.requested[oi])throw new Error('Plugin has circular dependency ("'+oi+'")');ci.loadPlugin(oi)}return ui.loaded[oi]}}}let ni=fi=>(fi=fi.filter(Boolean),fi.length<2?fi[0]||"":di(fi)==1?"["+fi.join("")+"]":"(?:"+fi.join("|")+")"),ri=fi=>{if(!ai(fi))return fi.join("");let oi="",ci=0,ui=()=>{ci>1&&(oi+="{"+ci+"}")};return fi.forEach((gi,bi)=>{if(gi===fi[bi-1]){ci++;return}ui(),oi+=gi,ci=1}),ui(),oi},si=fi=>{let oi=hi(fi);return ni(oi)},ai=fi=>new Set(fi).size!==fi.length,li=fi=>(fi+"").replace(/([\$\(\)\*\+\.\?\[\]\^\{\|\}\\])/gu,"\\$1"),di=fi=>fi.reduce((oi,ci)=>Math.max(oi,mi(ci)),0),mi=fi=>hi(fi).length,hi=fi=>Array.from(fi);let _i=fi=>{if(fi.length===1)return[[fi]];let oi=[],ci=fi.substring(1);return _i(ci).forEach(function(gi){let bi=gi.slice(0);bi[0]=fi.charAt(0)+bi[0],oi.push(bi),bi=gi.slice(0),bi.unshift(fi.charAt(0)),oi.push(bi)}),oi};let Ei=[[0,65535]],Ai="[\u0300-\u036F\xB7\u02BE\u02BC]",ki,Ti,Fi=3,en={},rn={"/":"\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 fi in rn){let oi=rn[fi]||"";for(let ci=0;ci{ki===void 0&&(ki=gn(fi||Ei))},on=(fi,oi="NFKD")=>fi.normalize(oi),an=fi=>hi(fi).reduce((oi,ci)=>oi+bn(ci),""),bn=fi=>(fi=on(fi).toLowerCase().replace(ln,oi=>en[oi]||""),on(fi,"NFC"));function*hn(fi){for(let[oi,ci]of fi)for(let ui=oi;ui<=ci;ui++){let gi=String.fromCharCode(ui),bi=an(gi);bi!=gi.toLowerCase()&&(bi.length>Fi||bi.length!=0&&(yield{folded:bi,composed:gi,code_point:ui}))}}let pn=fi=>{let oi={},ci=(ui,gi)=>{let bi=oi[ui]||new Set,yi=new RegExp("^"+si(bi)+"$","iu");gi.match(yi)||(bi.add(li(gi)),oi[ui]=bi)};for(let ui of hn(fi))ci(ui.folded,ui.folded),ci(ui.folded,ui.composed);return oi},gn=fi=>{let oi=pn(fi),ci={},ui=[];for(let bi in oi){let yi=oi[bi];yi&&(ci[bi]=si(yi)),bi.length>1&&ui.push(li(bi))}ui.sort((bi,yi)=>yi.length-bi.length);let gi=ni(ui);return Ti=new RegExp("^"+gi,"u"),ci},un=(fi,oi=1)=>{let ci=0;return fi=fi.map(ui=>(ki[ui]&&(ci+=ui.length),ki[ui]||ui)),ci>=oi?ri(fi):""},vn=(fi,oi=1)=>(oi=Math.max(oi,fi.length-1),ni(_i(fi).map(ci=>un(ci,oi)))),Tn=(fi,oi=!0)=>{let ci=fi.length>1?1:0;return ni(fi.map(ui=>{let gi=[],bi=oi?ui.length():ui.length()-1;for(let yi=0;yi{for(let ci of oi){if(ci.start!=fi.start||ci.end!=fi.end||ci.substrs.join("")!==fi.substrs.join(""))continue;let ui=fi.parts,gi=yi=>{for(let Ci of ui){if(Ci.start===yi.start&&Ci.substr===yi.substr)return!1;if(!(yi.length==1||Ci.length==1)&&(yi.startCi.start||Ci.startyi.start))return!0}return!1};if(!(ci.parts.filter(gi).length>0))return!0}return!1};class Hi{constructor(){this.parts=[],this.substrs=[],this.start=0,this.end=0}add(oi){oi&&(this.parts.push(oi),this.substrs.push(oi.substr),this.start=Math.min(oi.start,this.start),this.end=Math.max(oi.end,this.end))}last(){return this.parts[this.parts.length-1]}length(){return this.parts.length}clone(oi,ci){let ui=new Hi,gi=JSON.parse(JSON.stringify(this.parts)),bi=gi.pop();for(let Oi of gi)ui.add(Oi);let yi=ci.substr.substring(0,oi-bi.start),Ci=yi.length;return ui.add({start:bi.start,end:bi.start+Ci,length:Ci,substr:yi}),ui}}let Pi=fi=>{Yi(),fi=an(fi);let oi="",ci=[new Hi];for(let ui=0;ui0){Oi=Oi.sort((Wi,Bi)=>Wi.length()-Bi.length());for(let Wi of Oi)Ni(Wi,ci)||ci.push(Wi);continue}if(ui>0&&Di.size==1&&!Di.has("3")){oi+=Tn(ci,!1);let Wi=new Hi,Bi=ci[0];Bi&&Wi.add(Bi.last()),ci=[Wi]}}return oi+=Tn(ci,!0),oi};let $i=(fi,oi)=>{if(fi)return fi[oi]},mn=(fi,oi)=>{if(fi){for(var ci,ui=oi.split(".");(ci=ui.shift())&&(fi=fi[ci]););return fi}},tn=(fi,oi,ci)=>{var ui,gi;return!fi||(fi=fi+"",oi.regex==null)||(gi=fi.search(oi.regex),gi===-1)?0:(ui=oi.string.length/fi.length,gi===0&&(ui+=.5),ui*ci)},zi=(fi,oi)=>{var ci=fi[oi];if(typeof ci=="function")return ci;ci&&!Array.isArray(ci)&&(fi[oi]=[ci])},Li=(fi,oi)=>{if(Array.isArray(fi))fi.forEach(oi);else for(var ci in fi)fi.hasOwnProperty(ci)&&oi(fi[ci],ci)},ji=(fi,oi)=>typeof fi=="number"&&typeof oi=="number"?fi>oi?1:fioi?1:oi>fi?-1:0);class Ji{constructor(oi,ci){this.items=void 0,this.settings=void 0,this.items=oi,this.settings=ci||{diacritics:!0}}tokenize(oi,ci,ui){if(!oi||!oi.length)return[];let gi=[],bi=oi.split(/\s+/);var yi;return ui&&(yi=new RegExp("^("+Object.keys(ui).map(li).join("|")+"):(.*)$")),bi.forEach(Ci=>{let Oi,Di=null,Wi=null;yi&&(Oi=Ci.match(yi))&&(Di=Oi[1],Ci=Oi[2]),Ci.length>0&&(this.settings.diacritics?Wi=Pi(Ci)||null:Wi=li(Ci),Wi&&ci&&(Wi="\\b"+Wi)),gi.push({string:Ci,regex:Wi?new RegExp(Wi,"iu"):null,field:Di})}),gi}getScoreFunction(oi,ci){var ui=this.prepareSearch(oi,ci);return this._getScoreFunction(ui)}_getScoreFunction(oi){let ci=oi.tokens,ui=ci.length;if(!ui)return function(){return 0};let gi=oi.options.fields,bi=oi.weights,yi=gi.length,Ci=oi.getAttrFn;if(!yi)return function(){return 1};let Oi=function(){return yi===1?function(Di,Wi){let Bi=gi[0].field;return tn(Ci(Wi,Bi),Di,bi[Bi]||1)}:function(Di,Wi){var Bi=0;if(Di.field){let Zi=Ci(Wi,Di.field);!Di.regex&&Zi?Bi+=1/yi:Bi+=tn(Zi,Di,1)}else Li(bi,(Zi,In)=>{Bi+=tn(Ci(Wi,In),Di,Zi)});return Bi/yi}}();return ui===1?function(Di){return Oi(ci[0],Di)}:oi.options.conjunction==="and"?function(Di){var Wi,Bi=0;for(let Zi of ci){if(Wi=Oi(Zi,Di),Wi<=0)return 0;Bi+=Wi}return Bi/ui}:function(Di){var Wi=0;return Li(ci,Bi=>{Wi+=Oi(Bi,Di)}),Wi/ui}}getSortFunction(oi,ci){var ui=this.prepareSearch(oi,ci);return this._getSortFunction(ui)}_getSortFunction(oi){var ci,ui=[];let gi=this,bi=oi.options,yi=!oi.query&&bi.sort_empty?bi.sort_empty:bi.sort;if(typeof yi=="function")return yi.bind(this);let Ci=function(Wi,Bi){return Wi==="$score"?Bi.score:oi.getAttrFn(gi.items[Bi.id],Wi)};if(yi)for(let Di of yi)(oi.query||Di.field!=="$score")&&ui.push(Di);if(oi.query){ci=!0;for(let Di of ui)if(Di.field==="$score"){ci=!1;break}ci&&ui.unshift({field:"$score",direction:"desc"})}else ui=ui.filter(Di=>Di.field!=="$score");return ui.length?function(Di,Wi){var Bi,Zi;for(let In of ui)if(Zi=In.field,Bi=(In.direction==="desc"?-1:1)*ji(Ci(Zi,Di),Ci(Zi,Wi)),Bi)return Bi;return 0}:null}prepareSearch(oi,ci){let ui={};var gi=Object.assign({},ci);if(zi(gi,"sort"),zi(gi,"sort_empty"),gi.fields){zi(gi,"fields");let bi=[];gi.fields.forEach(yi=>{typeof yi=="string"&&(yi={field:yi,weight:1}),bi.push(yi),ui[yi.field]="weight"in yi?yi.weight:1}),gi.fields=bi}return{options:gi,query:oi.toLowerCase().trim(),tokens:this.tokenize(oi,gi.respect_word_boundaries,ui),total:0,items:[],weights:ui,getAttrFn:gi.nesting?mn:$i}}search(oi,ci){var ui=this,gi,bi;bi=this.prepareSearch(oi,ci),ci=bi.options,oi=bi.query;let yi=ci.score||ui._getScoreFunction(bi);oi.length?Li(ui.items,(Oi,Di)=>{gi=yi(Oi),(ci.filter===!1||gi>0)&&bi.items.push({score:gi,id:Di})}):Li(ui.items,(Oi,Di)=>{bi.items.push({score:1,id:Di})});let Ci=ui._getSortFunction(bi);return Ci&&bi.items.sort(Ci),bi.total=bi.items.length,typeof ci.limit=="number"&&(bi.items=bi.items.slice(0,ci.limit)),bi}}let Vi=(fi,oi)=>{if(Array.isArray(fi))fi.forEach(oi);else for(var ci in fi)fi.hasOwnProperty(ci)&&oi(fi[ci],ci)},Ii=fi=>{if(fi.jquery)return fi[0];if(fi instanceof HTMLElement)return fi;if(Sn(fi)){var oi=document.createElement("template");return oi.innerHTML=fi.trim(),oi.content.firstChild}return document.querySelector(fi)},Sn=fi=>typeof fi=="string"&&fi.indexOf("<")>-1,Yn=fi=>fi.replace(/['"\\]/g,"\\$&"),Fn=(fi,oi)=>{var ci=document.createEvent("HTMLEvents");ci.initEvent(oi,!0,!1),fi.dispatchEvent(ci)},Bn=(fi,oi)=>{Object.assign(fi.style,oi)},Gn=(fi,...oi)=>{var ci=Ts(oi);fi=zn(fi),fi.map(ui=>{ci.map(gi=>{ui.classList.add(gi)})})},Qn=(fi,...oi)=>{var ci=Ts(oi);fi=zn(fi),fi.map(ui=>{ci.map(gi=>{ui.classList.remove(gi)})})},Ts=fi=>{var oi=[];return Vi(fi,ci=>{typeof ci=="string"&&(ci=ci.trim().split(/[\11\12\14\15\40]/)),Array.isArray(ci)&&(oi=oi.concat(ci))}),oi.filter(Boolean)},zn=fi=>(Array.isArray(fi)||(fi=[fi]),fi),hs=(fi,oi,ci)=>{if(!(ci&&!ci.contains(fi)))for(;fi&&fi.matches;){if(fi.matches(oi))return fi;fi=fi.parentNode}},ms=(fi,oi=0)=>oi>0?fi[fi.length-1]:fi[0],qs=fi=>Object.keys(fi).length===0,qn=(fi,oi)=>{if(!fi)return-1;oi=oi||fi.nodeName;for(var ci=0;fi=fi.previousElementSibling;)fi.matches(oi)&&ci++;return ci},yn=(fi,oi)=>{Vi(oi,(ci,ui)=>{ci==null?fi.removeAttribute(ui):fi.setAttribute(ui,""+ci)})},Kr=(fi,oi)=>{fi.parentNode&&fi.parentNode.replaceChild(oi,fi)},as=(fi,oi)=>{if(oi===null)return;if(typeof oi=="string"){if(!oi.length)return;oi=new RegExp(oi,"i")}let ci=bi=>{var yi=bi.data.match(oi);if(yi&&bi.data.length>0){var Ci=document.createElement("span");Ci.className="highlight";var Oi=bi.splitText(yi.index);Oi.splitText(yi[0].length);var Di=Oi.cloneNode(!0);return Ci.appendChild(Di),Kr(Oi,Ci),1}return 0},ui=bi=>{bi.nodeType===1&&bi.childNodes&&!/(script|style)/i.test(bi.tagName)&&(bi.className!=="highlight"||bi.tagName!=="SPAN")&&Array.from(bi.childNodes).forEach(yi=>{gi(yi)})},gi=bi=>bi.nodeType===3?ci(bi):(ui(bi),0);gi(fi)},Ws=fi=>{var oi=fi.querySelectorAll("span.highlight");Array.prototype.forEach.call(oi,function(ci){var ui=ci.parentNode;ui.replaceChild(ci.firstChild,ci),ui.normalize()})},po=65,Us=13,Ms=27,Ss=37,Ro=38,Ys=39,Po=40,ha=8,Wl=46,pa=9,Ho=(typeof navigator=="undefined"?!1:/Mac/.test(navigator.userAgent))?"metaKey":"ctrlKey";var ja={options:[],optgroups:[],plugins:[],delimiter:",",splitOn:null,persist:!0,diacritics:!0,create:null,createOnBlur:!1,createFilter:null,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(fi){return fi.length>0},render:{}};let wn=fi=>typeof fi=="undefined"||fi===null?null:Gs(fi),Gs=fi=>typeof fi=="boolean"?fi?"1":"0":fi+"",Ks=fi=>(fi+"").replace(/&/g,"&").replace(//g,">").replace(/"/g,"""),Yl=(fi,oi)=>oi>0?setTimeout(fi,oi):(fi.call(null),null),Fo=(fi,oi)=>{var ci;return function(ui,gi){var bi=this;ci&&(bi.loading=Math.max(bi.loading-1,0),clearTimeout(ci)),ci=setTimeout(function(){ci=null,bi.loadedSearches[ui]=!0,fi.call(bi,ui,gi)},oi)}},$o=(fi,oi,ci)=>{var ui,gi=fi.trigger,bi={};fi.trigger=function(){var yi=arguments[0];if(oi.indexOf(yi)!==-1)bi[yi]=arguments;else return gi.apply(fi,arguments)},ci.apply(fi,[]),fi.trigger=gi;for(ui of oi)ui in bi&&gi.apply(fi,bi[ui])},Cs=fi=>({start:fi.selectionStart||0,length:(fi.selectionEnd||0)-(fi.selectionStart||0)}),Dn=(fi,oi=!1)=>{fi&&(fi.preventDefault(),oi&&fi.stopPropagation())},On=(fi,oi,ci,ui)=>{fi.addEventListener(oi,ci,ui)},pi=(fi,oi)=>{if(!oi||!oi[fi])return!1;var ci=(oi.altKey?1:0)+(oi.ctrlKey?1:0)+(oi.shiftKey?1:0)+(oi.metaKey?1:0);return ci===1},vi=(fi,oi)=>{let ci=fi.getAttribute("id");return ci||(fi.setAttribute("id",oi),oi)},wi=fi=>fi.replace(/[\\"']/g,"\\$&"),Si=(fi,oi)=>{oi&&fi.append(oi)};function Ri(fi,oi){var ci=Object.assign({},ja,oi),ui=ci.dataAttr,gi=ci.labelField,bi=ci.valueField,yi=ci.disabledField,Ci=ci.optgroupField,Oi=ci.optgroupLabelField,Di=ci.optgroupValueField,Wi=fi.tagName.toLowerCase(),Bi=fi.getAttribute("placeholder")||fi.getAttribute("data-placeholder");if(!Bi&&!ci.allowEmptyOption){let Cn=fi.querySelector('option[value=""]');Cn&&(Bi=Cn.textContent)}var Zi={placeholder:Bi,options:[],optgroups:[],items:[],maxItems:null},In=()=>{var Cn,Pn=Zi.options,kn={},dn=1;let jn=0;var Os=xn=>{var Ln=Object.assign({},xn.dataset),En=ui&&Ln[ui];return typeof En=="string"&&En.length&&(Ln=Object.assign(Ln,JSON.parse(En))),Ln},Va=(xn,Ln)=>{var En=wn(xn.value);if(En!=null&&!(!En&&!ci.allowEmptyOption)){if(kn.hasOwnProperty(En)){if(Ln){var Jr=kn[En][Ci];Jr?Array.isArray(Jr)?Jr.push(Ln):kn[En][Ci]=[Jr,Ln]:kn[En][Ci]=Ln}}else{var Nn=Os(xn);Nn[gi]=Nn[gi]||xn.textContent,Nn[bi]=Nn[bi]||En,Nn[yi]=Nn[yi]||xn.disabled,Nn[Ci]=Nn[Ci]||Ln,Nn.$option=xn,Nn.$order=Nn.$order||++jn,kn[En]=Nn,Pn.push(Nn)}xn.selected&&Zi.items.push(En)}},Bo=xn=>{var Ln,En;En=Os(xn),En[Oi]=En[Oi]||xn.getAttribute("label")||"",En[Di]=En[Di]||dn++,En[yi]=En[yi]||xn.disabled,En.$order=En.$order||++jn,Zi.optgroups.push(En),Ln=En[Di],Vi(xn.children,Jr=>{Va(Jr,Ln)})};Zi.maxItems=fi.hasAttribute("multiple")?null:1,Vi(fi.children,xn=>{Cn=xn.tagName.toLowerCase(),Cn==="optgroup"?Bo(xn):Cn==="option"&&Va(xn)})},Qi=()=>{let Cn=fi.getAttribute(ui);if(Cn)Zi.options=JSON.parse(Cn),Vi(Zi.options,kn=>{Zi.items.push(kn[bi])});else{var Pn=fi.value.trim()||"";if(!ci.allowEmptyOption&&!Pn.length)return;let kn=Pn.split(ci.delimiter);Vi(kn,dn=>{let jn={};jn[gi]=dn,jn[bi]=dn,Zi.options.push(jn)}),Zi.items=kn}};return Wi==="select"?In():Qi(),Object.assign({},ja,Zi,oi)}var qi=0;class nn extends ei(ti){constructor(oi,ci){super(),this.control_input=void 0,this.wrapper=void 0,this.dropdown=void 0,this.control=void 0,this.dropdown_content=void 0,this.focus_node=void 0,this.order=0,this.settings=void 0,this.input=void 0,this.tabIndex=void 0,this.is_select_tag=void 0,this.rtl=void 0,this.inputId=void 0,this._destroy=void 0,this.sifter=void 0,this.isOpen=!1,this.isDisabled=!1,this.isReadOnly=!1,this.isRequired=void 0,this.isInvalid=!1,this.isValid=!0,this.isLocked=!1,this.isFocused=!1,this.isInputHidden=!1,this.isSetup=!1,this.ignoreFocus=!1,this.ignoreHover=!1,this.hasOptions=!1,this.currentResults=void 0,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,qi++;var ui,gi=Ii(oi);if(gi.tomselect)throw new Error("Tom Select already initialized on this element");gi.tomselect=this;var bi=window.getComputedStyle&&window.getComputedStyle(gi,null);ui=bi.getPropertyValue("direction");let yi=Ri(gi,ci);this.settings=yi,this.input=gi,this.tabIndex=gi.tabIndex||0,this.is_select_tag=gi.tagName.toLowerCase()==="select",this.rtl=/rtl/i.test(ui),this.inputId=vi(gi,"tomselect-"+qi),this.isRequired=gi.required,this.sifter=new Ji(this.options,{diacritics:yi.diacritics}),yi.mode=yi.mode||(yi.maxItems===1?"single":"multi"),typeof yi.hideSelected!="boolean"&&(yi.hideSelected=yi.mode==="multi"),typeof yi.hidePlaceholder!="boolean"&&(yi.hidePlaceholder=yi.mode!=="multi");var Ci=yi.createFilter;typeof Ci!="function"&&(typeof Ci=="string"&&(Ci=new RegExp(Ci)),Ci instanceof RegExp?yi.createFilter=Pn=>Ci.test(Pn):yi.createFilter=Pn=>this.settings.duplicates||!this.options[Pn]),this.initializePlugins(yi.plugins),this.setupCallbacks(),this.setupTemplates();let Oi=Ii("
    "),Di=Ii("
    "),Wi=this._render("dropdown"),Bi=Ii('
    '),Zi=this.input.getAttribute("class")||"",In=yi.mode;var Qi;if(Gn(Oi,yi.wrapperClass,Zi,In),Gn(Di,yi.controlClass),Si(Oi,Di),Gn(Wi,yi.dropdownClass,In),yi.copyClassesToDropdown&&Gn(Wi,Zi),Gn(Bi,yi.dropdownContentClass),Si(Wi,Bi),Ii(yi.dropdownParent||Oi).appendChild(Wi),Sn(yi.controlInput)){Qi=Ii(yi.controlInput);var Cn=["autocorrect","autocapitalize","autocomplete","spellcheck"];Li(Cn,Pn=>{gi.getAttribute(Pn)&&yn(Qi,{[Pn]:gi.getAttribute(Pn)})}),Qi.tabIndex=-1,Di.appendChild(Qi),this.focus_node=Qi}else yi.controlInput?(Qi=Ii(yi.controlInput),this.focus_node=Qi):(Qi=Ii(""),this.focus_node=Di);this.wrapper=Oi,this.dropdown=Wi,this.dropdown_content=Bi,this.control=Di,this.control_input=Qi,this.setup()}setup(){let oi=this,ci=oi.settings,ui=oi.control_input,gi=oi.dropdown,bi=oi.dropdown_content,yi=oi.wrapper,Ci=oi.control,Oi=oi.input,Di=oi.focus_node,Wi={passive:!0},Bi=oi.inputId+"-ts-dropdown";yn(bi,{id:Bi}),yn(Di,{role:"combobox","aria-haspopup":"listbox","aria-expanded":"false","aria-controls":Bi});let Zi=vi(Di,oi.inputId+"-ts-control"),In="label[for='"+Yn(oi.inputId)+"']",Qi=document.querySelector(In),Cn=oi.focus.bind(oi);if(Qi){On(Qi,"click",Cn),yn(Qi,{for:Zi});let dn=vi(Qi,oi.inputId+"-ts-label");yn(Di,{"aria-labelledby":dn}),yn(bi,{"aria-labelledby":dn})}if(yi.style.width=Oi.style.width,oi.plugins.names.length){let dn="plugin-"+oi.plugins.names.join(" plugin-");Gn([yi,gi],dn)}(ci.maxItems===null||ci.maxItems>1)&&oi.is_select_tag&&yn(Oi,{multiple:"multiple"}),ci.placeholder&&yn(ui,{placeholder:ci.placeholder}),!ci.splitOn&&ci.delimiter&&(ci.splitOn=new RegExp("\\s*"+li(ci.delimiter)+"+\\s*")),ci.load&&ci.loadThrottle&&(ci.load=Fo(ci.load,ci.loadThrottle)),On(gi,"mousemove",()=>{oi.ignoreHover=!1}),On(gi,"mouseenter",dn=>{var jn=hs(dn.target,"[data-selectable]",gi);jn&&oi.onOptionHover(dn,jn)},{capture:!0}),On(gi,"click",dn=>{let jn=hs(dn.target,"[data-selectable]");jn&&(oi.onOptionSelect(dn,jn),Dn(dn,!0))}),On(Ci,"click",dn=>{var jn=hs(dn.target,"[data-ts-item]",Ci);if(jn&&oi.onItemSelect(dn,jn)){Dn(dn,!0);return}ui.value==""&&(oi.onClick(),Dn(dn,!0))}),On(Di,"keydown",dn=>oi.onKeyDown(dn)),On(ui,"keypress",dn=>oi.onKeyPress(dn)),On(ui,"input",dn=>oi.onInput(dn)),On(Di,"blur",dn=>oi.onBlur(dn)),On(Di,"focus",dn=>oi.onFocus(dn)),On(ui,"paste",dn=>oi.onPaste(dn));let Pn=dn=>{let jn=dn.composedPath()[0];if(!yi.contains(jn)&&!gi.contains(jn)){oi.isFocused&&oi.blur(),oi.inputState();return}jn==ui&&oi.isOpen?dn.stopPropagation():Dn(dn,!0)},kn=()=>{oi.isOpen&&oi.positionDropdown()};On(document,"mousedown",Pn),On(window,"scroll",kn,Wi),On(window,"resize",kn,Wi),this._destroy=()=>{document.removeEventListener("mousedown",Pn),window.removeEventListener("scroll",kn),window.removeEventListener("resize",kn),Qi&&Qi.removeEventListener("click",Cn)},this.revertSettings={innerHTML:Oi.innerHTML,tabIndex:Oi.tabIndex},Oi.tabIndex=-1,Oi.insertAdjacentElement("afterend",oi.wrapper),oi.sync(!1),ci.items=[],delete ci.optgroups,delete ci.options,On(Oi,"invalid",()=>{oi.isValid&&(oi.isValid=!1,oi.isInvalid=!0,oi.refreshState())}),oi.updateOriginalInput(),oi.refreshItems(),oi.close(!1),oi.inputState(),oi.isSetup=!0,Oi.disabled?oi.disable():Oi.readOnly?oi.setReadOnly(!0):oi.enable(),oi.on("change",this.onChange),Gn(Oi,"tomselected","ts-hidden-accessible"),oi.trigger("initialize"),ci.preload===!0&&oi.preload()}setupOptions(oi=[],ci=[]){this.addOptions(oi),Li(ci,ui=>{this.registerOptionGroup(ui)})}setupTemplates(){var oi=this,ci=oi.settings.labelField,ui=oi.settings.optgroupLabelField,gi={optgroup:bi=>{let yi=document.createElement("div");return yi.className="optgroup",yi.appendChild(bi.options),yi},optgroup_header:(bi,yi)=>'
    '+yi(bi[ui])+"
    ",option:(bi,yi)=>"
    "+yi(bi[ci])+"
    ",item:(bi,yi)=>"
    "+yi(bi[ci])+"
    ",option_create:(bi,yi)=>'
    Add '+yi(bi.input)+"
    ",no_results:()=>'
    No results found
    ',loading:()=>'
    ',not_loading:()=>{},dropdown:()=>"
    "};oi.settings.render=Object.assign({},gi,oi.settings.render)}setupCallbacks(){var oi,ci,ui={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(oi in ui)ci=this.settings[ui[oi]],ci&&this.on(oi,ci)}sync(oi=!0){let ci=this,ui=oi?Ri(ci.input,{delimiter:ci.settings.delimiter}):ci.settings;ci.setupOptions(ui.options,ui.optgroups),ci.setValue(ui.items||[],!0),ci.lastQuery=null}onClick(){var oi=this;if(oi.activeItems.length>0){oi.clearActiveItems(),oi.focus();return}oi.isFocused&&oi.isOpen?oi.blur():oi.focus()}onMouseDown(){}onChange(){Fn(this.input,"input"),Fn(this.input,"change")}onPaste(oi){var ci=this;if(ci.isInputHidden||ci.isLocked){Dn(oi);return}ci.settings.splitOn&&setTimeout(()=>{var ui=ci.inputValue();if(ui.match(ci.settings.splitOn)){var gi=ui.trim().split(ci.settings.splitOn);Li(gi,bi=>{wn(bi)&&(this.options[bi]?ci.addItem(bi):ci.createItem(bi))})}},0)}onKeyPress(oi){var ci=this;if(ci.isLocked){Dn(oi);return}var ui=String.fromCharCode(oi.keyCode||oi.which);if(ci.settings.create&&ci.settings.mode==="multi"&&ui===ci.settings.delimiter){ci.createItem(),Dn(oi);return}}onKeyDown(oi){var ci=this;if(ci.ignoreHover=!0,ci.isLocked){oi.keyCode!==pa&&Dn(oi);return}switch(oi.keyCode){case po:if(pi(Ho,oi)&&ci.control_input.value==""){Dn(oi),ci.selectAll();return}break;case Ms:ci.isOpen&&(Dn(oi,!0),ci.close()),ci.clearActiveItems();return;case Po:if(!ci.isOpen&&ci.hasOptions)ci.open();else if(ci.activeOption){let ui=ci.getAdjacent(ci.activeOption,1);ui&&ci.setActiveOption(ui)}Dn(oi);return;case Ro:if(ci.activeOption){let ui=ci.getAdjacent(ci.activeOption,-1);ui&&ci.setActiveOption(ui)}Dn(oi);return;case Us:ci.canSelect(ci.activeOption)?(ci.onOptionSelect(oi,ci.activeOption),Dn(oi)):(ci.settings.create&&ci.createItem()||document.activeElement==ci.control_input&&ci.isOpen)&&Dn(oi);return;case Ss:ci.advanceSelection(-1,oi);return;case Ys:ci.advanceSelection(1,oi);return;case pa:ci.settings.selectOnTab&&(ci.canSelect(ci.activeOption)&&(ci.onOptionSelect(oi,ci.activeOption),Dn(oi)),ci.settings.create&&ci.createItem()&&Dn(oi));return;case ha:case Wl:ci.deleteSelection(oi);return}ci.isInputHidden&&!pi(Ho,oi)&&Dn(oi)}onInput(oi){if(this.isLocked)return;let ci=this.inputValue();if(this.lastValue!==ci){if(this.lastValue=ci,ci==""){this._onInput();return}this.refreshTimeout&&clearTimeout(this.refreshTimeout),this.refreshTimeout=Yl(()=>{this.refreshTimeout=null,this._onInput()},this.settings.refreshThrottle)}}_onInput(){let oi=this.lastValue;this.settings.shouldLoad.call(this,oi)&&this.load(oi),this.refreshOptions(),this.trigger("type",oi)}onOptionHover(oi,ci){this.ignoreHover||this.setActiveOption(ci,!1)}onFocus(oi){var ci=this,ui=ci.isFocused;if(ci.isDisabled||ci.isReadOnly){ci.blur(),Dn(oi);return}ci.ignoreFocus||(ci.isFocused=!0,ci.settings.preload==="focus"&&ci.preload(),ui||ci.trigger("focus"),ci.activeItems.length||(ci.inputState(),ci.refreshOptions(!!ci.settings.openOnFocus)),ci.refreshState())}onBlur(oi){if(document.hasFocus()!==!1){var ci=this;if(ci.isFocused){ci.isFocused=!1,ci.ignoreFocus=!1;var ui=()=>{ci.close(),ci.setActiveItem(),ci.setCaret(ci.items.length),ci.trigger("blur")};ci.settings.create&&ci.settings.createOnBlur?ci.createItem(null,ui):ui()}}}onOptionSelect(oi,ci){var ui,gi=this;ci.parentElement&&ci.parentElement.matches("[data-disabled]")||(ci.classList.contains("create")?gi.createItem(null,()=>{gi.settings.closeAfterSelect&&gi.close()}):(ui=ci.dataset.value,typeof ui!="undefined"&&(gi.lastQuery=null,gi.addItem(ui),gi.settings.closeAfterSelect&&gi.close(),!gi.settings.hideSelected&&oi.type&&/click/.test(oi.type)&&gi.setActiveOption(ci))))}canSelect(oi){return!!(this.isOpen&&oi&&this.dropdown_content.contains(oi))}onItemSelect(oi,ci){var ui=this;return!ui.isLocked&&ui.settings.mode==="multi"?(Dn(oi),ui.setActiveItem(ci,oi),!0):!1}canLoad(oi){return!(!this.settings.load||this.loadedSearches.hasOwnProperty(oi))}load(oi){let ci=this;if(!ci.canLoad(oi))return;Gn(ci.wrapper,ci.settings.loadingClass),ci.loading++;let ui=ci.loadCallback.bind(ci);ci.settings.load.call(ci,oi,ui)}loadCallback(oi,ci){let ui=this;ui.loading=Math.max(ui.loading-1,0),ui.lastQuery=null,ui.clearActiveOption(),ui.setupOptions(oi,ci),ui.refreshOptions(ui.isFocused&&!ui.isInputHidden),ui.loading||Qn(ui.wrapper,ui.settings.loadingClass),ui.trigger("load",oi,ci)}preload(){var oi=this.wrapper.classList;oi.contains("preloaded")||(oi.add("preloaded"),this.load(""))}setTextboxValue(oi=""){var ci=this.control_input,ui=ci.value!==oi;ui&&(ci.value=oi,Fn(ci,"update"),this.lastValue=oi)}getValue(){return this.is_select_tag&&this.input.hasAttribute("multiple")?this.items:this.items.join(this.settings.delimiter)}setValue(oi,ci){var ui=ci?[]:["change"];$o(this,ui,()=>{this.clear(ci),this.addItems(oi,ci)})}setMaxItems(oi){oi===0&&(oi=null),this.settings.maxItems=oi,this.refreshState()}setActiveItem(oi,ci){var ui=this,gi,bi,yi,Ci,Oi,Di;if(ui.settings.mode!=="single"){if(!oi){ui.clearActiveItems(),ui.isFocused&&ui.inputState();return}if(gi=ci&&ci.type.toLowerCase(),gi==="click"&&pi("shiftKey",ci)&&ui.activeItems.length){for(Di=ui.getLastActive(),yi=Array.prototype.indexOf.call(ui.control.children,Di),Ci=Array.prototype.indexOf.call(ui.control.children,oi),yi>Ci&&(Oi=yi,yi=Ci,Ci=Oi),bi=yi;bi<=Ci;bi++)oi=ui.control.children[bi],ui.activeItems.indexOf(oi)===-1&&ui.setActiveItemClass(oi);Dn(ci)}else gi==="click"&&pi(Ho,ci)||gi==="keydown"&&pi("shiftKey",ci)?oi.classList.contains("active")?ui.removeActiveItem(oi):ui.setActiveItemClass(oi):(ui.clearActiveItems(),ui.setActiveItemClass(oi));ui.inputState(),ui.isFocused||ui.focus()}}setActiveItemClass(oi){let ci=this,ui=ci.control.querySelector(".last-active");ui&&Qn(ui,"last-active"),Gn(oi,"active last-active"),ci.trigger("item_select",oi),ci.activeItems.indexOf(oi)==-1&&ci.activeItems.push(oi)}removeActiveItem(oi){var ci=this.activeItems.indexOf(oi);this.activeItems.splice(ci,1),Qn(oi,"active")}clearActiveItems(){Qn(this.activeItems,"active"),this.activeItems=[]}setActiveOption(oi,ci=!0){oi!==this.activeOption&&(this.clearActiveOption(),oi&&(this.activeOption=oi,yn(this.focus_node,{"aria-activedescendant":oi.getAttribute("id")}),yn(oi,{"aria-selected":"true"}),Gn(oi,"active"),ci&&this.scrollToOption(oi)))}scrollToOption(oi,ci){if(!oi)return;let ui=this.dropdown_content,gi=ui.clientHeight,bi=ui.scrollTop||0,yi=oi.offsetHeight,Ci=oi.getBoundingClientRect().top-ui.getBoundingClientRect().top+bi;Ci+yi>gi+bi?this.scroll(Ci-gi+yi,ci):Ci{oi.setActiveItemClass(ui)}))}inputState(){var oi=this;oi.control.contains(oi.control_input)&&(yn(oi.control_input,{placeholder:oi.settings.placeholder}),oi.activeItems.length>0||!oi.isFocused&&oi.settings.hidePlaceholder&&oi.items.length>0?(oi.setTextboxValue(),oi.isInputHidden=!0):(oi.settings.hidePlaceholder&&oi.items.length>0&&yn(oi.control_input,{placeholder:""}),oi.isInputHidden=!1),oi.wrapper.classList.toggle("input-hidden",oi.isInputHidden))}inputValue(){return this.control_input.value.trim()}focus(){var oi=this;oi.isDisabled||oi.isReadOnly||(oi.ignoreFocus=!0,oi.control_input.offsetWidth?oi.control_input.focus():oi.focus_node.focus(),setTimeout(()=>{oi.ignoreFocus=!1,oi.onFocus()},0))}blur(){this.focus_node.blur(),this.onBlur()}getScoreFunction(oi){return this.sifter.getScoreFunction(oi,this.getSearchOptions())}getSearchOptions(){var oi=this.settings,ci=oi.sortField;return typeof oi.sortField=="string"&&(ci=[{field:oi.sortField}]),{fields:oi.searchField,conjunction:oi.searchConjunction,sort:ci,nesting:oi.nesting}}search(oi){var ci,ui,gi=this,bi=this.getSearchOptions();if(gi.settings.score&&(ui=gi.settings.score.call(gi,oi),typeof ui!="function"))throw new Error('Tom Select "score" setting must be a function that returns a function');return oi!==gi.lastQuery?(gi.lastQuery=oi,ci=gi.sifter.search(oi,Object.assign(bi,{score:ui})),gi.currentResults=ci):ci=Object.assign({},gi.currentResults),gi.settings.hideSelected&&(ci.items=ci.items.filter(yi=>{let Ci=wn(yi.id);return!(Ci&&gi.items.indexOf(Ci)!==-1)})),ci}refreshOptions(oi=!0){var ci,ui,gi,bi,yi,Ci,Oi,Di,Wi,Bi;let Zi={},In=[];var Qi=this,Cn=Qi.inputValue();let Pn=Cn===Qi.lastQuery||Cn==""&&Qi.lastQuery==null;var kn=Qi.search(Cn),dn=null,jn=Qi.settings.shouldOpen||!1,Os=Qi.dropdown_content;Pn&&(dn=Qi.activeOption,dn&&(Wi=dn.closest("[data-group]"))),bi=kn.items.length,typeof Qi.settings.maxOptions=="number"&&(bi=Math.min(bi,Qi.settings.maxOptions)),bi>0&&(jn=!0);let Va=(xn,Ln)=>{let En=Zi[xn];if(En!==void 0){let Nn=In[En];if(Nn!==void 0)return[En,Nn.fragment]}let Jr=document.createDocumentFragment();return En=In.length,In.push({fragment:Jr,order:Ln,optgroup:xn}),[En,Jr]};for(ci=0;ci0&&(Nn=Nn.cloneNode(!0),yn(Nn,{id:En.$id+"-clone-"+ui,"aria-selected":null}),Nn.classList.add("ts-cloned"),Qn(Nn,"active"),Qi.activeOption&&Qi.activeOption.dataset.value==Ln&&Wi&&Wi.dataset.group===yi.toString()&&(dn=Nn)),rh.appendChild(Nn),yi!=""&&(Zi[yi]=nh)}}Qi.settings.lockOptgroupOrder&&In.sort((xn,Ln)=>xn.order-Ln.order),Oi=document.createDocumentFragment(),Li(In,xn=>{let Ln=xn.fragment,En=xn.optgroup;if(!Ln||!Ln.children.length)return;let Jr=Qi.optgroups[En];if(Jr!==void 0){let Nn=document.createDocumentFragment(),qa=Qi.render("optgroup_header",Jr);Si(Nn,qa),Si(Nn,Ln);let Wa=Qi.render("optgroup",{group:Jr,options:Nn});Si(Oi,Wa)}else Si(Oi,Ln)}),Os.innerHTML="",Si(Os,Oi),Qi.settings.highlight&&(Ws(Os),kn.query.length&&kn.tokens.length&&Li(kn.tokens,xn=>{as(Os,xn.regex)}));var Bo=xn=>{let Ln=Qi.render(xn,{input:Cn});return Ln&&(jn=!0,Os.insertBefore(Ln,Os.firstChild)),Ln};if(Qi.loading?Bo("loading"):Qi.settings.shouldLoad.call(Qi,Cn)?kn.items.length===0&&Bo("no_results"):Bo("not_loading"),Di=Qi.canCreate(Cn),Di&&(Bi=Bo("option_create")),Qi.hasOptions=kn.items.length>0||Di,jn){if(kn.items.length>0){if(!dn&&Qi.settings.mode==="single"&&Qi.items[0]!=null&&(dn=Qi.getOption(Qi.items[0])),!Os.contains(dn)){let xn=0;Bi&&!Qi.settings.addPrecedence&&(xn=1),dn=Qi.selectable()[xn]}}else Bi&&(dn=Bi);oi&&!Qi.isOpen&&(Qi.open(),Qi.scrollToOption(dn,"auto")),Qi.setActiveOption(dn)}else Qi.clearActiveOption(),oi&&Qi.isOpen&&Qi.close(!1)}selectable(){return this.dropdown_content.querySelectorAll("[data-selectable]")}addOption(oi,ci=!1){let ui=this;if(Array.isArray(oi))return ui.addOptions(oi,ci),!1;let gi=wn(oi[ui.settings.valueField]);return gi===null||ui.options.hasOwnProperty(gi)?!1:(oi.$order=oi.$order||++ui.order,oi.$id=ui.inputId+"-opt-"+oi.$order,ui.options[gi]=oi,ui.lastQuery=null,ci&&(ui.userOptions[gi]=ci,ui.trigger("option_add",gi,oi)),gi)}addOptions(oi,ci=!1){Li(oi,ui=>{this.addOption(ui,ci)})}registerOption(oi){return this.addOption(oi)}registerOptionGroup(oi){var ci=wn(oi[this.settings.optgroupValueField]);return ci===null?!1:(oi.$order=oi.$order||++this.order,this.optgroups[ci]=oi,ci)}addOptionGroup(oi,ci){var ui;ci[this.settings.optgroupValueField]=oi,(ui=this.registerOptionGroup(ci))&&this.trigger("optgroup_add",ui,ci)}removeOptionGroup(oi){this.optgroups.hasOwnProperty(oi)&&(delete this.optgroups[oi],this.clearCache(),this.trigger("optgroup_remove",oi))}clearOptionGroups(){this.optgroups={},this.clearCache(),this.trigger("optgroup_clear")}updateOption(oi,ci){let ui=this;var gi,bi;let yi=wn(oi),Ci=wn(ci[ui.settings.valueField]);if(yi===null)return;let Oi=ui.options[yi];if(Oi==null)return;if(typeof Ci!="string")throw new Error("Value must be set in option data");let Di=ui.getOption(yi),Wi=ui.getItem(yi);if(ci.$order=ci.$order||Oi.$order,delete ui.options[yi],ui.uncacheValue(Ci),ui.options[Ci]=ci,Di){if(ui.dropdown_content.contains(Di)){let Bi=ui._render("option",ci);Kr(Di,Bi),ui.activeOption===Di&&ui.setActiveOption(Bi)}Di.remove()}Wi&&(bi=ui.items.indexOf(yi),bi!==-1&&ui.items.splice(bi,1,Ci),gi=ui._render("item",ci),Wi.classList.contains("active")&&Gn(gi,"active"),Kr(Wi,gi)),ui.lastQuery=null}removeOption(oi,ci){let ui=this;oi=Gs(oi),ui.uncacheValue(oi),delete ui.userOptions[oi],delete ui.options[oi],ui.lastQuery=null,ui.trigger("option_remove",oi),ui.removeItem(oi,ci)}clearOptions(oi){let ci=(oi||this.clearFilter).bind(this);this.loadedSearches={},this.userOptions={},this.clearCache();let ui={};Li(this.options,(gi,bi)=>{ci(gi,bi)&&(ui[bi]=gi)}),this.options=this.sifter.items=ui,this.lastQuery=null,this.trigger("option_clear")}clearFilter(oi,ci){return this.items.indexOf(ci)>=0}getOption(oi,ci=!1){let ui=wn(oi);if(ui===null)return null;let gi=this.options[ui];if(gi!=null){if(gi.$div)return gi.$div;if(ci)return this._render("option",gi)}return null}getAdjacent(oi,ci,ui="option"){var gi=this,bi;if(!oi)return null;ui=="item"?bi=gi.controlChildren():bi=gi.dropdown_content.querySelectorAll("[data-selectable]");for(let yi=0;yi0?bi[yi+1]:bi[yi-1];return null}getItem(oi){if(typeof oi=="object")return oi;var ci=wn(oi);return ci!==null?this.control.querySelector(`[data-value="${wi(ci)}"]`):null}addItems(oi,ci){var ui=this,gi=Array.isArray(oi)?oi:[oi];gi=gi.filter(yi=>ui.items.indexOf(yi)===-1);let bi=gi[gi.length-1];gi.forEach(yi=>{ui.isPending=yi!==bi,ui.addItem(yi,ci)})}addItem(oi,ci){var ui=ci?[]:["change","dropdown_close"];$o(this,ui,()=>{var gi,bi;let yi=this,Ci=yi.settings.mode,Oi=wn(oi);if(!(Oi&&yi.items.indexOf(Oi)!==-1&&(Ci==="single"&&yi.close(),Ci==="single"||!yi.settings.duplicates))&&!(Oi===null||!yi.options.hasOwnProperty(Oi))&&(Ci==="single"&&yi.clear(ci),!(Ci==="multi"&&yi.isFull()))){if(gi=yi._render("item",yi.options[Oi]),yi.control.contains(gi)&&(gi=gi.cloneNode(!0)),bi=yi.isFull(),yi.items.splice(yi.caretPos,0,Oi),yi.insertAtCaret(gi),yi.isSetup){if(!yi.isPending&&yi.settings.hideSelected){let Di=yi.getOption(Oi),Wi=yi.getAdjacent(Di,1);Wi&&yi.setActiveOption(Wi)}!yi.isPending&&!yi.settings.closeAfterSelect&&yi.refreshOptions(yi.isFocused&&Ci!=="single"),yi.settings.closeAfterSelect!=!1&&yi.isFull()?yi.close():yi.isPending||yi.positionDropdown(),yi.trigger("item_add",Oi,gi),yi.isPending||yi.updateOriginalInput({silent:ci})}(!yi.isPending||!bi&&yi.isFull())&&(yi.inputState(),yi.refreshState())}})}removeItem(oi=null,ci){let ui=this;if(oi=ui.getItem(oi),!oi)return;var gi,bi;let yi=oi.dataset.value;gi=qn(oi),oi.remove(),oi.classList.contains("active")&&(bi=ui.activeItems.indexOf(oi),ui.activeItems.splice(bi,1),Qn(oi,"active")),ui.items.splice(gi,1),ui.lastQuery=null,!ui.settings.persist&&ui.userOptions.hasOwnProperty(yi)&&ui.removeOption(yi,ci),gi{}){arguments.length===3&&(ci=arguments[2]),typeof ci!="function"&&(ci=()=>{});var ui=this,gi=ui.caretPos,bi;if(oi=oi||ui.inputValue(),!ui.canCreate(oi))return ci(),!1;ui.lock();var yi=!1,Ci=Oi=>{if(ui.unlock(),!Oi||typeof Oi!="object")return ci();var Di=wn(Oi[ui.settings.valueField]);if(typeof Di!="string")return ci();ui.setTextboxValue(),ui.addOption(Oi,!0),ui.setCaret(gi),ui.addItem(Di),ci(Oi),yi=!0};return typeof ui.settings.create=="function"?bi=ui.settings.create.call(this,oi,Ci):bi={[ui.settings.labelField]:oi,[ui.settings.valueField]:oi},yi||Ci(bi),!0}refreshItems(){var oi=this;oi.lastQuery=null,oi.isSetup&&oi.addItems(oi.items),oi.updateOriginalInput(),oi.refreshState()}refreshState(){let oi=this;oi.refreshValidityState();let ci=oi.isFull(),ui=oi.isLocked;oi.wrapper.classList.toggle("rtl",oi.rtl);let gi=oi.wrapper.classList;gi.toggle("focus",oi.isFocused),gi.toggle("disabled",oi.isDisabled),gi.toggle("readonly",oi.isReadOnly),gi.toggle("required",oi.isRequired),gi.toggle("invalid",!oi.isValid),gi.toggle("locked",ui),gi.toggle("full",ci),gi.toggle("input-active",oi.isFocused&&!oi.isInputHidden),gi.toggle("dropdown-active",oi.isOpen),gi.toggle("has-options",qs(oi.options)),gi.toggle("has-items",oi.items.length>0)}refreshValidityState(){var oi=this;oi.input.validity&&(oi.isValid=oi.input.validity.valid,oi.isInvalid=!oi.isValid)}isFull(){return this.settings.maxItems!==null&&this.items.length>=this.settings.maxItems}updateOriginalInput(oi={}){let ci=this;var ui,gi;let bi=ci.input.querySelector('option[value=""]');if(ci.is_select_tag){let Oi=function(Di,Wi,Bi){return Di||(Di=Ii('")),Di!=bi&&ci.input.append(Di),yi.push(Di),(Di!=bi||Ci>0)&&(Di.selected=!0),Di},yi=[],Ci=ci.input.querySelectorAll("option:checked").length;ci.input.querySelectorAll("option:checked").forEach(Di=>{Di.selected=!1}),ci.items.length==0&&ci.settings.mode=="single"?Oi(bi,"",""):ci.items.forEach(Di=>{if(ui=ci.options[Di],gi=ui[ci.settings.labelField]||"",yi.includes(ui.$option)){let Wi=ci.input.querySelector(`option[value="${wi(Di)}"]:not(:checked)`);Oi(Wi,Di,gi)}else ui.$option=Oi(ui.$option,Di,gi)})}else ci.input.value=ci.getValue();ci.isSetup&&(oi.silent||ci.trigger("change",ci.getValue()))}open(){var oi=this;oi.isLocked||oi.isOpen||oi.settings.mode==="multi"&&oi.isFull()||(oi.isOpen=!0,yn(oi.focus_node,{"aria-expanded":"true"}),oi.refreshState(),Bn(oi.dropdown,{visibility:"hidden",display:"block"}),oi.positionDropdown(),Bn(oi.dropdown,{visibility:"visible",display:"block"}),oi.focus(),oi.trigger("dropdown_open",oi.dropdown))}close(oi=!0){var ci=this,ui=ci.isOpen;oi&&(ci.setTextboxValue(),ci.settings.mode==="single"&&ci.items.length&&ci.inputState()),ci.isOpen=!1,yn(ci.focus_node,{"aria-expanded":"false"}),Bn(ci.dropdown,{display:"none"}),ci.settings.hideSelected&&ci.clearActiveOption(),ci.refreshState(),ui&&ci.trigger("dropdown_close",ci.dropdown)}positionDropdown(){if(this.settings.dropdownParent==="body"){var oi=this.control,ci=oi.getBoundingClientRect(),ui=oi.offsetHeight+ci.top+window.scrollY,gi=ci.left+window.scrollX;Bn(this.dropdown,{width:ci.width+"px",top:ui+"px",left:gi+"px"})}}clear(oi){var ci=this;if(ci.items.length){var ui=ci.controlChildren();Li(ui,gi=>{ci.removeItem(gi,!0)}),ci.inputState(),oi||ci.updateOriginalInput(),ci.trigger("clear")}}insertAtCaret(oi){let ci=this,ui=ci.caretPos,gi=ci.control;gi.insertBefore(oi,gi.children[ui]||null),ci.setCaret(ui+1)}deleteSelection(oi){var ci,ui,gi,bi,yi=this;ci=oi&&oi.keyCode===ha?-1:1,ui=Cs(yi.control_input);let Ci=[];if(yi.activeItems.length)bi=ms(yi.activeItems,ci),gi=qn(bi),ci>0&&gi++,Li(yi.activeItems,Oi=>Ci.push(Oi));else if((yi.isFocused||yi.settings.mode==="single")&&yi.items.length){let Oi=yi.controlChildren(),Di;ci<0&&ui.start===0&&ui.length===0?Di=Oi[yi.caretPos-1]:ci>0&&ui.start===yi.inputValue().length&&(Di=Oi[yi.caretPos]),Di!==void 0&&Ci.push(Di)}if(!yi.shouldDelete(Ci,oi))return!1;for(Dn(oi,!0),typeof gi!="undefined"&&yi.setCaret(gi);Ci.length;)yi.removeItem(Ci.pop());return yi.inputState(),yi.positionDropdown(),yi.refreshOptions(!1),!0}shouldDelete(oi,ci){let ui=oi.map(gi=>gi.dataset.value);return!(!ui.length||typeof this.settings.onDelete=="function"&&this.settings.onDelete(ui,ci)===!1)}advanceSelection(oi,ci){var ui,gi,bi=this;bi.rtl&&(oi*=-1),!bi.inputValue().length&&(pi(Ho,ci)||pi("shiftKey",ci)?(ui=bi.getLastActive(oi),ui?ui.classList.contains("active")?gi=bi.getAdjacent(ui,oi,"item"):gi=ui:oi>0?gi=bi.control_input.nextElementSibling:gi=bi.control_input.previousElementSibling,gi&&(gi.classList.contains("active")&&bi.removeActiveItem(ui),bi.setActiveItemClass(gi))):bi.moveCaret(oi))}moveCaret(oi){}getLastActive(oi){let ci=this.control.querySelector(".last-active");if(ci)return ci;var ui=this.control.querySelectorAll(".active");if(ui)return ms(ui,oi)}setCaret(oi){this.caretPos=this.items.length}controlChildren(){return Array.from(this.control.querySelectorAll("[data-ts-item]"))}lock(){this.setLocked(!0)}unlock(){this.setLocked(!1)}setLocked(oi=this.isReadOnly||this.isDisabled){this.isLocked=oi,this.refreshState()}disable(){this.setDisabled(!0),this.close()}enable(){this.setDisabled(!1)}setDisabled(oi){this.focus_node.tabIndex=oi?-1:this.tabIndex,this.isDisabled=oi,this.input.disabled=oi,this.control_input.disabled=oi,this.setLocked()}setReadOnly(oi){this.isReadOnly=oi,this.input.readOnly=oi,this.control_input.readOnly=oi,this.setLocked()}destroy(){var oi=this,ci=oi.revertSettings;oi.trigger("destroy"),oi.off(),oi.wrapper.remove(),oi.dropdown.remove(),oi.input.innerHTML=ci.innerHTML,oi.input.tabIndex=ci.tabIndex,Qn(oi.input,"tomselected","ts-hidden-accessible"),oi._destroy(),delete oi.input.tomselect}render(oi,ci){var ui,gi;let bi=this;if(typeof this.settings.render[oi]!="function"||(gi=bi.settings.render[oi].call(this,ci,Ks),!gi))return null;if(gi=Ii(gi),oi==="option"||oi==="option_create"?ci[bi.settings.disabledField]?yn(gi,{"aria-disabled":"true"}):yn(gi,{"data-selectable":""}):oi==="optgroup"&&(ui=ci.group[bi.settings.optgroupValueField],yn(gi,{"data-group":ui}),ci.group[bi.settings.disabledField]&&yn(gi,{"data-disabled":""})),oi==="option"||oi==="item"){let yi=Gs(ci[bi.settings.valueField]);yn(gi,{"data-value":yi}),oi==="item"?(Gn(gi,bi.settings.itemClass),yn(gi,{"data-ts-item":""})):(Gn(gi,bi.settings.optionClass),yn(gi,{role:"option",id:ci.$id}),ci.$div=gi,bi.options[yi]=ci)}return gi}_render(oi,ci){let ui=this.render(oi,ci);if(ui==null)throw"HTMLElement expected";return ui}clearCache(){Li(this.options,oi=>{oi.$div&&(oi.$div.remove(),delete oi.$div)})}uncacheValue(oi){let ci=this.getOption(oi);ci&&ci.remove()}canCreate(oi){return this.settings.create&&oi.length>0&&this.settings.createFilter.call(this,oi)}hook(oi,ci,ui){var gi=this,bi=gi[ci];gi[ci]=function(){var yi,Ci;return oi==="after"&&(yi=bi.apply(gi,arguments)),Ci=ui.apply(gi,arguments),oi==="instead"?Ci:(oi==="before"&&(yi=bi.apply(gi,arguments)),yi)}}}function Xi(){On(this.input,"change",()=>{this.sync()})}function _n(fi){var oi=this,ci=oi.onOptionSelect;oi.settings.hideSelected=!1;let ui=Object.assign({className:"tomselect-checkbox",checkedClassNames:void 0,uncheckedClassNames:void 0},fi);var gi=function(Ci,Oi){Oi?(Ci.checked=!0,ui.uncheckedClassNames&&Ci.classList.remove(...ui.uncheckedClassNames),ui.checkedClassNames&&Ci.classList.add(...ui.checkedClassNames)):(Ci.checked=!1,ui.checkedClassNames&&Ci.classList.remove(...ui.checkedClassNames),ui.uncheckedClassNames&&Ci.classList.add(...ui.uncheckedClassNames))},bi=function(Ci){setTimeout(()=>{var Oi=Ci.querySelector("input."+ui.className);Oi instanceof HTMLInputElement&&gi(Oi,Ci.classList.contains("selected"))},1)};oi.hook("after","setupTemplates",()=>{var yi=oi.settings.render.option;oi.settings.render.option=(Ci,Oi)=>{var Di=Ii(yi.call(oi,Ci,Oi)),Wi=document.createElement("input");ui.className&&Wi.classList.add(ui.className),Wi.addEventListener("click",function(Zi){Dn(Zi)}),Wi.type="checkbox";let Bi=wn(Ci[oi.settings.valueField]);return gi(Wi,!!(Bi&&oi.items.indexOf(Bi)>-1)),Di.prepend(Wi),Di}}),oi.on("item_remove",yi=>{var Ci=oi.getOption(yi);Ci&&(Ci.classList.remove("selected"),bi(Ci))}),oi.on("item_add",yi=>{var Ci=oi.getOption(yi);Ci&&bi(Ci)}),oi.hook("instead","onOptionSelect",(yi,Ci)=>{if(Ci.classList.contains("selected")){Ci.classList.remove("selected"),oi.removeItem(Ci.dataset.value),oi.refreshOptions(),Dn(yi,!0);return}ci.call(oi,yi,Ci),bi(Ci)})}function Ki(fi){let oi=this,ci=Object.assign({className:"clear-button",title:"Clear All",html:ui=>`
    `},fi);oi.on("initialize",()=>{var ui=Ii(ci.html(ci));ui.addEventListener("click",gi=>{oi.isLocked||(oi.clear(),oi.settings.mode==="single"&&oi.settings.allowEmptyOption&&oi.addItem(""),gi.preventDefault(),gi.stopPropagation())}),oi.control.appendChild(ui)})}let fn=(fi,oi)=>{var ci;(ci=fi.parentNode)==null||ci.insertBefore(oi,fi.nextSibling)},Mn=(fi,oi)=>{var ci;(ci=fi.parentNode)==null||ci.insertBefore(oi,fi)},gs=(fi,oi)=>{do{var ci;if(oi=(ci=oi)==null?void 0:ci.previousElementSibling,fi==oi)return!0}while(oi&&oi.previousElementSibling);return!1};function is(){var fi=this;if(fi.settings.mode!=="multi")return;var oi=fi.lock,ci=fi.unlock;let ui=!0,gi;fi.hook("after","setupTemplates",()=>{var bi=fi.settings.render.item;fi.settings.render.item=(yi,Ci)=>{let Oi=Ii(bi.call(fi,yi,Ci));yn(Oi,{draggable:"true"});let Di=Cn=>{ui||Dn(Cn),Cn.stopPropagation()},Wi=Cn=>{gi=Oi,setTimeout(()=>{Oi.classList.add("ts-dragging")},0)},Bi=Cn=>{Cn.preventDefault(),Oi.classList.add("ts-drag-over"),In(Oi,gi)},Zi=()=>{Oi.classList.remove("ts-drag-over")},In=(Cn,Pn)=>{Pn!==void 0&&(gs(Pn,Oi)?fn(Cn,Pn):Mn(Cn,Pn))},Qi=()=>{var Cn;document.querySelectorAll(".ts-drag-over").forEach(kn=>kn.classList.remove("ts-drag-over")),(Cn=gi)==null||Cn.classList.remove("ts-dragging"),gi=void 0;var Pn=[];fi.control.querySelectorAll("[data-value]").forEach(kn=>{if(kn.dataset.value){let dn=kn.dataset.value;dn&&Pn.push(dn)}}),fi.setValue(Pn)};return On(Oi,"mousedown",Di),On(Oi,"dragstart",Wi),On(Oi,"dragenter",Bi),On(Oi,"dragover",Bi),On(Oi,"dragleave",Zi),On(Oi,"dragend",Qi),Oi}}),fi.hook("instead","lock",()=>(ui=!1,oi.call(fi))),fi.hook("instead","unlock",()=>(ui=!0,ci.call(fi)))}function mo(fi){let oi=this,ci=Object.assign({title:"Untitled",headerClass:"dropdown-header",titleRowClass:"dropdown-header-title",labelClass:"dropdown-header-label",closeClass:"dropdown-header-close",html:ui=>'
    '+ui.title+'×
    '},fi);oi.on("initialize",()=>{var ui=Ii(ci.html(ci)),gi=ui.querySelector("."+ci.closeClass);gi&&gi.addEventListener("click",bi=>{Dn(bi,!0),oi.close()}),oi.dropdown.insertBefore(ui,oi.dropdown.firstChild)})}function Qs(){var fi=this;fi.hook("instead","setCaret",oi=>{fi.settings.mode==="single"||!fi.control.contains(fi.control_input)?oi=fi.items.length:(oi=Math.max(0,Math.min(fi.items.length,oi)),oi!=fi.caretPos&&!fi.isPending&&fi.controlChildren().forEach((ci,ui)=>{ui{if(!fi.isFocused)return;let ci=fi.getLastActive(oi);if(ci){let ui=qn(ci);fi.setCaret(oi>0?ui+1:ui),fi.setActiveItem(),Qn(ci,"last-active")}else fi.setCaret(fi.caretPos+oi)})}function Gl(){let fi=this;fi.settings.shouldOpen=!0,fi.hook("before","setup",()=>{fi.focus_node=fi.control,Gn(fi.control_input,"dropdown-input");let oi=Ii('