pax_global_header00006660000000000000000000000064146473166270014532gustar00rootroot0000000000000052 comment=faf388556f35837c863f044d2e9de4a424c097eb a2d-2.0.5/000077500000000000000000000000001464731662700122045ustar00rootroot00000000000000a2d-2.0.5/LICENSE000066400000000000000000000515171464731662700132220ustar00rootroot00000000000000License: MIT Copyright: 2024 NGC2023 Files: a2dapp/static/bootstrap/css/cdn_bootstrap_5-3_min.css a2dapp/static/bootstrap/js/cdn_bootstrap_5-3.js Copyright: 2011-2023 The Bootstrap Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License: CC-BY-3.0 Files: a2dapp/static/bootstrap/css/bootstrap_docs_5-3.css a2dapp/static/bootstrap/js/bootstrap_5-3_mode.js Copyright: 2011-2023 The Bootstrap Authors Creative Commons Legal Code . Attribution 3.0 Unported . CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. . License . THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. . BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. . 1. Definitions . a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. c. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. . 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. . 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: . a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, d. to Distribute and Publicly Perform Adaptations. e. For the avoidance of doubt: . i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. . The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. . 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: . a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested. b. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. c. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. . 5. Representations, Warranties and Disclaimer . UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. . 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. . 7. Termination . a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. . 8. Miscellaneous . a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. . Creative Commons Notice . Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. . Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License. . Creative Commons may be contacted at https://creativecommons.org/. Comment We have made modifications to a portion of the Bootstrap code to suit our project's requirements. These minor modifications enables the Color mode toggler from Bootstrap's docs to work with a2d. a2d-2.0.5/README.md000066400000000000000000000070601464731662700134660ustar00rootroot00000000000000# a2d - APRS to DAPNET portal a2d is an application for transmitting APRS messages to DAPNET pagers, dedicated to the HAM radio community usage. ## Description a2d utilizes the APRS API to retrieve APRS messages for a callsign and relays them to DAPNET for delivery to your pager device. It supports multiple SSIDs. ## Prerequisites and Installation 1. **Ham Radio License:** Ensure you hold a valid HAM radio license with a callsign. Transmitting signals complies with license regulations and local laws. 2. **APRS API Key:** Register on aprs.fi with your callsign to generate a confidential API Key for downloading APRS messages. Keep this key private. 3. **DAPNET User and Password:** Create a secure account on hampager.de for DAPNET. Additional steps may be required if you don't have an approved DAPNET pager or transmitter. 4. **Debian System with Internet Connection**: For optimal performance and convenience in HAM Radio applications, especially if you prefer a compact, standalone setup with internet access, we recommend using a Raspberry Pi. The Raspberry Pi offers a cost-effective solution that's well-suited for these purposes. **Compatibility** - **Debian 12**: a2d has been thoroughly tested on Debian 12. - **Debian 12 (VMware)**: Tested on Debian 12 within a VMware environment. - **Raspberry Pi OS with Debian 12 (bookworm)**: Tested on Raspberry Pi OS with Debian version 12 (bookworm). You can utilize various packages like VNC or SSH to set up your Raspberry Pi even if you intend to run it headlessly (without a physical display). This approach provides flexibility while maintaining a small footprint, making it a versatile choice for HAM Radio enthusiasts. **Installation:** You can install a2d from GitHub Packages. Download the Debian package from [a2d GitHub Releases](https://github.com/NGC2023/a2d/releases). Navigate to the directory where you downloaded the Debian package using the command line and run the following command: `sudo apt install -y ./.deb` `sudo apt install -y ./.deb` Replace a2d_package_version.deb with the a2d file name you downloaded before running this command. To enable access to a2d over the network from another system, it is suggested to install additional packages like nginx and certbot. While the a2d interface supports any reverse proxy and HTTP server, it provides options for limited management of nginx. However, please note that nginx is not installed by default. If you choose to use nginx, you will need to install and can configure via a2d. To create and maintain CA SSL certificates, certbot is required. certbot is not installed by default but is essential for generating CA SSL certificates and managing them automatically. The a2d interface works with certbot to handle SSLs related to a2d. Install nginx and certbot: `sudo apt update` `sudo apt install ` To **uninstall a2d**, follow these steps. For a thorough removal of user configuration files, it is advisable to uninstall the application after performing a Factory Reset in the a2d portal (Check Resetting a2d portal section). This ensures a clean removal of user-specific settings. `sudo apt purge a2d` However, please note that this command won't remove the core nginx server and other dependencies that were installed alongside a2d. ## Configuration and Usage a2d is designed with a web UI. For detailed documentation on how to use and configure a2d, please visit our [GitHub Pages](https://ngc2023.github.io/a2d/) or access the locally installed documentation through the `a2d-doc` deb package. a2d-2.0.5/a2d/000077500000000000000000000000001464731662700126525ustar00rootroot00000000000000a2d-2.0.5/a2d/__init__.py000066400000000000000000000000201464731662700147530ustar00rootroot00000000000000#Python package a2d-2.0.5/a2d/a2d_utils/000077500000000000000000000000001464731662700145405ustar00rootroot00000000000000a2d-2.0.5/a2d/a2d_utils/__init__.py000066400000000000000000000000201464731662700166410ustar00rootroot00000000000000#Python package a2d-2.0.5/a2d/a2d_utils/a2d_utils.py000066400000000000000000000031631464731662700170030ustar00rootroot00000000000000import sqlite3 import configparser #db utils def establish_connection(db_file): # Establish connection to the SQLite database conn = sqlite3.connect(db_file) cursor = conn.cursor() return conn, cursor def retrieve_data_from_table(cursor, table_name): # Retrieve data from the specified table cursor.execute(f'SELECT * FROM {table_name}') rows = cursor.fetchall() return rows def create_table_if_not_exists(cursor, table_name, columns): # Create a table if it doesn't exist in the database create_table_query = f'CREATE TABLE IF NOT EXISTS {table_name} {columns}' cursor.execute(create_table_query) def message_id_exists(cursor, table_name, message_id): # Check if the message ID exists in the specified table cursor.execute(f'SELECT messageid FROM {table_name} WHERE messageid = ?', (message_id,)) result = cursor.fetchone() return bool(result) #Cron util def remove_cronjob(): command = '/usr/bin/python3 -m a2d.runscripts' lines = [] try: with open('/etc/cron.d/a2d', 'r') as cron_file: lines = cron_file.readlines() except FileNotFoundError: pass filtered_lines = [line for line in lines if not line.strip().endswith(command)] with open('/etc/cron.d/a2d', 'w') as cron_file: cron_file.writelines(filtered_lines) #Ini read/write utils def read_ini_data(ini_file): config = configparser.ConfigParser() config.optionxform = lambda option: option config.read(ini_file) return config def write_ini_data(config, ini_file): with open(ini_file, 'w') as configfile: config.write(configfile) a2d-2.0.5/a2d/a2d_utils/decr_usrinfo.py000066400000000000000000000012671464731662700176020ustar00rootroot00000000000000from cryptography.fernet import Fernet def retrieve_usrinfo(): key_file = '/etc/a2d/.keys/a2d_AnDinfo.key' encrypted_file = '/etc/a2d/.creds/a2d_AnDinfo.conf' with open(key_file, 'rb') as file: key = file.read() cipher_suite = Fernet(key) with open(encrypted_file, 'rb') as file: encrypted_data = file.read() decrypted_data = cipher_suite.decrypt(encrypted_data).decode('utf-8') # Parse the decrypted data as needed data_dict = {} for line in decrypted_data.split('\n'): line = line.strip() if line and ' = ' in line: key, value = line.split(' = ', 1) data_dict[key] = value return data_dict a2d-2.0.5/a2d/a2d_utils/get_aprs.py000066400000000000000000000020111464731662700167100ustar00rootroot00000000000000from a2d.a2d_utils.decr_usrinfo import retrieve_usrinfo import requests data = retrieve_usrinfo() APRSAPI_KEY = data['aprsapi_key'] callsign_nossid = data['callsign_nossid'] def get_aprs(c): if c[2] == '0': url = "https://api.aprs.fi/api/get?what=msg&dst=" + c[1] + "&apikey="+APRSAPI_KEY+"&format=json" trgcall = c[2] else: url = "https://api.aprs.fi/api/get?what=msg&dst=" + c[1] + "-" + c[2] + "&apikey="+APRSAPI_KEY+"&format=json" trgcall = c[2] aprs = requests.get(url) aprs = aprs.json() try: #If ARPS server returns "Get failed, query ratelimit" (because of too frequent requests) entries = aprs['entries'] except KeyError: return None return entries, trgcall def aprs_check(): url = "https://api.aprs.fi/api/get?what=msg&dst="+callsign_nossid+"&apikey="+APRSAPI_KEY+"&format=json" try: aprs_check = requests.get(url) entries_check = aprs_check.json() except KeyError: return None return entries_check a2d-2.0.5/a2d/a2d_utils/post_dapnet.py000066400000000000000000000034711464731662700174370ustar00rootroot00000000000000from requests.auth import HTTPBasicAuth import requests from a2d.a2d_utils.decr_usrinfo import retrieve_usrinfo from a2d.a2d_utils.a2d_utils import establish_connection, retrieve_data_from_table, remove_cronjob, read_ini_data, write_ini_data # Retrieve values from the retrieve_usrinfo function data = retrieve_usrinfo() DAPNET_USER = data['dapnet_user'] DAPNET_PASS = data['dapnet_pass'] #File and Directories data_dir = f'/var/lib/a2d/dbs' # Establish connection to the SQLite database ham_info_db = f'{data_dir}/ham_info.db' conn, cursor = establish_connection(ham_info_db) row = retrieve_data_from_table(cursor, 'ham_info') conn.close() # DAPNET function def dapnet(new_msg_str): login = DAPNET_USER passwd = DAPNET_PASS text = new_msg_str callsign = [row[0][1]] url = 'http://www.hampager.de:8080/calls' txgroup = row[0][2] # DAPNET push functions def push_to_dapnet(text, callsign, login, passwd, url, txgroup): payload = { "text": text, "callSignNames": callsign, "transmitterGroupNames": [txgroup], "emergency": False } headers = { "Content-Type": "application/json" } auth = HTTPBasicAuth(login, passwd) response = requests.post(url, json=payload, headers=headers, auth=auth) return response.status_code # DAPNET send status_code = push_to_dapnet(text, callsign, login, passwd, url, txgroup) # Update the configuration file with the status code dapnet_data = '/etc/a2d/dapnet_data.ini' config = read_ini_data(dapnet_data) status_code_str = str(status_code) config.set('DAPNETVerify', 'HTTPStatus', status_code_str) write_ini_data(config, dapnet_data) #Stop cron if the status code is not 201 if status_code_str != '201': remove_cronjob() a2d-2.0.5/a2d/a2d_utils/pseudo_aprs_utils.py000066400000000000000000000034711464731662700206630ustar00rootroot00000000000000from a2d.a2d_utils.a2d_utils import message_id_exists, establish_connection from a2d.a2d_utils.get_aprs import get_aprs import multiprocessing def process_aprs_data(rows, table_name): # SQLite database aprs_messages_db = f'/var/lib/a2d/dbs/{table_name}.db' conn, cursor = establish_connection(aprs_messages_db) data_to_insert = [] for c in rows: result = get_aprs(c) if result is None: continue entries, trgcall = result if not entries: continue for item in entries: messageid = item['messageid'] srccall = item['srccall'] message = item['message'] # Check if messageid already exists in the SQLite table if not message_id_exists(cursor, table_name, messageid): data_to_insert.append((messageid, srccall, message, trgcall)) # Close the connection to the database before creating the table conn.close() # Re-establish connection to the database to create the table and insert data conn, cursor = establish_connection(aprs_messages_db) # Batch insert the data into the SQLite table cursor.executemany(f'INSERT INTO {table_name} VALUES (?, ?, ?, ?)', data_to_insert) # Commit the changes and close the connection conn.commit() conn.close() def process_aprs_data_parallel(rows, table_name): num_processes = max(1, min(multiprocessing.cpu_count(), len(rows))) chunk_size = max(1, len(rows) // num_processes) chunks = [rows[i:i + chunk_size] for i in range(0, len(rows), chunk_size)] processes = [] for chunk in chunks: process = multiprocessing.Process(target=process_aprs_data, args=(chunk, table_name)) processes.append(process) process.start() for process in processes: process.join() a2d-2.0.5/a2d/app.py000066400000000000000000000063401464731662700140070ustar00rootroot00000000000000import os from flask import Flask, render_template, request, session, redirect, jsonify, send_file from a2d.routes.auth import auth_routes from a2d.routes.run import run_routes from a2d.routes.dns import dns_routes from a2d.routes.system import system_routes from a2d.routes.network import network_routes from a2d.routes.config import config_routes from a2d.routes.reset import reset_routes from a2d.routes.data import data_routes, get_adv_conf_values from a2d.routes.docs import docs_routes from a2d.modals.creds import get_credentials app = Flask(__name__) app.config['APP_VERSION'] = '2.0.5' #Storing session key implemented to avoid nginx error SESSION_KEY_FILE = "/etc/a2d/.keys/session_key.bin" SESSION_KEY_DIR = os.path.dirname(SESSION_KEY_FILE) def generate_and_store_session_key(): if not os.path.exists(SESSION_KEY_DIR): try: os.makedirs(SESSION_KEY_DIR) except OSError as e: pass if os.path.exists(SESSION_KEY_DIR): session_key = os.urandom(32) with open(SESSION_KEY_FILE, "wb") as key_file: key_file.write(session_key) else: pass def load_session_key(): try: with open(SESSION_KEY_FILE, "rb") as key_file: return key_file.read() except FileNotFoundError: return None def set_session_key(): session_key = load_session_key() if session_key is None: generate_and_store_session_key() session_key = load_session_key() return session_key app.secret_key = set_session_key() # Register the auth_routes blueprint app.register_blueprint(auth_routes) app.register_blueprint(run_routes) app.register_blueprint(config_routes) app.register_blueprint(data_routes) app.register_blueprint(dns_routes) app.register_blueprint(system_routes) app.register_blueprint(network_routes) app.register_blueprint(reset_routes) app.register_blueprint(docs_routes) @app.route('/') def home(): if 'pin' in session: creds = get_credentials() active = 'Dashboard' service_status = request.args.get('service_status') last_run_human = request.args.get('last_run_human') run_interval = request.args.get('run_interval') callsign = request.args.get('callsign') callsign2 = request.args.get('callsign2') all_files_exist = request.args.get('allFilesExist') intervalmin, intervalh, ssids, logcounts = get_adv_conf_values() return render_template( 'index.html', creds=creds, active=active, intervalmin=intervalmin, intervalh=intervalh, ssids=ssids, logcounts=logcounts, service_status=service_status, last_run_human=last_run_human, run_interval=run_interval, callsign=callsign, callsign2=callsign2, all_files_exist=all_files_exist ) else: return redirect('/login') @app.route('/version', methods=['GET']) def get_version(): return jsonify({'version': app.config['APP_VERSION']}) @app.route('/image') def get_image(): image_path = '/usr/share/images/logo_a2d.png' return send_file(image_path, as_attachment=True) if __name__ == '__main__': app.run(host='127.0.0.1', port=9333) a2d-2.0.5/a2d/build_usrdbs.py000066400000000000000000000072101464731662700157050ustar00rootroot00000000000000#coding=utf-8 #!/usr/bin/env python3 import configparser import multiprocessing from a2d.a2d_utils.decr_usrinfo import retrieve_usrinfo from a2d.a2d_utils.a2d_utils import establish_connection, create_table_if_not_exists, retrieve_data_from_table def update_previous_values_db(previous_values_db, callsign_nossid, txgroup, condition1, condition2): conn, cursor = establish_connection(previous_values_db) create_table_if_not_exists(cursor, "previous_values", "(id INTEGER PRIMARY KEY, callsign TEXT, txgroup TEXT)") cursor.execute('SELECT callsign, txgroup FROM previous_values WHERE id = 1') row = cursor.fetchone() if row is None or row[0] != callsign_nossid: cursor.execute('REPLACE INTO previous_values (id, callsign, txgroup) VALUES (1, ?, ?)', (callsign_nossid, txgroup)) conn.commit() condition1 = True elif row[1] != txgroup: cursor.execute('REPLACE INTO previous_values (id, callsign, txgroup) VALUES (1, ?, ?)', (callsign_nossid, txgroup)) conn.commit() condition2 = True else: condition1 = False condition2 = False conn.close() return condition1, condition2 def update_callssid_db(callssid_db, callsign_nossid, ssids, condition1): conn, cursor = establish_connection(callssid_db) create_table_if_not_exists(cursor, "callssid", "(id INTEGER PRIMARY KEY, callsign TEXT, ssid TEXT)") callssid_conn, callssid_cursor = establish_connection(callssid_db) rows = retrieve_data_from_table(callssid_cursor, 'callssid') callssid_conn.close() # Get the SSIDs from the callssid database existing_ssids = [row[2] for row in rows if row[1] == callsign_nossid] if condition1: cursor.execute("DELETE FROM callssid") if ssids != existing_ssids: # Compare SSIDs from the database with those in a2d_adv_conf.ini cursor.execute("DELETE FROM callssid WHERE callsign = ?", (callsign_nossid,)) data = [(callsign_nossid, ssid.strip()) for ssid in ssids] cursor.executemany('INSERT OR REPLACE INTO callssid (callsign, ssid) VALUES (?, ?)', data) conn.commit() conn.close() pass def update_ham_info_db(ham_info_db, callsign_nossid, txgroup, condition1, condition2): conn, cursor = establish_connection(ham_info_db) create_table_if_not_exists(cursor, "ham_info", "(id INTEGER PRIMARY KEY, callsign TEXT, txgroup TEXT)") if condition1 or condition2: cursor.execute(''' REPLACE INTO ham_info VALUES (?, ?, ?)''', (1, callsign_nossid, txgroup) ) conn.commit() conn.close() pass if __name__ == "__main__": data = retrieve_usrinfo() callsign_nossid = data['callsign_nossid'] txgroup = data['txgroup'] config = configparser.ConfigParser() config.read("/etc/a2d/a2d_adv_conf.ini") preferred_ssids = config.get("SSID", "PreferredSSIDs", fallback="") ssids = preferred_ssids.split(",") condition1 = False condition2 = False data_dir = '/var/lib/a2d/dbs' previous_values_db = f'{data_dir}/previous_values.db' callssid_db = f'{data_dir}/callssid.db' ham_info_db = f'{data_dir}/ham_info.db' # Call the database update functions condition1, condition2 = update_previous_values_db(previous_values_db, callsign_nossid, txgroup, condition1, condition2) num_processes = multiprocessing.cpu_count() pool = multiprocessing.Pool(processes=num_processes) result1 = pool.apply_async(update_callssid_db, (callssid_db, callsign_nossid, ssids, condition1)) result2 = pool.apply_async(update_ham_info_db, (ham_info_db, callsign_nossid, txgroup, condition1, condition2)) pool.close() pool.join() a2d-2.0.5/a2d/gunicorn_config.py000066400000000000000000000005631464731662700164010ustar00rootroot00000000000000#coding=utf-8 #!/usr/bin/env python3 bind = '127.0.0.1:9333' workers = 3 #For 2 core threads = 2 #Per worker optimum for 2 core and 512MB memory max_requests = 1000 #Restart worker after 1000 requests timeout = 30 max_requests_jitter = 100 #Restart connection after 100 req errorlog = '/var/log/a2d_gu_error.log' loglevel = 'error' logrotate = 1000000 #rotate after 1MB a2d-2.0.5/a2d/modals/000077500000000000000000000000001464731662700141315ustar00rootroot00000000000000a2d-2.0.5/a2d/modals/__init__.py000066400000000000000000000000201464731662700162320ustar00rootroot00000000000000#Python package a2d-2.0.5/a2d/modals/creds.py000066400000000000000000000045171464731662700156120ustar00rootroot00000000000000import os import configparser from cryptography.fernet import Fernet import io encryption_key = '/etc/a2d/.keys/a2d_AnDinfo.key' a2d_config_file = '/etc/a2d/.creds/a2d_AnDinfo.conf' def generate_encryption_key(): if not os.path.exists(encryption_key): key = Fernet.generate_key() with open(encryption_key, 'wb') as key_file: key_file.write(key) # Change permissions to 400 os.chmod(encryption_key, 0o400) return encryption_key def encrypt_config(config): generate_encryption_key() # Ensure encryption key file exists with open(encryption_key, 'rb') as keyfile: key = keyfile.read() cipher = Fernet(key) config_buffer = io.StringIO() config.write(config_buffer) serialized_config = config_buffer.getvalue().encode() encrypted_config = cipher.encrypt(serialized_config) return encrypted_config def decrypt_config(config_path): with open(config_path, 'rb') as configfile: encrypted_config = configfile.read() with open(encryption_key, 'rb') as keyfile: key = keyfile.read() cipher = Fernet(key) decrypted_config = cipher.decrypt(encrypted_config) config = configparser.ConfigParser() config.read_string(decrypted_config.decode()) return config def save_credentials(callsign, txgroup, dapnetuser, dapnetpass, aprsapi): config = configparser.ConfigParser() config['DEFAULT'] = { 'callsign_nossid': callsign, 'txgroup': txgroup, 'DAPNET_USER': dapnetuser, 'DAPNET_PASS': dapnetpass, 'APRSAPI_KEY': aprsapi } encrypted_config = encrypt_config(config) with open(a2d_config_file, 'wb') as configfile: configfile.write(encrypted_config) def get_credentials(): config_path = a2d_config_file if os.path.isfile(config_path): config = decrypt_config(config_path) callsign = config.get('DEFAULT', 'callsign_nossid', fallback='') txgroup = config.get('DEFAULT', 'txgroup', fallback='') dapnetuser = config.get('DEFAULT', 'DAPNET_USER', fallback='') dapnetpass = config.get('DEFAULT', 'DAPNET_PASS', fallback='') return { 'callsign': callsign, 'txgroup': txgroup, 'dapnetuser': dapnetuser, 'dapnetpass': dapnetpass, } else: return {'message': 'Input credentials.'} a2d-2.0.5/a2d/modals/user.py000066400000000000000000000053051464731662700154640ustar00rootroot00000000000000from cryptography.fernet import Fernet import os encryption_key_file = '/etc/a2d/.keys/app.key' passphrase_key_file = '/etc/a2d/.keys/passphrase.key' user_pin_file = '/etc/a2d/.creds/user_pin.bin' user_passphrase_file = '/etc/a2d/.creds/user_passphrase.bin' cipher = None def generate_encryption_key(): encryption_key = Fernet.generate_key() os.makedirs(os.path.dirname(encryption_key_file), exist_ok=True) with open(encryption_key_file, 'wb') as key_file: key_file.write(encryption_key) # Change permissions to 400 if os.path.exists(encryption_key_file): os.chmod(encryption_key_file, 0o400) return encryption_key def get_encryption_key(): if os.path.isfile(encryption_key_file): with open(encryption_key_file, 'rb') as key_file: encryption_key = key_file.read() else: encryption_key = generate_encryption_key() return encryption_key def verify_pin(pin): encryption_key = get_encryption_key() cipher = Fernet(encryption_key) with open(user_pin_file, 'rb') as file: encrypted_pin = file.read() decrypted_pin = cipher.decrypt(encrypted_pin).decode() if pin == decrypted_pin: return True return False def create_user(pin): encryption_key = get_encryption_key() os.makedirs(os.path.dirname(user_pin_file), exist_ok=True) cipher = Fernet(encryption_key) encrypted_pin = cipher.encrypt(pin.encode()) with open(user_pin_file, 'wb') as file: file.write(encrypted_pin) def generate_passphrase_key(): encryption_key = Fernet.generate_key() os.makedirs(os.path.dirname(passphrase_key_file), exist_ok=True) with open(passphrase_key_file, 'wb') as key_file: key_file.write(encryption_key) # Change permissions to 400 if os.path.exists(passphrase_key_file): os.chmod(passphrase_key_file, 0o400) return encryption_key def get_passphrase_key(): if os.path.isfile(passphrase_key_file): with open(passphrase_key_file, 'rb') as key_file: encryption_key = key_file.read() else: encryption_key = generate_passphrase_key() return encryption_key def decrypt_user_passphrase(): encryption_key = get_passphrase_key() cipher = Fernet(encryption_key) with open(user_passphrase_file, 'rb') as file: encrypted_passphrase = file.read() decrypted_passphrase = cipher.decrypt(encrypted_passphrase).decode() return decrypted_passphrase def create_user_passphrase(passphrase): encryption_key = get_passphrase_key() cipher = Fernet(encryption_key) encrypted_passphrase = cipher.encrypt(passphrase.encode()) with open(user_passphrase_file, 'wb') as file: file.write(encrypted_passphrase)a2d-2.0.5/a2d/pseudorun.py000066400000000000000000000034721464731662700152560ustar00rootroot00000000000000import os import sys from a2d.a2d_utils.pseudo_aprs_utils import process_aprs_data_parallel from a2d.a2d_utils.a2d_utils import establish_connection, retrieve_data_from_table, create_table_if_not_exists # Database directory data_dir = f'/var/lib/a2d/dbs' def main(): # Get the absolute path of the current script current_dir = os.path.dirname(os.path.abspath(__file__)) # Add the 'utils' directory to the Python module search path if not already present utils_dir = os.path.join(current_dir, 'utils') if utils_dir not in sys.path: sys.path.append(utils_dir) # Establish connection to the APRS messages database aprs_messages_db = f'{data_dir}/pseudo_aprs_messages.db' aprs_conn, aprs_cursor = establish_connection(aprs_messages_db) # Create the table if it doesn't exist create_table_if_not_exists(aprs_cursor, 'pseudo_aprs_messages', '(messageid TEXT PRIMARY KEY, srccall TEXT, message TEXT, trgcall TEXT)') # Retrieve data from the 'callssid' table callssid_db = f'{data_dir}/callssid.db' callssid_conn, callssid_cursor = establish_connection(callssid_db) rows = retrieve_data_from_table(callssid_cursor, 'callssid') callssid_conn.close() # Close the connection to the APRS messages database aprs_conn.close() # Process APRS messages using multiprocessing process_aprs_data_parallel(rows, 'pseudo_aprs_messages') if __name__ == "__main__": main() # Create a SQLite empty aprs database aprs_messages_db = f'{data_dir}/aprs_messages.db' aprs_conn, aprs_cursor = establish_connection(aprs_messages_db) # Create a table to store the data create_table_if_not_exists(aprs_cursor, 'aprs_messages', '(messageid TEXT PRIMARY KEY, srccall TEXT, message TEXT, trgcall TEXT)') # Commit the changes and close the connection aprs_conn.commit() aprs_conn.close() a2d-2.0.5/a2d/push_a2d.py000066400000000000000000000071151464731662700147350ustar00rootroot00000000000000#coding=utf-8 #!/usr/bin/env python3 import multiprocessing from multiprocessing import Manager from a2d.a2d_utils.get_aprs import get_aprs from a2d.a2d_utils.post_dapnet import dapnet from a2d.a2d_utils.a2d_utils import establish_connection, retrieve_data_from_table, create_table_if_not_exists #Database directory data_dir = f'/var/lib/a2d/dbs' # Establish connection to the SQLite databases callssid_db = f'{data_dir}/callssid.db' callssid_conn, callssid_cursor = establish_connection(callssid_db) rows = retrieve_data_from_table(callssid_cursor, 'callssid') callssid_conn.close() def find_new_msg(): # Connect to the pseudo_aprs_messages database pseudo_aprs_db = f'{data_dir}/pseudo_aprs_messages.db' connection, pseudo_cursor = establish_connection(pseudo_aprs_db) pseudo_aprs_rows = retrieve_data_from_table(pseudo_cursor, 'pseudo_aprs_messages') connection.close() pseudo_aprs_set = set(row[0] for row in pseudo_aprs_rows) # Extract the messageid from each row return pseudo_aprs_set def new_msg(srccall, trgcall, message): new_msg_str = srccall + '(' + trgcall + '):' + message dapnet(new_msg_str) def process_row(row, aprs_messages_db, pseudo_aprs_set, lock): conn, cursor = establish_connection(aprs_messages_db) try: # Acquire the lock before accessing the database lock.acquire() result = get_aprs(row) if result is None: return entries, trgcall = result if not entries: return for item in entries: messageid = item['messageid'] srccall = item['srccall'] message = item['message'] # Check if messageid already exists in the SQLite table cursor.execute("SELECT messageid FROM aprs_messages WHERE messageid = ?", (messageid,)) result = cursor.fetchone() # If messageid doesn't exist, insert the data into the SQLite table if not result: cursor.execute("INSERT INTO aprs_messages (messageid, srccall, message, trgcall) VALUES (?, ?, ?, ?)", (messageid, srccall, message, trgcall)) # Check if the messageid also doesn't exist in the pseudo_aprs_messages if messageid not in pseudo_aprs_set: # Pass the new message to new_msg() function new_msg(srccall, trgcall, message) conn.commit() finally: # Release the lock after the database operations are done lock.release() cursor.close() conn.close() def APRS_messages(): # Create a SQLite database aprs_messages_db = f'{data_dir}/aprs_messages.db' conn, cursor = establish_connection(aprs_messages_db) create_table_if_not_exists(cursor, 'aprs_messages', '(messageid TEXT PRIMARY KEY, srccall TEXT, message TEXT, trgcall TEXT)') cursor.close() conn.close() # Get pseudo_aprs_set using your find_new_msg() function pseudo_aprs_set = find_new_msg() # Number of processes to create num_processes = multiprocessing.cpu_count() # Create a process pool pool = multiprocessing.Pool(processes=num_processes) # Create a shared lock using Manager manager = Manager() lock = manager.Lock() # Prepare arguments for the process_row function args = [(row, aprs_messages_db, pseudo_aprs_set, lock) for row in rows] # Map the process_row function to the arguments in the pool pool.starmap(process_row, args) # Close the pool and wait for all processes to finish pool.close() pool.join() if __name__ == "__main__": APRS_messages() a2d-2.0.5/a2d/routes/000077500000000000000000000000001464731662700141735ustar00rootroot00000000000000a2d-2.0.5/a2d/routes/__init__.py000066400000000000000000000000201464731662700162740ustar00rootroot00000000000000#Python package a2d-2.0.5/a2d/routes/auth.py000066400000000000000000000123721464731662700155130ustar00rootroot00000000000000import os from flask import Blueprint, render_template, request, redirect, session from a2d.modals.user import verify_pin, create_user, create_user_passphrase, decrypt_user_passphrase from a2d.modals.creds import save_credentials, get_credentials from a2d.routes.handle_db import reset_dbs from functools import wraps import configparser auth_routes = Blueprint('auth', __name__) def handle_digits(): digit1 = request.form['digit1'] digit2 = request.form['digit2'] digit3 = request.form['digit3'] digit4 = request.form['digit4'] digit5 = request.form['digit5'] digit6 = request.form['digit6'] pin = digit1 + digit2 + digit3 + digit4 + digit5 + digit6 return pin @auth_routes.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': pin = handle_digits() if verify_pin(pin): session['pin'] = pin return redirect('/') else: return render_template('login.html', error=True) return render_template('login.html') @auth_routes.route('/logout') def logout(): session.clear() return redirect('/login') # Check if the user is authenticated before accessing protected routes def passphrase_required(f): @wraps(f) def decorated_function(*args, **kwargs): if check_passphrase_file() == "user_passphrase.bin does not exist." or passphrase_authenticated(): return f(*args, **kwargs) return render_template('verifypassphrase.html') return decorated_function # Check if the user has entered the correct passphrase def passphrase_authenticated(): return session.get('pp_authenticated', False) @auth_routes.route('/create-passphrase', methods=['GET', 'POST']) @passphrase_required def passphrase_change(): if request.method == 'POST': pin = handle_digits() create_user(pin) session['pin'] = pin passphrase = request.form['passphrase'] create_user_passphrase(passphrase) session['passphrase'] = passphrase return redirect('/logout') return render_template('updatepin.html') @auth_routes.route('/verify-passphrase', methods=['POST']) def verify_passphrase(): passphrase = request.form.get('passphrase') stored_passphrase = decrypt_user_passphrase() if passphrase == stored_passphrase: session['passphrase'] = passphrase session['pp_authenticated'] = True return redirect('/create-passphrase') else: return redirect('/logout') @auth_routes.route('/pin-change', methods=['GET', 'POST']) def pin_change(): if request.method == 'POST': pin = handle_digits() create_user(pin) session['pin'] = pin return redirect('/logout') else: if check_passphrase_file() == "user_passphrase.bin does not exist.": return redirect('/create-passphrase') return render_template('verifypassphrase.html') @auth_routes.route('/check_pin_file') def check_pin_file(): pin_file_exists = os.path.isfile('/etc/a2d/.creds/user_pin.bin') if pin_file_exists: message = "7965732062696e" else: message = "6e6f2062696e" return message def check_passphrase_file(): passphrase_file_exists = os.path.isfile('/etc/a2d/.creds/user_passphrase.bin') if passphrase_file_exists: message = "user_passphrase.bin exists." else: message = "user_passphrase.bin does not exist." return message # Define a decorator function to check if the user is logged in def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): if 'pin' not in session: return redirect('/logout') return f(*args, **kwargs) return decorated_function @auth_routes.route('/AnD-creds', methods=['POST']) @login_required def handle_creds(): callsign = request.form['callsign'] txgroup = request.form['txgroup'] dapnetuser = request.form['dapnetuser'] dapnetpass = request.form['dapnetpass'] aprsapi = request.form['aprsapi'] credentials = get_credentials() previous_callsign = credentials.get('callsign') previous_txgroup = credentials.get('txgroup') previous_dapnetuser = credentials.get('dapnetuser') previous_dapnetpass = credentials.get('dapnetpass') if previous_callsign != callsign or previous_txgroup != txgroup or previous_dapnetuser != dapnetuser or previous_dapnetpass != dapnetpass: reset_dbs() # Update the configuration file with the status code config = configparser.ConfigParser() config.optionxform = lambda option: option config.read('/etc/a2d/dapnet_data.ini') reset_dapnet_http_code = '201' config.set('DAPNETVerify', 'HTTPStatus', reset_dapnet_http_code) with open('/etc/a2d/dapnet_data.ini', 'w') as configfile: config.write(configfile) runconfig = configparser.ConfigParser() runconfig.optionxform = lambda option: option runconfig.read('/etc/a2d/runscript_data.ini') reset_aprs_check_ini = 'unverified' runconfig.set('APRSCheck', 'APRSCheck', reset_aprs_check_ini) with open('/etc/a2d/runscript_data.ini', 'w') as runconfigfile: runconfig.write(runconfigfile) save_credentials(callsign, txgroup, dapnetuser, dapnetpass, aprsapi) return redirect('/') a2d-2.0.5/a2d/routes/certs.py000066400000000000000000000105511464731662700156670ustar00rootroot00000000000000import os import subprocess import datetime import re from a2d.routes.nginx import reload_nginx, read_nginx_config, disable_default_ng, enable_default_ng def read_ssl(cert_file): if not cert_file or not os.path.exists(cert_file): return "None,None,None" try: # Use subprocess to run openssl commands subject = subprocess.check_output(['openssl', 'x509', '-noout', '-subject', '-in', cert_file]).decode() expiry_date = subprocess.check_output(['openssl', 'x509', '-noout', '-enddate', '-in', cert_file]).decode() common_name = None organization_name = None # Use regular expressions to extract CN and O fields cn_match = re.search(r'CN\s*=\s*([^/,]+)', subject) o_match = re.search(r'O\s*=\s*([^/,]+)', subject) if cn_match: common_name = cn_match.group(1).strip() if o_match: organization_name = o_match.group(1).strip() expiry_date = expiry_date.split('=')[1].strip() expiry_date = datetime.datetime.strptime(expiry_date, '%b %d %H:%M:%S %Y %Z') expiry_date = expiry_date.strftime('%Y-%m-%d %H:%M:%S') return f"{common_name},{organization_name},{expiry_date}" except Exception as e: return "None,None,None" def a2d_self_ssl(common_name, validity_days, organization_name): ssl_cert_path = '/etc/nginx/ssl/a2d-ssl.crt' ssl_key_path = '/etc/nginx/ssl/a2d-ssl.key' try: # Check if the certificate and key already exist if os.path.exists(ssl_cert_path) and os.path.exists(ssl_key_path): os.remove(ssl_cert_path) os.remove(ssl_key_path) # Redirect stdout and stderr to /dev/null to avoid unknown output from openssl with open(os.devnull, 'w') as devnull: # Generate the self-signed certificate subprocess.run([ "openssl", "req", "-new", "-newkey", "rsa:4096", "-days", str(validity_days), "-nodes", "-x509", "-keyout", ssl_key_path, "-out", ssl_cert_path, "-subj", f"/CN={common_name}/O={organization_name}" ], check=True, stdout=devnull, stderr=devnull) # Verify if the certificate and key were successfully generated if os.path.exists(ssl_cert_path) and os.path.exists(ssl_key_path): return "sSSL generated" else: return "Error generating SSL" except subprocess.CalledProcessError as e: return "Error generating SSL" except Exception as e: return "Error generating SSL" def a2d_rm_cassl(common_name): if not common_name: return try: # Execute the certbot command to delete the certificate subprocess.run(["certbot", "delete", "--cert-name", common_name, "--non-interactive"], check=True) return "caSSL removed" except FileNotFoundError: return "Error removing SSL" except subprocess.CalledProcessError as e: return "Error removing SSL" def a2d_ca_ssl(common_name, email): disable_default_ng() reload_nginx() listen_port = read_nginx_config('listen') try: # Execute the certbot command to generate the certificate subprocess.run(["certbot", "certonly", "-d", common_name, "--standalone", "--preferred-challenges", "http", "--email", email, "--agree-tos", "--non-interactive"], check=True) if listen_port != '80': enable_default_ng() reload_nginx() return "caSSL generated" except subprocess.CalledProcessError as e: if listen_port != '80': enable_default_ng() reload_nginx() return "Error generating SSL" def a2d_ca_list(): try: # Execute the 'ls' and 'grep' commands result = subprocess.run(["ls", "/etc/letsencrypt/live/"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) output = result.stdout # Filter out the "README" entry certificates = [cert.strip() for cert in output.split('\n') if cert.strip() != "README"] return certificates except subprocess.CalledProcessError as e: return [] def a2d_rm_selfssl(): ssl_cert_path = '/etc/nginx/ssl/a2d-ssl.crt' ssl_key_path = '/etc/nginx/ssl/a2d-ssl.key' # Check if the certificate and key already exist if os.path.exists(ssl_cert_path) and os.path.exists(ssl_key_path): os.remove(ssl_cert_path) os.remove(ssl_key_path) a2d-2.0.5/a2d/routes/config.py000066400000000000000000000071341464731662700160170ustar00rootroot00000000000000import os import hashlib import yaml from flask import Blueprint, request, redirect, render_template, Response from a2d.routes.auth import login_required from a2d.modals.user import decrypt_user_passphrase from a2d.modals.creds import get_credentials config_routes = Blueprint('config', __name__) file_paths = { "00": "/etc/a2d/a2d_adv_conf.ini", "01": "/etc/a2d/.creds/a2d_AnDinfo.conf", "10": "/etc/a2d/.keys/a2d_AnDinfo.key", } @config_routes.route("/export", methods=["POST"]) @login_required def export_files(): passphrase = request.form.get("passphrase") if not verify_passphrase(passphrase): return "Invalid Passphrase", 400 all_files_exist = True for file_path in file_paths.values(): if not os.path.exists(file_path): all_files_exist = False break if not all_files_exist: return "Files missing", 401 if all_files_exist: data = {file_name: read_binary_file(file_path) for file_name, file_path in file_paths.items()} watermark = calculate_watermark(data) export_data = {"d": data, "f": watermark} yaml_data = yaml.dump(export_data) sha256 = hashlib.sha256() sha256.update(yaml_data.encode('utf-8')) response = Response(yaml_data, mimetype="application/x-yaml") response.headers["Content-Disposition"] = "attachment; filename=a2d_config_backup.yaml" return response @config_routes.route("/import", methods=["POST"]) @login_required def import_files(): passphrase = request.form.get("passphrase") if not verify_passphrase(passphrase): return "Invalid Passphrase", 400 import_file = request.files.get("import_file") if import_file and import_file.filename.endswith(".yaml"): yaml_data = import_file.read().decode('utf-8') data = yaml.safe_load(yaml_data) watermark = data.get("f") if verify_watermark(data["d"], watermark): if os.path.exists('/etc/a2d/.keys/a2d_AnDinfo.key'): os.chmod('/etc/a2d/.keys/a2d_AnDinfo.key', 0o644) for file_name, file_data in data["d"].items(): if file_name in file_paths: create_directory_if_not_exists(file_paths[file_name]) write_binary_file(file_paths[file_name], file_data) os.chmod('/etc/a2d/.keys/a2d_AnDinfo.key', 0o400) return redirect('/') #This wont work, implemented in client side else: return "File tampered", 401 else: return "Invalid file", 402 def read_binary_file(file_path): with open(file_path, "rb") as file: return file.read() def write_binary_file(file_path, data): with open(file_path, "wb") as file: file.write(data) def create_directory_if_not_exists(file_path): directory = os.path.dirname(file_path) if not os.path.exists(directory): os.makedirs(directory) def calculate_watermark(data): yaml_data = yaml.dump(data, default_flow_style=False, allow_unicode=True) sha256 = hashlib.sha256() sha256.update(yaml_data.encode('utf-8')) return sha256.digest() def verify_watermark(data, watermark): calculated_watermark = calculate_watermark(data) return calculated_watermark == watermark def verify_passphrase(passphrase): stored_passphrase = decrypt_user_passphrase() return passphrase == stored_passphrase @config_routes.route('/help-full') @login_required def help_full(): creds = get_credentials return render_template('help_full.html', creds=creds) @config_routes.route('/help-short') def help_short(): return render_template('help_short.html') a2d-2.0.5/a2d/routes/data.py000066400000000000000000000131471464731662700154640ustar00rootroot00000000000000from flask import Blueprint, jsonify, request, redirect import os import sqlite3 import configparser import datetime from a2d.modals.creds import get_credentials from a2d.routes.run import restart_service from a2d.routes.auth import login_required from a2d.routes.handle_db import reset_dbs data_routes = Blueprint('data', __name__) adv_conf = '/etc/a2d/a2d_adv_conf.ini' runscript_data = '/etc/a2d/runscript_data.ini' @data_routes.route('/fetch-logs') @login_required def fetch_logs(): if os.path.exists('/var/lib/a2d/dbs/aprs_messages.db') and os.path.exists('/var/lib/a2d/dbs/pseudo_aprs_messages.db'): aprs_db_path = '/var/lib/a2d/dbs/aprs_messages.db' pseudo_db_path = '/var/lib/a2d/dbs/pseudo_aprs_messages.db' config = configparser.ConfigParser() config.optionxform = lambda option: option # Preserve case sensitivity config.read(adv_conf) max_logs = config.getint('LOGS', 'NumberOfLogs', fallback='') try: # Connect to the aprs database connection = sqlite3.connect(aprs_db_path) cursor = connection.cursor() cursor.execute('SELECT * FROM aprs_messages') aprs_rows = cursor.fetchall() connection.close() # Connect to the pseudo_aprs_messages database connection = sqlite3.connect(pseudo_db_path) cursor = connection.cursor() cursor.execute('SELECT * FROM pseudo_aprs_messages') pseudo_aprs_rows = cursor.fetchall() connection.close() # Convert the lists of rows to sets for easy comparison aprs_set = set(aprs_rows) pseudo_aprs_set = set(pseudo_aprs_rows) # Find the difference between the two sets (rows present in aprs_messages but not in pseudo_aprs_messages) diff_set = aprs_set - pseudo_aprs_set # Sort the logs in descending order based on the first element (row[0]) logs = [] count = 0 # Counter to keep track of the number of logs added for row in sorted(diff_set, reverse=True, key=lambda x: x[0]): log = f'{row[0]} > {row[1]} ({row[3]}): {row[2]}' logs.append(log) count += 1 if count >= max_logs: # Limit the number of logs user setting break return jsonify(logs) except sqlite3.OperationalError: pass return jsonify([]) # Return an empty list if an error occurs @data_routes.route('/save-adv-conf', methods=['POST']) @login_required def save_adv_conf(): config = configparser.ConfigParser() config.optionxform = lambda option: option # Preserve case sensitivity config.read(adv_conf) previous_ssids = config.get('SSID', 'PreferredSSIDs', fallback='').split(',') # Update the values in the config object intervalmin = request.form['intervalmin'] intervalh = request.form['intervalh'] ssids = ','.join(request.form.getlist('ssids[]')) ssids_list = ssids.split(',') logcounts = request.form['logcounts'] config['LOGS']['NumberOfLogs'] = logcounts if intervalmin == '0' and intervalh == '0': config['Timer']['IntervalMinutes'] = '15' config['Timer']['IntervalHours'] = '0' elif intervalmin == '1' and intervalh == '0': config['Timer']['IntervalMinutes'] = '2' config['Timer']['IntervalHours'] = '0' else: config['Timer']['IntervalMinutes'] = intervalmin config['Timer']['IntervalHours'] = intervalh if previous_ssids != ssids_list: if ssids is None: config['SSID']['PreferredSSIDs'] = '0' else: config['SSID']['PreferredSSIDs'] = ssids reset_dbs() with open(adv_conf, 'w') as config_file: config.write(config_file, space_around_delimiters=False) restart_service() return redirect('/') def get_adv_conf_values(): config = configparser.ConfigParser() config.optionxform = lambda option: option # Preserve case sensitivity config.read(adv_conf) intervalmin = config.get('Timer', 'IntervalMinutes', fallback='') intervalh = config.get('Timer', 'IntervalHours', fallback='') ssids = config.get('SSID', 'PreferredSSIDs', fallback='').split(',') logcounts = config.get('LOGS', 'NumberOfLogs', fallback='') return intervalmin, intervalh, ssids, logcounts @data_routes.route('/for-dash') @login_required def for_dash(): credentials = get_credentials() #Get callsign and callsign2 callsign = credentials.get('callsign', 'Setup Configuration') callsign2 = credentials.get('callsign') #ssids-display config = configparser.ConfigParser() config.read(adv_conf) ssids_display = config.get('SSID', 'PreferredSSIDs', fallback='').split(',') ssids_display = ['-{}'.format(ssid) for ssid in ssids_display if ssid.strip()] ssids_display = ' '.join(ssids_display) #Run interval intervalmin = config.get('Timer', 'IntervalMinutes', fallback='') intervalh = config.get('Timer', 'IntervalHours', fallback='') run_interval = f"{intervalh}h {intervalmin}min" #Last run config.read(runscript_data) # Get the values for last run unix time last_run_unix = config.get('InternalTimeStamp', 'LastRunTime', fallback='') last_run_human = 'None' if not last_run_unix else datetime.datetime.fromtimestamp(float(last_run_unix)).strftime('%Y-%m-%d %H:%M:%S') dash_data = { "callsign": callsign, "callsign2": callsign2, "ssids_display": ssids_display, "run_interval": run_interval, "last_run_human": last_run_human } return jsonify(dash_data) a2d-2.0.5/a2d/routes/dns.py000066400000000000000000000260331464731662700153350ustar00rootroot00000000000000import os import subprocess import re from datetime import datetime from flask import Blueprint, request, render_template, jsonify from a2d.modals.creds import get_credentials from a2d.routes.auth import login_required from a2d.routes.certs import read_ssl, a2d_self_ssl, a2d_rm_cassl, a2d_ca_ssl, a2d_ca_list from a2d.routes.nginx import reload_nginx, read_nginx_config, disable_default_ng, enable_default_ng dns_routes = Blueprint('dns', __name__) # Define the directory paths NGINX_CONFIG_PATH = '/etc/nginx/conf.d/00-a2d.conf' SSL_CERT_PATH = '/etc/nginx/ssl/a2d-ssl.crt' SSL_KEY_PATH = '/etc/nginx/ssl/a2d-ssl.key' #DNS data to HTML @dns_routes.route('/local-dns-setting', methods=['GET']) @login_required def local_dns_setting(): creds = get_credentials() # Need this to display Navbar_full in dns.html #SSL status and server name current_listen_port = read_nginx_config('listen') if current_listen_port == 'ssl': #Strip from nginx conifg file current_listen_port = '443' current_server_name = read_nginx_config('server_name') current_ssl_status = current_listen_port == '443' a2d_default_dns = (current_listen_port == '9331' and current_server_name == '_' and not current_ssl_status) #Self SSL details output = read_ssl(SSL_CERT_PATH) stored_selfcommon_name, stored_selforganization_name, _ = output.split(',') if stored_selfcommon_name == '': stored_selfcommon_name = 'None' if stored_selforganization_name == '': stored_selforganization_name = 'None' #Get cassl list cassl_certs_list = list_cassl_certs() return render_template( 'dns.html', listen_port=current_listen_port, server_name=current_server_name, current_ssl_status=current_ssl_status, a2d_default_dns=a2d_default_dns, stored_selfcommon_name=stored_selfcommon_name, stored_selforganization_name=stored_selforganization_name, cassl_certs=cassl_certs_list, creds=creds ) #Set server config @dns_routes.route('/server-config', methods=['POST']) @login_required def server_config(): set_selfssl_status = 'disable' set_cassl_status = 'disable' cassl_certs_list = list_cassl_certs() listen_port = request.form['listen_port'] server_name = request.form['server_name'] enable_self_ssl = request.form.get('enable_ssl') == 'true' set_cassl_certs = request.form['set_cassl_certs'] enable_ca_ssl = request.form.get('enable_cassl') == 'true' set_default_dns = request.form.get('set_default_dns') == 'true' if enable_self_ssl and not enable_ca_ssl: # Check if the self-signed certificate and key files exist if (os.path.exists(SSL_CERT_PATH) and os.path.exists(SSL_KEY_PATH)): set_selfssl_status = 'enable' else: return "Create SSL" elif enable_ca_ssl and not enable_self_ssl: if set_cassl_certs in cassl_certs_list: if set_cassl_certs == server_name: set_cassl_status = 'enable' elif set_cassl_certs == '': return "Create caSSL" else: set_cassl_status = 'enable' server_name = set_cassl_certs else: return "Create caSSL" elif listen_port == '443' and not enable_self_ssl and not set_default_dns: if not (os.path.exists(SSL_CERT_PATH) and os.path.exists(SSL_KEY_PATH)): return "Create SSL" else: set_selfssl_status = 'enable' elif listen_port == '443' and set_default_dns: listen_port = '9331' set_selfssl_status = 'disable' server_name = '_' elif set_default_dns: listen_port = '9331' set_selfssl_status = 'disable' server_name = '_' else: pass # Update the Nginx configuration update_nginx_config(listen_port, server_name, set_selfssl_status, set_cassl_status) if listen_port == '80': disable_default_ng() elif listen_port != '80': enable_default_ng() reload_nginx() if set_selfssl_status == 'enable': return "SSL enabled" elif set_cassl_status == 'enable': return "caSSL enabled" elif listen_port == '9331' and set_selfssl_status == 'disable' and server_name == '_': return "Set default" else: return "Server updated" #Update nginx config def update_nginx_config(listen_port, server_name, set_selfssl_status, set_cassl_status): try: with open(NGINX_CONFIG_PATH, 'r') as config_file: lines = config_file.readlines() if server_name != '_' or '': CASSL_CERT_PATH = f'/etc/letsencrypt/live/{server_name}/fullchain.pem' CASSL_KEY_PATH = f'/etc/letsencrypt/live/{server_name}/privkey.pem' else: CASSL_CERT_PATH, CASSL_KEY_PATH = 'None', 'None' updated_lines = [] ssl_line = "listen 443 ssl;" self_ssl_line1 = f"ssl_certificate {SSL_CERT_PATH};" self_ssl_line2 = f"ssl_certificate_key {SSL_KEY_PATH};" ca_ssl_line1 = f"ssl_certificate {CASSL_CERT_PATH};" ca_ssl_line2 = f"ssl_certificate_key {CASSL_KEY_PATH};" updated_listen = False # Flag to keep track of whether the "listen" line has been updated for line in lines: if line.strip().startswith("listen "): if not updated_listen: # Only add one "listen" line if set_selfssl_status == 'enable': updated_lines.append(f" {ssl_line}\n {self_ssl_line1}\n {self_ssl_line2}\n") elif set_cassl_status == 'enable': updated_lines.append(f" {ssl_line}\n {ca_ssl_line1}\n {ca_ssl_line2}\n") else: updated_lines.append(f" listen {listen_port};\n") updated_listen = True elif line.strip().startswith("ssl_certificate"): if set_selfssl_status != 'enable' or set_cassl_status != 'enable': # Skip these lines if self-signed SSL is not enabled continue elif line.strip().startswith("server_name "): updated_lines.append(f" server_name {server_name};\n") else: updated_lines.append(line) with open(NGINX_CONFIG_PATH, 'w') as config_file: config_file.writelines(updated_lines) except Exception as e: return f"Error updating nginx conf: {e}" #Generate self-signed SSL @dns_routes.route('/gen-self-signed-ssl', methods=['POST']) @login_required def gen_self_signed_ssl(): common_name = request.form["common_name"] validity_days = int(request.form["validity_days"]) organization_name = request.form["organization_name"] self_ssl_result = a2d_self_ssl(common_name, validity_days, organization_name) if self_ssl_result == "sSSL generated": reload_nginx() return "sSSL generated" else: return "Error generating SSL" #Generate CN signed SSL @dns_routes.route('/gen-ca-ssl', methods=['POST']) @login_required def gen_ca_ssl(): ca_common_name = request.form["ca_common_name"] email_id = request.form["email_id"] cassl_certs_list = list_cassl_certs() if ca_common_name not in cassl_certs_list: if is_valid_email(email_id): ca_ssl = a2d_ca_ssl(ca_common_name, email_id) if ca_ssl == "caSSL generated": return "caSSL generated" else: return "Error generating SSL" else: return "invalid email" else: return "caSSL exist" #Email syntax check def is_valid_email(email): pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$' if re.match(pattern, email): return True else: return False #Remove CN signed SSL @dns_routes.route('/rm-ca-ssl', methods=['POST']) @login_required def rm_ca_ssl(): rm_cassl_certs = request.form['rm_cassl_certs'] cassl_certs_list = list_cassl_certs() current_server_name = read_nginx_config('server_name') if rm_cassl_certs in cassl_certs_list: if rm_cassl_certs != current_server_name: rm_cassl = a2d_rm_cassl(rm_cassl_certs) if rm_cassl == "caSSL removed": reload_nginx() return "caSSL removed" else: return "Error removing SSL" else: return "caSSL in use" else: return "No caSSL" #Get the list of CA SSL def list_cassl_certs(): result = a2d_ca_list() return result #HTML data display @dns_routes.route('/for-html', methods=['GET']) @login_required def for_html(): # Read the current DNS configuration values current_listen_port = read_nginx_config('listen') if current_listen_port == 'ssl': current_listen_port = '443' current_server_name = read_nginx_config('server_name') current_SSL_crt = read_nginx_config('ssl_certificate') if current_SSL_crt: CASSL_CERT_PATH = f'/etc/letsencrypt/live/{current_server_name}/fullchain.pem' else: CASSL_CERT_PATH = 'None' if current_SSL_crt == SSL_CERT_PATH: current_ssl_status = 'Self-Signed' elif current_SSL_crt == CASSL_CERT_PATH: current_ssl_status = 'CA Validated' else: current_ssl_status = 'Disabled' #Read SSL certificate status if current_SSL_crt: output = read_ssl(current_SSL_crt) common_name, organization_name, expiry_date = output.split(',') # Handle empty values by replacing them with None if common_name == '': common_name = 'None' if organization_name == '': organization_name = 'None' if expiry_date == '': expiry_date = 'None' current_date = datetime.now() current_date =current_date.strftime('%Y-%m-%d %H:%M:%S') if expiry_date and expiry_date < current_date: expiry_date = 'Expired' else: common_name, organization_name, expiry_date = 'None', 'None', 'None' #Read service status nginx_status = get_ctl_status('nginx') gunicorn_status = get_ctl_status('a2d') response_data = { "listen_port": current_listen_port, "server_name": current_server_name, "current_ssl_status": current_ssl_status, "common_name": common_name, "organization_name": organization_name, "expiry_date": expiry_date, "nginx_status": nginx_status, "gunicorn_status": gunicorn_status } return jsonify(response_data) #Get systemctl status def get_ctl_status(ctl_name): try: output = subprocess.check_output(['systemctl', 'status', ctl_name], text=True) ctl_status = re.search(r'since\s(.*)', output).group(1) ctl_status = ctl_status.split(";")[1].strip() ctl_status = str(ctl_status) return ctl_status except subprocess.CalledProcessError as e: if e.returncode == 4: ctl_status = '(Not installed)' return ctl_status elif e.returncode == 3: ctl_status = '(Not running)' return ctl_status ctl_status = str(e) return ctl_status a2d-2.0.5/a2d/routes/docs.py000066400000000000000000000011711464731662700154750ustar00rootroot00000000000000from flask import Blueprint, render_template, send_from_directory, redirect, abort import os docs_routes = Blueprint('docs', __name__) DOCUMENTATION_PATH = '/usr/share/doc/a2d-doc/html' @docs_routes.route('/docs/') def serve_docs(filename): try: return send_from_directory(DOCUMENTATION_PATH, filename) except FileNotFoundError: abort(404) @docs_routes.route('/info') def info(): index_file = os.path.join(DOCUMENTATION_PATH, 'index.html') if os.path.exists(index_file): return redirect('/docs/index.html', code=302) else: return render_template('no_docs.html') a2d-2.0.5/a2d/routes/handle_db.py000066400000000000000000000016121464731662700164450ustar00rootroot00000000000000import os import sqlite3 def reset_dbs(): db_folder = '/var/lib/a2d/dbs' aprs_db_path = os.path.join(db_folder, 'aprs_messages.db') pseudo_aprs_db_path = os.path.join(db_folder, 'pseudo_aprs_messages.db') if os.path.exists(aprs_db_path) and os.path.exists(pseudo_aprs_db_path): clear_table(aprs_db_path, 'aprs_messages') clear_table(pseudo_aprs_db_path, 'pseudo_aprs_messages') elif os.path.exists(aprs_db_path): clear_table(aprs_db_path, 'aprs_messages') elif os.path.exists(pseudo_aprs_db_path): clear_table(pseudo_aprs_db_path, 'pseudo_aprs_messages') def clear_table(db_path, table_name): try: conn = sqlite3.connect(db_path) cursor = conn.cursor() cursor.execute(f'DELETE FROM {table_name}') conn.commit() conn.close() os.remove(db_path) except Exception as e: pass a2d-2.0.5/a2d/routes/network.py000066400000000000000000000022031464731662700162330ustar00rootroot00000000000000import subprocess import re from flask import Blueprint, jsonify from a2d.routes.auth import login_required network_routes = Blueprint('network', __name__) @network_routes.route('/get-rtt', methods=['GET']) @login_required def get_rtt(): hosts = {"aprs": "api.aprs.fi", "dapnet": "www.hampager.de"} rtt_results = {} for key, host in hosts.items(): try: result = subprocess.run(['ping', '-c', '1', host], capture_output=True, text=True, timeout=5) output_lines = result.stdout.splitlines() rtt_line = [line for line in output_lines if "time=" in line] if rtt_line: rtt_match = re.search(r"time=(\d+(\.\d+)?)", rtt_line[0]) if rtt_match: rtt_value = float(rtt_match.group(1)) rtt_results[key + "_rtt"] = f"{rtt_value} ms" else: rtt_results[key + "_rtt"] = "Unreachable" else: rtt_results[key + "_rtt"] = "Unreachable" except subprocess.TimeoutExpired: rtt_results[key + "_rtt"] = "Unreachable" return jsonify(rtt_results) a2d-2.0.5/a2d/routes/nginx.py000066400000000000000000000043411464731662700156720ustar00rootroot00000000000000import subprocess import re NGINX_CONFIG_PATH = '/etc/nginx/conf.d/00-a2d.conf' CORE_NGINX_CONF = '/etc/nginx/nginx.conf' def reload_nginx(): if not is_nginx_installed(): return try: subprocess.run(["systemctl", "reload", "nginx"], check=True) except subprocess.CalledProcessError as e: return "Error reloading nginx:", e def read_nginx_config(key): if not is_nginx_installed(): return try: with open(NGINX_CONFIG_PATH, 'r') as config_file: for line in config_file: if line.strip().startswith(f"{key} "): return line.split()[-1].rstrip(';') except Exception as e: return f"Error reading nginx conf: {e}" def disable_default_ng(): if not is_nginx_installed(): return with open(CORE_NGINX_CONF, 'r') as nginx_conf_file: nginx_conf_content = nginx_conf_file.readlines() # Search for the line with 'include /etc/nginx/sites-enabled/*;' and comment it out for i, line in enumerate(nginx_conf_content): if 'include /etc/nginx/sites-enabled/*;' in line: if not line.startswith('#'): nginx_conf_content[i] = '#' + line with open(CORE_NGINX_CONF, 'w') as nginx_conf_file: nginx_conf_file.writelines(nginx_conf_content) def enable_default_ng(): if not is_nginx_installed(): return with open(CORE_NGINX_CONF, 'r') as nginx_conf_file: nginx_conf_content = nginx_conf_file.readlines() # Search for the line with 'include /etc/nginx/sites-enabled/*;' and uncomment it for i, line in enumerate(nginx_conf_content): if re.match(r'^\s*#\s*include /etc/nginx/sites-enabled/\*;', line): nginx_conf_content[i] = re.sub(r'^\s*#\s*', '', line) with open(CORE_NGINX_CONF, 'w') as nginx_conf_file: nginx_conf_file.writelines(nginx_conf_content) def is_nginx_installed(): try: # Try checking the nginx status using systemctl subprocess.check_output(['systemctl', 'status', 'nginx'], stderr=subprocess.STDOUT) return True except subprocess.CalledProcessError as e: if e.returncode == 4: return False return False except FileNotFoundError: return False a2d-2.0.5/a2d/routes/reset.py000066400000000000000000000107561464731662700157000ustar00rootroot00000000000000import os import sqlite3 import shutil from flask import Blueprint, request import configparser from a2d.routes.auth import login_required from a2d.modals.user import decrypt_user_passphrase from a2d.a2d_utils.a2d_utils import remove_cronjob from a2d.routes.dns import update_nginx_config from a2d.routes.certs import a2d_rm_selfssl, a2d_ca_list, a2d_rm_cassl from a2d.routes.nginx import reload_nginx, enable_default_ng reset_routes = Blueprint('reset', __name__) adv_conf = '/etc/a2d/a2d_adv_conf.ini' dapnet_data = '/etc/a2d/dapnet_data.ini' runscript_data = '/etc/a2d/runscript_data.ini' @reset_routes.route('/reset-portal', methods=['POST']) @login_required def reset_account(): passphrase = request.form.get("passphrase") if not verify_passphrase(passphrase): return "Invalid Passphrase", 400 reset_confirm = request.form['reset_confirm'] selfSSL_delete = request.form['self_ssl_delete'] == 'true' caSSL_delete = request.form['ca_ssl_delete'] == 'true' if reset_confirm.lower() == 'delete': remove_cronjob() rm_dbs() #a2d_adv_conf.ini config1 = configparser.ConfigParser() config1.optionxform = lambda option: option # Preserve case sensitivity config1.read(adv_conf) config1.set('Timer', 'IntervalMinutes', '15') config1.set('Timer', 'IntervalHours', '0') config1.set('SSID', 'PreferredSSIDs', '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15') config1.set('LOGS', 'NumberOfLogs', '150') with open(adv_conf, 'w') as config_file1: config1.write(config_file1, space_around_delimiters=False) #dapnet_data.ini config2 = configparser.ConfigParser() config2.optionxform = lambda option: option # Preserve case sensitivity config2.read(dapnet_data) config2.set('DAPNETVerify', 'HTTPStatus', '201') with open(dapnet_data, 'w') as config_file2: config2.write(config_file2, space_around_delimiters=False) #runscript_data.ini config3 = configparser.ConfigParser() config3.optionxform = lambda option: option # Preserve case sensitivity config3.read(runscript_data) config3.set('InternalTimeStamp', 'LastRunTime', '') config3.set('APRSKeyVerify', 'apiKeyStatus', 'valid') config3.set('APRSTimeStamp', 'LastAPRSPull', '') config3.set('APRSCheck', 'APRSCheck', 'verified') config3.set('APRSCheck', 'LastCheck', '') with open(runscript_data, 'w') as config_file3: config3.write(config_file3, space_around_delimiters=False) if selfSSL_delete: a2d_rm_selfssl() a2d_default_dns() if caSSL_delete: cassl_certs_list = a2d_ca_list() for rm_cassl_certs in cassl_certs_list: a2d_rm_cassl(rm_cassl_certs) a2d_default_dns() try: #Remove all creds and keys shutil.rmtree('/etc/a2d/.creds') shutil.rmtree('/etc/a2d/.keys') except Exception as e: pass return 'logout' else: return 'Type delete' def verify_passphrase(passphrase): stored_passphrase = decrypt_user_passphrase() return passphrase == stored_passphrase def a2d_default_dns(): listen_port = '9331' set_selfssl_status = 'disable' server_name = '_' set_cassl_status = '' update_nginx_config(listen_port, server_name, set_selfssl_status, set_cassl_status) enable_default_ng() reload_nginx() def rm_dbs(): db_folder = '/var/lib/a2d/dbs' aprs_db_path = os.path.join(db_folder, 'aprs_messages.db') pseudo_aprs_db_path = os.path.join(db_folder, 'pseudo_aprs_messages.db') callssid_db_path = os.path.join(db_folder, 'callssid.db') ham_info_db_path = os.path.join(db_folder, 'ham_info.db') previous_values_db_path = os.path.join(db_folder, 'previous_values.db') try: clear_table(aprs_db_path, 'aprs_messages') clear_table(pseudo_aprs_db_path, 'pseudo_aprs_messages') clear_table(callssid_db_path, 'callssid') clear_table(ham_info_db_path, 'ham_info') clear_table(previous_values_db_path, 'previous_values') except Exception as e: pass def clear_table(db_path, table_name): try: conn = sqlite3.connect(db_path) cursor = conn.cursor() cursor.execute(f'DELETE FROM {table_name}') conn.commit() conn.close() os.remove(db_path) except Exception as e: pass a2d-2.0.5/a2d/routes/run.py000066400000000000000000000121441464731662700153530ustar00rootroot00000000000000import os import subprocess import configparser from flask import Blueprint, redirect, jsonify from a2d.routes.auth import login_required from a2d.a2d_utils.a2d_utils import remove_cronjob run_routes = Blueprint('run', __name__) def generate_cron_job(interval_minutes, interval_hours, interval_hours_uni): cron_jobs = [] if interval_minutes == 0 and interval_hours == 0: cron_jobs.append(f"*/15 * * * * root /usr/bin/python3 -m a2d.runscripts") elif interval_minutes == 1 and interval_hours == 0: cron_jobs.append(f"*/2 * * * * root /usr/bin/python3 -m a2d.runscripts") elif interval_minutes != 0 and interval_hours == 0: cron_jobs.append(f"*/{interval_minutes} * * * * root /usr/bin/python3 -m a2d.runscripts") elif interval_minutes == 0 and interval_hours != 0: cron_jobs.append(f"0 */{interval_hours} * * * root /usr/bin/python3 -m a2d.runscripts") elif interval_minutes != 0 and interval_minutes != 1 and interval_hours != 0 and interval_hours != 1: cron_jobs.append(f"*/{interval_minutes} */{interval_hours} * * * root /usr/bin/python3 -m a2d.runscripts") else: cron_jobs.append(f"0 */{interval_hours_uni} * * * root /usr/bin/python3 -m a2d.runscripts") cron_jobs.append(f"0-59/{interval_minutes} * * * * root /usr/bin/python3 -m a2d.runscripts") return cron_jobs def add_cronjob(): # Read values from the configuration file config_file = '/etc/a2d/a2d_adv_conf.ini' config = configparser.ConfigParser() config.read(config_file) # Get the values for IntervalMinutes and IntervalHours interval_minutes = int(config.get('Timer', 'IntervalMinutes')) interval_hours = int(config.get('Timer', 'IntervalHours')) interval_hours_uni = interval_hours + 1 # Generate the cron job schedule using the updated function cron_schedule_list = generate_cron_job(interval_minutes, interval_hours, interval_hours_uni) # Create and write the cron job to /etc/cron.d/a2d.cron with open('/etc/cron.d/a2d', 'a') as cron_file: for cron_schedule in cron_schedule_list: cron_file.write(f'{cron_schedule}\n') def check_cronjob(): # Find the cron job that matches the specified command command = '/usr/bin/python3 -m a2d.runscripts' runstatus = "Stopped" # Set a default value before the loop try: with open('/etc/cron.d/a2d', 'r') as cron_file: for line in cron_file: if line.strip().endswith(command): runstatus = "Running" break # No need to continue reading the file once a match is found except FileNotFoundError: pass # Handle the case when the file doesn't exist return runstatus @run_routes.route('/start-service') @login_required def start_service(): if os.path.isfile('/etc/a2d/.creds/a2d_AnDinfo.conf'): runstatus = check_cronjob() if runstatus == "Stopped": # Call the function to add the cron job add_cronjob() run_command = ["nohup", "/usr/share/scripts/run_a2d.sh", "&"] subprocess.Popen(run_command) return redirect('/') return redirect('/') @run_routes.route('/stop-service') @login_required def stop_service(): runstatus = check_cronjob() if runstatus == "Running": # Call the function to remove the cron job remove_cronjob() return redirect('/') def restart_service(): runstatus = check_cronjob() if runstatus == "Running": remove_cronjob() add_cronjob() @run_routes.route('/service-status') @login_required def service_status(): runconfig_file = '/etc/a2d/runscript_data.ini' runconfig = configparser.ConfigParser() runconfig.read(runconfig_file) aprs_server = runconfig.get('APRSKeyVerify', 'apiKeyStatus', fallback='') dapconfig = configparser.ConfigParser() dapconfig_file = '/etc/a2d/dapnet_data.ini' dapconfig.read(dapconfig_file) dapnet_server = dapconfig.get('DAPNETVerify', 'HTTPStatus', fallback='') status_messages = { ('invalid', '201'): 'Invalid APRS API key', ('wrong', '201'): 'Wrong APRS API key', ('valid', '401'): 'Wrong DAPNET user or passwd', ('valid', '400'): 'Wrong callsign or txgroup', ('invalid', '401'): 'Invalid APRS API key/Wrong DAPNET user or passwd', ('wrong', '401'): 'Wrong APRS API key/Wrong DAPNET user or passwd', ('invalid', '400'): 'Invalid APRS API key/Wrong callsign or txgroup', ('wrong', '400'): 'Wrong APRS API key/Wrong callsign or txgroup', } if aprs_server == 'valid' and dapnet_server == '201': service_status = { "color_status": True, "status_message": check_cronjob() } elif aprs_server == 'valid' and dapnet_server not in ('201', '401', '400'): service_status = { "color_status": False, "status_message": "DAPNET HTTP" + dapnet_server } else: service_status = { "color_status": False, "status_message": status_messages.get((aprs_server, dapnet_server)) } return jsonify({"service_status": service_status}) a2d-2.0.5/a2d/routes/system.py000066400000000000000000000041001464731662700160640ustar00rootroot00000000000000import psutil from flask import Blueprint, jsonify from a2d.routes.auth import login_required system_routes = Blueprint('system', __name__) @system_routes.route('/system-info', methods=['GET']) @login_required def system_info(): style = 'style="color: #d4660c; font-weight: bold;"' #CPU temperature raw_cpu_temperature = get_cpu_temperature() if raw_cpu_temperature is not None: cpu_temperature_value = float(raw_cpu_temperature) formatted_cpu_temperature = f" {cpu_temperature_value:.2f}°C" if cpu_temperature_value > 80: temperature_style = style else: temperature_style = '' cpu_temperature = f'{formatted_cpu_temperature}' else: cpu_temperature = "Not available" #System memory raw_memory_usage = get_system_memory_usage() if raw_memory_usage is not None: memory_usage_value = float(raw_memory_usage) memory_usage = f" {memory_usage_value}%" if memory_usage_value > 80: memory_style = style else: memory_style = '' system_memory_usage = f'{memory_usage}' else: system_memory_usage = "Not available" #CPU load cpu_load = get_cpu_load() cpu_load = f" {cpu_load}%" system_data = { "cpu_temperature": cpu_temperature, "system_memory_usage": system_memory_usage, "cpu_load": cpu_load } return jsonify(system_data) def get_cpu_temperature(): try: with open('/sys/class/thermal/thermal_zone0/temp', 'r') as temp_file: temp_str = temp_file.read().strip() temp_milli_celsius = int(temp_str) temp_celsius = temp_milli_celsius / 1000.0 return temp_celsius except Exception as e: print("An error occurred:", e) return None def get_system_memory_usage(): memory = psutil.virtual_memory() return memory.percent def get_cpu_load(): load = psutil.cpu_percent(interval=1) return load a2d-2.0.5/a2d/runscripts.py000066400000000000000000000062531464731662700154460ustar00rootroot00000000000000#coding=utf-8 #!/usr/bin/env python3 import os import subprocess import time from a2d.a2d_utils.get_aprs import aprs_check from a2d.a2d_utils.a2d_utils import remove_cronjob, read_ini_data, write_ini_data runscript_data = '/etc/a2d/runscript_data.ini' def run_script(): config = read_ini_data(runscript_data) current_time = time.time() str_current_time = str(current_time) #Check when was the last APRS Check last_aprs_check_ini = config.get('APRSCheck', 'LastCheck', fallback='') if last_aprs_check_ini: last_aprs_check_ini = float(last_aprs_check_ini) last_check_interval = current_time - last_aprs_check_ini if last_check_interval >= 60 * 3: config.set('APRSCheck', 'APRSCheck', 'unverified') write_ini_data(config, runscript_data) aprs_check_ini = config.get('APRSCheck', 'APRSCheck', fallback='') if aprs_check_ini == 'unverified': config.set('APRSCheck', 'LastCheck', str_current_time) # Check the APRS API response for the current callsign and API key entries_check = aprs_check() if entries_check is not None and "code" in entries_check: code = entries_check["code"] if code == "apikey-wrong": # Write the key status in /etc/a2d/runscript_data.ini config.set('APRSKeyVerify', 'apiKeyStatus', 'wrong') write_ini_data(config, runscript_data) # Terminate the script if the API key is wrong remove_cronjob() return elif code == "apikey-invalid": # Write the key status in /etc/a2d/runscript_data.ini config.set('APRSKeyVerify', 'apiKeyStatus', 'invalid') write_ini_data(config, runscript_data) # Terminate the script if the API key is invalid remove_cronjob() return config.set('APRSKeyVerify', 'apiKeyStatus', 'valid') config.set('APRSCheck', 'APRSCheck', 'verified') write_ini_data(config, runscript_data) # Continue running the script to restrict aprs pull last_aprs_pull = config.get('APRSTimeStamp', 'LastAPRSPull', fallback='') if last_aprs_pull: last_aprs_pull = float(last_aprs_pull) time_difference = current_time - last_aprs_pull ideal_aprs_interval = 121 if time_difference <= ideal_aprs_interval: time_to_sleep = ideal_aprs_interval - time_difference time.sleep(time_to_sleep) # Continue running the script and write the start time #Write start time config.set('InternalTimeStamp', 'LastRunTime', str_current_time) write_ini_data(config, runscript_data) db_file_path = "/var/lib/a2d/dbs/aprs_messages.db" if os.path.exists(db_file_path): modules = ["a2d.build_usrdbs", "a2d.push_a2d"] else: modules = ["a2d.build_usrdbs", "a2d.pseudorun"] for module in modules: subprocess.call(["python3", "-m", module]) str_current_time2 = str(time.time()) config.set('APRSTimeStamp', 'LastAPRSPull', str_current_time2) write_ini_data(config, runscript_data) if __name__ == '__main__': run_script() a2d-2.0.5/a2d/static/000077500000000000000000000000001464731662700141415ustar00rootroot00000000000000a2d-2.0.5/a2d/static/bootstrap/000077500000000000000000000000001464731662700161565ustar00rootroot00000000000000a2d-2.0.5/a2d/static/bootstrap/css/000077500000000000000000000000001464731662700167465ustar00rootroot00000000000000a2d-2.0.5/a2d/static/bootstrap/css/bootstrap_docs_5-3.css000066400000000000000000001604351464731662700231020ustar00rootroot00000000000000/*! * Bootstrap Docs (https://getbootstrap.com/) * Copyright 2011-2023 The Bootstrap Authors * Licensed under the Creative Commons Attribution 3.0 Unported License. * For details, see https://creativecommons.org/licenses/by/3.0/. */ :root { --bs-breakpoint-xs: 0; --bs-breakpoint-sm: 576px; --bs-breakpoint-md: 768px; --bs-breakpoint-lg: 992px; --bs-breakpoint-xl: 1200px; --bs-breakpoint-xxl: 1400px; } .grid { display: grid; grid-template-rows: repeat(var(--bs-rows, 1), 1fr); grid-template-columns: repeat(var(--bs-columns, 12), 1fr); gap: var(--bs-gap, 1.5rem); } .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; } } :root, [data-bs-theme="light"] { --bd-purple: #4c0bce; --bd-violet: #712cf9; --bd-accent: #ffe484; --bd-violet-rgb: 112.520718, 44.062154, 249.437846; --bd-accent-rgb: 255, 228, 132; --bd-pink-rgb: 214, 51, 132; --bd-teal-rgb: 32, 201, 151; --bd-violet-bg: var(--bd-violet); --bd-toc-color: var(--bd-violet); --bd-sidebar-link-bg: rgba(var(--bd-violet-rgb), 0.1); --bd-callout-link: 10, 88, 202; --bd-callout-code-color: #ab296a; --bd-pre-bg: var(--bs-tertiary-bg); } [data-bs-theme="dark"] { --bd-violet: #9461fb; --bd-violet-bg: #712cf9; --bd-toc-color: var(--bs-emphasis-color); --bd-sidebar-link-bg: rgba(84, 33, 187, 0.5); --bd-callout-link: 110, 168, 254; --bd-callout-code-color: #e685b5; --bd-pre-bg: #1b1f22; } .bd-navbar { padding: 0.75rem 0; background-color: transparent; box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(255, 255, 255, 0.15); } .bd-navbar::after { position: absolute; inset: 0; z-index: -1; display: block; content: ""; background-image: linear-gradient( rgba(var(--bd-violet-rgb), 1), rgba(var(--bd-violet-rgb), 0.95) ); } @media (max-width: 991.98px) { .bd-navbar .bd-navbar-toggle { width: 4.25rem; } } .bd-navbar .navbar-toggler { padding: 0; margin-right: -0.5rem; border: 0; } .bd-navbar .navbar-toggler:first-child { margin-left: -0.5rem; } .bd-navbar .navbar-toggler .bi { width: 1.5rem; height: 1.5rem; } .bd-navbar .navbar-toggler:focus { box-shadow: none; } .bd-navbar .navbar-brand { color: #fff; transition: transform 0.2s ease-in-out; } @media (prefers-reduced-motion: reduce) { .bd-navbar .navbar-brand { transition: none; } } .bd-navbar .navbar-brand:hover { transform: rotate(-5deg) scale(1.1); } .bd-navbar .navbar-toggler, .bd-navbar .nav-link { padding-right: 0.25rem; padding-left: 0.25rem; color: rgba(255, 255, 255, 0.85); } .bd-navbar .navbar-toggler:hover, .bd-navbar .navbar-toggler:focus, .bd-navbar .nav-link:hover, .bd-navbar .nav-link:focus { color: #fff; } .bd-navbar .navbar-toggler.active, .bd-navbar .nav-link.active { font-weight: 600; color: #fff; } .bd-navbar .navbar-nav-svg { display: inline-block; vertical-align: -0.125rem; } .bd-navbar .offcanvas-lg { background-color: var(--bd-violet-bg); border-left: 0; } @media (max-width: 991.98px) { .bd-navbar .offcanvas-lg { box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175); } } .bd-navbar .dropdown-toggle:focus:not(:focus-visible) { outline: 0; } .bd-navbar .dropdown-menu { --bs-dropdown-min-width: 12rem; --bs-dropdown-padding-x: 0.25rem; --bs-dropdown-padding-y: 0.25rem; --bs-dropdown-link-hover-bg: rgba(var(--bd-violet-rgb), 0.1); --bs-dropdown-link-active-bg: rgba(var(--bd-violet-rgb), 1); --bs-dropdown-font-size: 0.875rem; font-size: 0.875rem; border-radius: 0.5rem; box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); } .bd-navbar .dropdown-menu li + li { margin-top: 0.125rem; } .bd-navbar .dropdown-menu .dropdown-item { border-radius: 0.25rem; } .bd-navbar .dropdown-menu .dropdown-item:active .bi { color: inherit !important; } .bd-navbar .dropdown-menu .active { font-weight: 600; } .bd-navbar .dropdown-menu .active .bi { display: block !important; } .bd-navbar .dropdown-menu-end { --bs-dropdown-min-width: 8rem; } [data-bs-theme="dark"] .bd-navbar { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(255, 255, 255, 0.15); } :root { --docsearch-primary-color: var(--bd-violet); --docsearch-logo-color: var(--bd-violet); } [data-bs-theme="dark"] { --docsearch-text-color: #f5f6f7; --docsearch-container-background: rgba(9, 10, 17, 0.8); --docsearch-modal-background: #15172a; --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309; --docsearch-searchbox-background: #090a11; --docsearch-searchbox-focus-background: #000; --docsearch-hit-color: #bec3c9; --docsearch-hit-shadow: none; --docsearch-hit-background: #090a11; --docsearch-key-gradient: linear-gradient(-26.5deg, #565872, #31355b); --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, 0.3); --docsearch-footer-background: #1e2136; --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5), 0 -4px 8px 0 rgba(0, 0, 0, 0.2); --docsearch-muted-color: #7f8497; } .bd-search { position: relative; } @media (min-width: 992px) { .bd-search { position: absolute; top: 0.875rem; left: 50%; width: 200px; margin-left: -100px; } } @media (min-width: 1200px) { .bd-search { width: 280px; margin-left: -140px; } } .DocSearch-Container { --docsearch-muted-color: var(--bs-secondary-color); --docsearch-hit-shadow: none; z-index: 2000; cursor: auto; } @media (min-width: 992px) { .DocSearch-Container { padding-top: 4rem; } } .DocSearch-Button { --docsearch-searchbox-background: rgba(0, 0, 0, 0.1); --docsearch-searchbox-color: #fff; --docsearch-searchbox-focus-background: rgba(0, 0, 0, 0.25); --docsearch-searchbox-shadow: 0 0 0 0.25rem rgba(255, 228, 132, 0.4); --docsearch-text-color: #fff; --docsearch-muted-color: rgba(255, 255, 255, 0.65); width: 100%; height: 38px; margin: 0; border: 1px solid rgba(255, 255, 255, 0.4); border-radius: 0.375rem; } .DocSearch-Button .DocSearch-Search-Icon { opacity: 0.65; } .DocSearch-Button:active, .DocSearch-Button:focus, .DocSearch-Button:hover { border-color: #ffe484; } .DocSearch-Button:active .DocSearch-Search-Icon, .DocSearch-Button:focus .DocSearch-Search-Icon, .DocSearch-Button:hover .DocSearch-Search-Icon { opacity: 1; } @media (max-width: 991.98px) { .DocSearch-Button, .DocSearch-Button:hover, .DocSearch-Button:focus { background: transparent; border: 0; box-shadow: none; } .DocSearch-Button:focus { box-shadow: var(--docsearch-searchbox-shadow); } } @media (max-width: 991.98px) { .DocSearch-Button-Keys, .DocSearch-Button-Placeholder { display: none; } } .DocSearch-Button-Keys { min-width: 0; padding: 0.125rem 0.25rem; background: rgba(0, 0, 0, 0.25); border-radius: 0.25rem; } .DocSearch-Button-Key { top: 0; width: auto; height: 1.25rem; padding-right: 0.125rem; padding-left: 0.125rem; margin-right: 0; font-size: 0.875rem; background: none; box-shadow: none; } .DocSearch-Commands-Key { padding-left: 1px; font-size: 0.875rem; background-color: rgba(0, 0, 0, 0.1); background-image: none; box-shadow: none; } .DocSearch-Form { border-radius: var(--bs-border-radius); } .DocSearch-Hits mark { padding: 0; } .DocSearch-Hit { padding-bottom: 0; border-radius: 0; } .DocSearch-Hit a { border-radius: 0; border: solid var(--bs-border-color); border-width: 0 1px 1px; } .DocSearch-Hit:first-child a { border-top-left-radius: var(--bs-border-radius); border-top-right-radius: var(--bs-border-radius); border-top-width: 1px; } .DocSearch-Hit:last-child a { border-bottom-right-radius: var(--bs-border-radius); border-bottom-left-radius: var(--bs-border-radius); } .DocSearch-Hit-icon { display: flex; align-items: center; } .DocSearch-Logo svg .cls-1, .DocSearch-Logo svg .cls-2 { fill: var(--docsearch-logo-color); } .bd-masthead { --bd-pink-rgb: 214, 51, 132; padding: 3rem 0; background-image: linear-gradient( 180deg, rgba(var(--bs-body-bg-rgb), 0.01), rgba(var(--bs-body-bg-rgb), 1) 85% ), radial-gradient( ellipse at top left, rgba(var(--bs-primary-rgb), 0.5), transparent 50% ), radial-gradient( ellipse at top right, rgba(var(--bd-accent-rgb), 0.5), transparent 50% ), radial-gradient( ellipse at center right, rgba(var(--bd-violet-rgb), 0.5), transparent 50% ), radial-gradient( ellipse at center left, rgba(var(--bd-pink-rgb), 0.5), transparent 50% ); } .bd-masthead h1 { --bs-heading-color: var(--bs-emphasis-color); font-size: calc(1.525rem + 3.3vw); } @media (min-width: 1200px) { .bd-masthead h1 { font-size: 4rem; } } .bd-masthead .lead { font-size: 1rem; font-weight: 400; color: var(--bs-secondary-color); } .bd-masthead .bd-code-snippet { margin: 0; border-color: var(--bs-border-color-translucent); border-width: 1px; border-radius: 0.5rem; } .bd-masthead .highlight { width: 100%; padding: 0.5rem 1rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; background-color: rgba(var(--bs-body-color-rgb), 0.075); border-radius: calc(0.5rem - 1px); } @media (min-width: 992px) { .bd-masthead .highlight { padding-right: 4rem; } } .bd-masthead .highlight pre { padding: 0; margin: 0.625rem 0; overflow: hidden; } .bd-masthead .btn-clipboard { position: absolute; top: -0.625rem; right: 0; background-color: transparent; } .bd-masthead #carbonads { margin-inline: auto; } @media (min-width: 768px) { .bd-masthead .lead { font-size: calc(1.275rem + 0.3vw); } } @media (min-width: 768px) and (min-width: 1200px) { .bd-masthead .lead { font-size: 1.5rem; } } .masthead-followup h2, .masthead-followup h3, .masthead-followup h4 { --bs-heading-color: var(--bs-emphasis-color); } .masthead-followup .lead { font-size: 1rem; } @media (min-width: 768px) { .masthead-followup .lead { font-size: 1.25rem; } } .masthead-followup-icon { padding: 1rem; color: rgba(var(--bg-rgb), 1); background-color: rgba(var(--bg-rgb), 0.1); background-blend-mode: multiple; border-radius: 1rem; mix-blend-mode: darken; } .masthead-followup-icon svg { filter: drop-shadow(0 1px 1px var(--bs-body-bg)); } .masthead-notice { background-color: var(--bd-accent); box-shadow: inset 0 -1px 1px rgba(var(--bs-body-color-rgb), 0.15), 0 0.25rem 1.5rem rgba(var(--bs-body-bg-rgb), 0.75); } .animate-img > img { transition: transform 0.2s ease-in-out; } @media (prefers-reduced-motion: reduce) { .animate-img > img { transition: none; } } .animate-img:hover > img { transform: scale(1.1); } [data-bs-theme="dark"] .masthead-followup-icon { mix-blend-mode: lighten; } #carbonads { position: static; display: block; max-width: 400px; padding: 15px 15px 15px 160px; margin: 2rem 0; overflow: hidden; font-size: 0.8125rem; line-height: 1.4; text-align: left; background-color: var(--bs-tertiary-bg); } #carbonads a { color: var(--bs-body-color); text-decoration: none; } @media (min-width: 576px) { #carbonads { border-radius: 0.5rem; } } .carbon-img { float: left; margin-left: -145px; } .carbon-poweredby { display: block; margin-top: 0.75rem; color: var(--bs-body-color) !important; } .bd-content > h2, .bd-content > h3, .bd-content > h4 { --bs-heading-color: var(--bs-emphasis-color); } .bd-content > h2:not(:first-child) { margin-top: 3rem; } .bd-content > h3 { margin-top: 2rem; } .bd-content > ul li, .bd-content > ol li { margin-bottom: 0.25rem; } .bd-content > ul li > p ~ ul, .bd-content > ol li > p ~ ul { margin-top: -0.5rem; margin-bottom: 1rem; } .bd-content > .table, .bd-content > .table-responsive .table { --bs-table-border-color: var(--bs-border-color); max-width: 100%; margin-bottom: 1.5rem; font-size: 0.875rem; } @media (max-width: 991.98px) { .bd-content > .table.table-bordered, .bd-content > .table-responsive .table.table-bordered { border: 0; } } .bd-content > .table thead, .bd-content > .table-responsive .table thead { border-bottom: 2px solid currentcolor; } .bd-content > .table tbody:not(:first-child), .bd-content > .table-responsive .table tbody:not(:first-child) { border-top: 2px solid currentcolor; } .bd-content > .table th:first-child, .bd-content > .table td:first-child, .bd-content > .table-responsive .table th:first-child, .bd-content > .table-responsive .table td:first-child { padding-left: 0; } .bd-content > .table th:not(:last-child), .bd-content > .table td:not(:last-child), .bd-content > .table-responsive .table th:not(:last-child), .bd-content > .table-responsive .table td:not(:last-child) { padding-right: 1.5rem; } .bd-content > .table th, .bd-content > .table-responsive .table th { color: var(--bs-emphasis-color); } .bd-content > .table strong, .bd-content > .table-responsive .table strong { color: var(--bs-emphasis-color); } .bd-content > .table th, .bd-content > .table td:first-child > code, .bd-content > .table-responsive .table th, .bd-content > .table-responsive .table td:first-child > code { white-space: nowrap; } .table-options td:nth-child(2) { min-width: 160px; } .table-options td:last-child, .table-utilities td:last-child { min-width: 280px; } .table-swatches th { color: var(--bs-emphasis-color); } .table-swatches td code { white-space: nowrap; } .bd-title { --bs-heading-color: var(--bs-emphasis-color); font-size: calc(1.425rem + 2.1vw); } @media (min-width: 1200px) { .bd-title { font-size: 3rem; } } .bd-lead { font-size: calc(1.275rem + 0.3vw); font-weight: 300; } @media (min-width: 1200px) { .bd-lead { font-size: 1.5rem; } } .bi { width: 1em; height: 1em; vertical-align: -0.125em; fill: currentcolor; } @media (min-width: 992px) { .border-lg-start { border-left: var(--bs-border-width) solid var(--bs-border-color); } } .bd-summary-link { color: var(--bs-link-color); } .bd-summary-link:hover, details[open] > .bd-summary-link { color: var(--bs-link-hover-color); } [data-bs-theme="blue"] { --bs-body-color: var(--bs-white); --bs-body-color-rgb: 255, 255, 255; --bs-body-bg: var(--bs-blue); --bs-body-bg-rgb: 13, 110, 253; --bs-tertiary-bg: #0a58ca; } [data-bs-theme="blue"] .dropdown-menu { --bs-dropdown-bg: #0c63e4; --bs-dropdown-link-active-bg: #084298; } [data-bs-theme="blue"] .btn-secondary { --bs-btn-bg: #3d8bfc; --bs-btn-border-color: rgba(255, 255, 255, 0.25); --bs-btn-hover-bg: #247cfc; --bs-btn-hover-border-color: rgba(255, 255, 255, 0.25); --bs-btn-active-bg: #0b6dfb; --bs-btn-active-border-color: rgba(255, 255, 255, 0.5); --bs-btn-focus-border-color: rgba(255, 255, 255, 0.5); --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(255, 255, 255, 0.2); } .skippy { background-color: #4c0bce; } .skippy a { color: #fff; } @media (min-width: 992px) { .bd-sidebar { position: -webkit-sticky; position: sticky; top: 5rem; display: block !important; height: calc(100vh - 6rem); padding-left: 0.25rem; margin-left: -0.25rem; overflow-y: auto; } } @media (max-width: 991.98px) { .bd-sidebar .offcanvas-lg { border-right-color: var(--bs-border-color); box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175); } } .bd-links-heading { color: var(--bs-emphasis-color); } @media (max-width: 991.98px) { .bd-links-nav { font-size: 0.875rem; } } @media (max-width: 991.98px) { .bd-links-nav { -moz-column-count: 2; column-count: 2; -moz-column-gap: 1.5rem; column-gap: 1.5rem; } .bd-links-nav .bd-links-group { -moz-column-break-inside: avoid; break-inside: avoid; } .bd-links-nav .bd-links-span-all { -moz-column-span: all; column-span: all; } } .bd-links-link { padding: 0.1875rem 0.5rem; margin-top: 0.125rem; margin-left: 1.125rem; color: var(--bs-body-color); text-decoration: none; } .bd-links-link:hover, .bd-links-link:focus, .bd-links-link.active { color: var(--bs-emphasis-color); background-color: var(--bd-sidebar-link-bg); } .bd-links-link.active { font-weight: 600; } .bd-gutter { --bs-gutter-x: 3rem; } @media (min-width: 992px) { .bd-layout { display: grid; grid-template-areas: "sidebar main"; grid-template-columns: 1fr 5fr; gap: 1.5rem; } } .bd-sidebar { grid-area: sidebar; } .bd-main { grid-area: main; } @media (max-width: 991.98px) { .bd-main { max-width: 760px; margin-inline: auto; } } @media (min-width: 768px) { .bd-main { display: grid; grid-template-areas: "intro" "toc" "content"; grid-template-rows: auto auto 1fr; gap: inherit; } } @media (min-width: 992px) { .bd-main { grid-template-areas: "intro toc" "content toc"; grid-template-rows: auto 1fr; grid-template-columns: 4fr 1fr; } } .bd-intro { grid-area: intro; } .bd-toc { grid-area: toc; } .bd-content { grid-area: content; min-width: 1px; } @media (min-width: 992px) { .bd-toc { position: -webkit-sticky; position: sticky; top: 5rem; right: 0; z-index: 2; height: calc(100vh - 7rem); overflow-y: auto; } } .bd-toc nav { font-size: 0.875rem; } .bd-toc nav ul { padding-left: 0; margin-bottom: 0; list-style: none; } .bd-toc nav ul ul { padding-left: 1rem; } .bd-toc nav a { display: block; padding: 0.125rem 0 0.125rem 0.75rem; color: inherit; text-decoration: none; border-left: 0.125rem solid transparent; } .bd-toc nav a:hover, .bd-toc nav a.active { color: var(--bd-toc-color); border-left-color: var(--bd-toc-color); } .bd-toc nav a.active { font-weight: 500; } .bd-toc nav a code { font: inherit; } .bd-toc-toggle { display: flex; align-items: center; } @media (max-width: 575.98px) { .bd-toc-toggle { justify-content: space-between; width: 100%; } } @media (max-width: 767.98px) { .bd-toc-toggle { color: var(--bs-body-color); border: 1px solid var(--bs-border-color); border-radius: var(--bs-border-radius); } .bd-toc-toggle:hover, .bd-toc-toggle:focus, .bd-toc-toggle:active, .bd-toc-toggle[aria-expanded="true"] { color: var(--bd-violet); background-color: var(--bs-body-bg); border-color: var(--bd-violet); } .bd-toc-toggle:focus, .bd-toc-toggle[aria-expanded="true"] { box-shadow: 0 0 0 3px rgba(var(--bd-violet-rgb), 0.25); } } @media (max-width: 767.98px) { .bd-toc-collapse nav { padding: 1.25rem 1.25rem 1.25rem 1rem; background-color: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); border-radius: var(--bs-border-radius); } } @media (min-width: 768px) { .bd-toc-collapse { display: block !important; } } .bd-footer a { color: var(--bs-body-color); text-decoration: none; } .bd-footer a:hover, .bd-footer a:focus { color: var(--bs-link-hover-color); text-decoration: underline; } .bd-code-snippet { margin: 0 -1.5rem 1rem; border: solid var(--bs-border-color); border-width: 1px 0; } @media (min-width: 768px) { .bd-code-snippet { margin-right: 0; margin-left: 0; border-width: 1px; border-radius: var(--bs-border-radius); } } .bd-example { --bd-example-padding: 1rem; position: relative; padding: var(--bd-example-padding); margin: 0 -1.5rem 1rem; border: solid var(--bs-border-color); border-width: 1px 0; } .bd-example::after { display: block; clear: both; content: ""; } @media (min-width: 768px) { .bd-example { --bd-example-padding: 1.5rem; margin-right: 0; margin-left: 0; border-width: 1px; border-radius: var(--bs-border-radius); } } .bd-example + p { margin-top: 2rem; } .bd-example > .form-control + .form-control { margin-top: 0.5rem; } .bd-example > .nav + .nav, .bd-example > .alert + .alert, .bd-example > .navbar + .navbar, .bd-example > .progress + .progress { margin-top: 1rem; } .bd-example > .dropdown-menu { position: static; display: block; } .bd-example > :last-child, .bd-example > nav:last-child .breadcrumb { margin-bottom: 0; } .bd-example > hr:last-child { margin-bottom: 1rem; } .bd-example > svg + svg, .bd-example > img + img { margin-left: 0.5rem; } .bd-example > .btn, .bd-example > .btn-group { margin: 0.25rem 0.125rem; } .bd-example > .btn-toolbar + .btn-toolbar { margin-top: 0.5rem; } .bd-example > .list-group { max-width: 400px; } .bd-example > [class*="list-group-horizontal"] { max-width: 100%; } .bd-example .fixed-top, .bd-example .sticky-top { position: static; margin: calc(var(--bd-example-padding) * -1) calc(var(--bd-example-padding) * -1) var(--bd-example-padding); } .bd-example .fixed-bottom, .bd-example .sticky-bottom { position: static; margin: var(--bd-example-padding) calc(var(--bd-example-padding) * -1) calc(var(--bd-example-padding) * -1); } .bd-example .pagination { margin-bottom: 0; } .bd-example-row [class^="col"], .bd-example-cols [class^="col"] > *, .bd-example-cssgrid [class*="grid"] > * { padding-top: 0.75rem; padding-bottom: 0.75rem; background-color: rgba(var(--bd-violet-rgb), 0.15); border: 1px solid rgba(var(--bd-violet-rgb), 0.3); } .bd-example-row .row + .row, .bd-example-cssgrid .grid + .grid { margin-top: 1rem; } .bd-example-row-flex-cols .row { min-height: 10rem; background-color: rgba(var(--bd-violet-rgb), 0.15); } .bd-example-flex div:not(.vr) { background-color: rgba(var(--bd-violet-rgb), 0.15); border: 1px solid rgba(var(--bd-violet-rgb), 0.3); } .example-container { width: 800px; --bs-gutter-x: 1.5rem; --bs-gutter-y: 0; width: 100%; padding-right: calc(var(--bs-gutter-x) * 0.5); padding-left: calc(var(--bs-gutter-x) * 0.5); margin-right: auto; margin-left: auto; } .example-row { --bs-gutter-x: 1.5rem; --bs-gutter-y: 0; display: flex; flex-wrap: wrap; margin-top: calc(-1 * var(--bs-gutter-y)); margin-right: calc(-0.5 * var(--bs-gutter-x)); margin-left: calc(-0.5 * var(--bs-gutter-x)); } .example-content-main { flex-shrink: 0; width: 100%; max-width: 100%; padding-right: calc(var(--bs-gutter-x) * 0.5); padding-left: calc(var(--bs-gutter-x) * 0.5); margin-top: var(--bs-gutter-y); } @media (min-width: 576px) { .example-content-main { flex: 0 0 auto; width: 50%; } } @media (min-width: 992px) { .example-content-main { flex: 0 0 auto; width: 66.666667%; } } .example-content-secondary { flex-shrink: 0; width: 100%; max-width: 100%; padding-right: calc(var(--bs-gutter-x) * 0.5); padding-left: calc(var(--bs-gutter-x) * 0.5); margin-top: var(--bs-gutter-y); } @media (min-width: 576px) { .example-content-secondary { flex: 0 0 auto; width: 50%; } } @media (min-width: 992px) { .example-content-secondary { flex: 0 0 auto; width: 33.333333%; } } .bd-example-ratios .ratio { display: inline-block; width: 10rem; color: var(--bs-secondary-color); background-color: var(--bs-tertiary-bg); border: var(--bs-border-width) solid var(--bs-border-color); } .bd-example-ratios .ratio > div { display: flex; align-items: center; justify-content: center; } .bd-example-ratios-breakpoint .ratio-4x3 { width: 16rem; } @media (min-width: 768px) { .bd-example-ratios-breakpoint .ratio-4x3 { --bs-aspect-ratio: 50%; } } .bd-example-offcanvas .offcanvas { position: static; display: block; height: 200px; visibility: visible; transform: translate(0); } .tooltip-demo a { white-space: nowrap; } .tooltip-demo .btn { margin: 0.25rem 0.125rem; } .custom-tooltip { --bs-tooltip-bg: var(--bd-violet-bg); --bs-tooltip-color: var(--bs-white); } .custom-popover { --bs-popover-max-width: 200px; --bs-popover-border-color: var(--bd-violet-bg); --bs-popover-header-bg: var(--bd-violet-bg); --bs-popover-header-color: var(--bs-white); --bs-popover-body-padding-x: 1rem; --bs-popover-body-padding-y: 0.5rem; } .scrollspy-example { height: 200px; margin-top: 0.5rem; overflow: auto; } .scrollspy-example-2 { height: 350px; overflow: auto; } .simple-list-example-scrollspy .active { background-color: rgba(var(--bd-violet-rgb), 0.15); } .bd-example-border-utils [class^="border"] { display: inline-block; width: 5rem; height: 5rem; margin: 0.25rem; background-color: var(--bs-tertiary-bg); } .bd-example-rounded-utils [class*="rounded"] { margin: 0.25rem; } .bd-example-position-utils { position: relative; padding: 2rem; } .bd-example-position-utils .position-relative { height: 200px; background-color: var(--bs-tertiary-bg); } .bd-example-position-utils .position-absolute { width: 2rem; height: 2rem; background-color: var(--bs-body-color); border-radius: 0.375rem; } .bd-example-position-examples::after { content: none; } .bd-example-placeholder-cards::after { display: none; } .bd-example-placeholder-cards .card { width: 18rem; } .bd-example-toasts { min-height: 240px; } .bd-example-zindex-levels { min-height: 15rem; } .bd-example-zindex-levels > div { color: var(--bs-body-bg); background-color: var(--bd-violet); border: 1px solid var(--bd-purple); } .bd-example-zindex-levels > div > span { position: absolute; right: 5px; bottom: 0; } .bd-example-zindex-levels > :nth-child(2) { top: 3rem; left: 3rem; } .bd-example-zindex-levels > :nth-child(3) { top: 4.5rem; left: 4.5rem; } .bd-example-zindex-levels > :nth-child(4) { top: 6rem; left: 6rem; } .bd-example-zindex-levels > :nth-child(5) { top: 7.5rem; left: 7.5rem; } .highlight { position: relative; padding: 0.75rem 1.5rem; background-color: var(--bd-pre-bg); } @media (min-width: 768px) { .highlight { padding: 0.75rem 1.25rem; border-radius: calc(var(--bs-border-radius) - 1px); } } @media (min-width: 992px) { .highlight pre { margin-right: 1.875rem; } } .highlight pre { padding: 0.25rem 0 0.875rem; margin-top: 0.8125rem; margin-bottom: 0; overflow: overlay; white-space: pre; background-color: transparent; border: 0; } .highlight pre code { font-size: inherit; color: var(--bs-body-color); word-wrap: normal; } .highlight-toolbar { background-color: var(--bd-pre-bg); } .highlight-toolbar + .highlight { border-top-left-radius: 0; border-top-right-radius: 0; } @media (min-width: 768px) { .bd-file-ref .highlight-toolbar { border-top-left-radius: calc(var(--bs-border-radius) - 1px); border-top-right-radius: calc(var(--bs-border-radius) - 1px); } } .bd-content .bd-code-snippet { margin-bottom: 1rem; } .btn-bd-primary { --bs-btn-font-weight: 600; --bs-btn-color: var(--bs-white); --bs-btn-bg: var(--bd-violet-bg); --bs-btn-border-color: var(--bd-violet-bg); --bs-btn-hover-color: var(--bs-white); --bs-btn-hover-bg: #6528e0; --bs-btn-hover-border-color: #6528e0; --bs-btn-focus-shadow-rgb: var(--bd-violet-rgb); --bs-btn-active-color: var(--bs-btn-hover-color); --bs-btn-active-bg: #5a23c8; --bs-btn-active-border-color: #5a23c8; } .btn-bd-accent { --bs-btn-font-weight: 600; --bs-btn-color: var(--bd-accent); --bs-btn-border-color: var(--bd-accent); --bs-btn-hover-color: var(--bd-dark); --bs-btn-hover-bg: var(--bd-accent); --bs-btn-hover-border-color: var(--bd-accent); --bs-btn-focus-shadow-rgb: var(--bd-accent-rgb); --bs-btn-active-color: var(--bs-btn-hover-color); --bs-btn-active-bg: var(--bs-btn-hover-bg); --bs-btn-active-border-color: var(--bs-btn-hover-border-color); } .btn-bd-light { --btn-custom-color: #9461fb; --bs-btn-color: var(--bs-gray-600); --bs-btn-border-color: var(--bs-border-color); --bs-btn-hover-color: var(--btn-custom-color); --bs-btn-hover-border-color: var(--btn-custom-color); --bs-btn-active-color: var(--btn-custom-color); --bs-btn-active-bg: var(--bs-white); --bs-btn-active-border-color: var(--btn-custom-color); --bs-btn-focus-border-color: var(--btn-custom-color); --bs-btn-focus-shadow-rgb: var(--bd-violet-rgb); } .bd-btn-lg { --bs-btn-border-radius: 0.5rem; padding: 0.8125rem 2rem; } .bd-callout { --bs-link-color-rgb: var(--bd-callout-link); --bs-code-color: var(--bd-callout-code-color); padding: 1.25rem; margin-top: 1.25rem; margin-bottom: 1.25rem; color: var(--bd-callout-color, inherit); background-color: var(--bd-callout-bg, var(--bs-gray-100)); border-left: 0.25rem solid var(--bd-callout-border, var(--bs-gray-300)); } .bd-callout h4 { margin-bottom: 0.25rem; } .bd-callout > :last-child { margin-bottom: 0; } .bd-callout + .bd-callout { margin-top: -0.25rem; } .bd-callout .highlight { background-color: rgba(0, 0, 0, 0.05); } .bd-callout-info { --bd-callout-color: var(--bs-info-text-emphasis); --bd-callout-bg: var(--bs-info-bg-subtle); --bd-callout-border: var(--bs-info-border-subtle); } .bd-callout-warning { --bd-callout-color: var(--bs-warning-text-emphasis); --bd-callout-bg: var(--bs-warning-bg-subtle); --bd-callout-border: var(--bs-warning-border-subtle); } .bd-callout-danger { --bd-callout-color: var(--bs-danger-text-emphasis); --bd-callout-bg: var(--bs-danger-bg-subtle); --bd-callout-border: var(--bs-danger-border-subtle); } .bd-brand-logos { color: #712cf9; } .bd-brand-logos .inverse { color: #fff; background-color: #712cf9; } .bd-brand-item + .bd-brand-item { border-top: 1px solid var(--bs-border-color); } @media (min-width: 768px) { .bd-brand-item + .bd-brand-item { border-top: 0; border-left: 1px solid var(--bs-border-color); } } .color-swatches { margin: 0 -5px; } .color-swatches .bd-purple { background-color: #4c0bce; } .color-swatches .bd-purple-light { background-color: #d5c1fd; } .color-swatches .bd-purple-lighter { background-color: #e5e1ea; } .color-swatches .bd-gray { background-color: #f9f9f9; } .color-swatch { width: 4rem; height: 4rem; } @media (min-width: 768px) { .color-swatch { width: 6rem; height: 6rem; } } .swatch-blue { color: #fff; background-color: #0d6efd; } .swatch-blue::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "4.50" "\a""4.50" "\a""4.66"; background-color: #0d6efd; background-image: linear-gradient( to bottom, transparent 0.25rem, #fff 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-indigo { color: #fff; background-color: #6610f2; } .swatch-indigo::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "7.18" "\a""7.18" "\a""2.92"; background-color: #6610f2; background-image: linear-gradient( to bottom, transparent 0.25rem, #fff 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-purple { color: #fff; background-color: #6f42c1; } .swatch-purple::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "6.51" "\a""6.51" "\a""3.22"; background-color: #6f42c1; background-image: linear-gradient( to bottom, transparent 0.25rem, #fff 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-pink { color: #fff; background-color: #d63384; } .swatch-pink::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "4.50" "\a""4.50" "\a""4.66"; background-color: #d63384; background-image: linear-gradient( to bottom, transparent 0.25rem, #fff 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-red { color: #fff; background-color: #dc3545; } .swatch-red::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "4.52" "\a""4.52" "\a""4.63"; background-color: #dc3545; background-image: linear-gradient( to bottom, transparent 0.25rem, #fff 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-orange { color: #000; background-color: #fd7e14; } .swatch-orange::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "8.17" "\a""2.57" "\a""8.17"; background-color: #fd7e14; background-image: linear-gradient( to bottom, transparent 0.25rem, #000 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-yellow { color: #000; background-color: #ffc107; } .swatch-yellow::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "12.8" "\a""1.63" "\a""12.8"; background-color: #ffc107; background-image: linear-gradient( to bottom, transparent 0.25rem, #000 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-green { color: #fff; background-color: #198754; } .swatch-green::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "4.53" "\a""4.53" "\a""4.63"; background-color: #198754; background-image: linear-gradient( to bottom, transparent 0.25rem, #fff 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-teal { color: #000; background-color: #20c997; } .swatch-teal::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "9.86" "\a""2.12" "\a""9.86"; background-color: #20c997; background-image: linear-gradient( to bottom, transparent 0.25rem, #000 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-cyan { color: #000; background-color: #0dcaf0; } .swatch-cyan::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "10.7" "\a""1.95" "\a""10.7"; background-color: #0dcaf0; background-image: linear-gradient( to bottom, transparent 0.25rem, #000 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-black { color: #fff; background-color: #000; } .swatch-black::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "21" "\a""21" "\a""1"; background-color: #000; background-image: linear-gradient( to bottom, transparent 0.25rem, #fff 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-white { color: #000; background-color: #fff; } .swatch-white::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "21" "\a""1" "\a""21"; background-color: #fff; background-image: linear-gradient( to bottom, transparent 0.25rem, #000 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-gray { color: #fff; background-color: #6c757d; } .swatch-gray::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "4.68" "\a""4.68" "\a""4.47"; background-color: #6c757d; background-image: linear-gradient( to bottom, transparent 0.25rem, #fff 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-gray-dark { color: #fff; background-color: #343a40; } .swatch-gray-dark::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "11.5" "\a""11.5" "\a""1.82"; background-color: #343a40; background-image: linear-gradient( to bottom, transparent 0.25rem, #fff 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .swatch-gray-500 { color: #000; background-color: #adb5bd; } .swatch-gray-500::after { position: absolute; top: 1rem; right: 1rem; padding-left: 1rem; font-size: 0.75rem; line-height: 1.35; white-space: pre; content: "10.1" "\a""2.07" "\a""10.1"; background-color: #adb5bd; background-image: linear-gradient( to bottom, transparent 0.25rem, #000 0.25rem 0.75rem, transparent 0.75rem 1.25rem, #fff 1.25rem 1.75rem, transparent 1.75rem 2.25rem, #000 2.25rem 2.75rem, transparent 2.75rem ); background-repeat: no-repeat; background-size: 0.5rem 100%; } .bd-blue-100 { color: #000; background-color: #cfe2ff; } .bd-blue-200 { color: #000; background-color: #9ec5fe; } .bd-blue-300 { color: #000; background-color: #6ea8fe; } .bd-blue-400 { color: #000; background-color: #3d8bfd; } .bd-blue-500 { color: #fff; background-color: #0d6efd; } .bd-blue-600 { color: #fff; background-color: #0a58ca; } .bd-blue-700 { color: #fff; background-color: #084298; } .bd-blue-800 { color: #fff; background-color: #052c65; } .bd-blue-900 { color: #fff; background-color: #031633; } .bd-indigo-100 { color: #000; background-color: #e0cffc; } .bd-indigo-200 { color: #000; background-color: #c29ffa; } .bd-indigo-300 { color: #000; background-color: #a370f7; } .bd-indigo-400 { color: #fff; background-color: #8540f5; } .bd-indigo-500 { color: #fff; background-color: #6610f2; } .bd-indigo-600 { color: #fff; background-color: #520dc2; } .bd-indigo-700 { color: #fff; background-color: #3d0a91; } .bd-indigo-800 { color: #fff; background-color: #290661; } .bd-indigo-900 { color: #fff; background-color: #140330; } .bd-purple-100 { color: #000; background-color: #e2d9f3; } .bd-purple-200 { color: #000; background-color: #c5b3e6; } .bd-purple-300 { color: #000; background-color: #a98eda; } .bd-purple-400 { color: #000; background-color: #8c68cd; } .bd-purple-500 { color: #fff; background-color: #6f42c1; } .bd-purple-600 { color: #fff; background-color: #59359a; } .bd-purple-700 { color: #fff; background-color: #432874; } .bd-purple-800 { color: #fff; background-color: #2c1a4d; } .bd-purple-900 { color: #fff; background-color: #160d27; } .bd-pink-100 { color: #000; background-color: #f7d6e6; } .bd-pink-200 { color: #000; background-color: #efadce; } .bd-pink-300 { color: #000; background-color: #e685b5; } .bd-pink-400 { color: #000; background-color: #de5c9d; } .bd-pink-500 { color: #fff; background-color: #d63384; } .bd-pink-600 { color: #fff; background-color: #ab296a; } .bd-pink-700 { color: #fff; background-color: #801f4f; } .bd-pink-800 { color: #fff; background-color: #561435; } .bd-pink-900 { color: #fff; background-color: #2b0a1a; } .bd-red-100 { color: #000; background-color: #f8d7da; } .bd-red-200 { color: #000; background-color: #f1aeb5; } .bd-red-300 { color: #000; background-color: #ea868f; } .bd-red-400 { color: #000; background-color: #e35d6a; } .bd-red-500 { color: #fff; background-color: #dc3545; } .bd-red-600 { color: #fff; background-color: #b02a37; } .bd-red-700 { color: #fff; background-color: #842029; } .bd-red-800 { color: #fff; background-color: #58151c; } .bd-red-900 { color: #fff; background-color: #2c0b0e; } .bd-orange-100 { color: #000; background-color: #ffe5d0; } .bd-orange-200 { color: #000; background-color: #fecba1; } .bd-orange-300 { color: #000; background-color: #feb272; } .bd-orange-400 { color: #000; background-color: #fd9843; } .bd-orange-500 { color: #000; background-color: #fd7e14; } .bd-orange-600 { color: #000; background-color: #ca6510; } .bd-orange-700 { color: #fff; background-color: #984c0c; } .bd-orange-800 { color: #fff; background-color: #653208; } .bd-orange-900 { color: #fff; background-color: #331904; } .bd-yellow-100 { color: #000; background-color: #fff3cd; } .bd-yellow-200 { color: #000; background-color: #ffe69c; } .bd-yellow-300 { color: #000; background-color: #ffda6a; } .bd-yellow-400 { color: #000; background-color: #ffcd39; } .bd-yellow-500 { color: #000; background-color: #ffc107; } .bd-yellow-600 { color: #000; background-color: #cc9a06; } .bd-yellow-700 { color: #000; background-color: #997404; } .bd-yellow-800 { color: #fff; background-color: #664d03; } .bd-yellow-900 { color: #fff; background-color: #332701; } .bd-green-100 { color: #000; background-color: #d1e7dd; } .bd-green-200 { color: #000; background-color: #a3cfbb; } .bd-green-300 { color: #000; background-color: #75b798; } .bd-green-400 { color: #000; background-color: #479f76; } .bd-green-500 { color: #fff; background-color: #198754; } .bd-green-600 { color: #fff; background-color: #146c43; } .bd-green-700 { color: #fff; background-color: #0f5132; } .bd-green-800 { color: #fff; background-color: #0a3622; } .bd-green-900 { color: #fff; background-color: #051b11; } .bd-teal-100 { color: #000; background-color: #d2f4ea; } .bd-teal-200 { color: #000; background-color: #a6e9d5; } .bd-teal-300 { color: #000; background-color: #79dfc1; } .bd-teal-400 { color: #000; background-color: #4dd4ac; } .bd-teal-500 { color: #000; background-color: #20c997; } .bd-teal-600 { color: #000; background-color: #1aa179; } .bd-teal-700 { color: #fff; background-color: #13795b; } .bd-teal-800 { color: #fff; background-color: #0d503c; } .bd-teal-900 { color: #fff; background-color: #06281e; } .bd-cyan-100 { color: #000; background-color: #cff4fc; } .bd-cyan-200 { color: #000; background-color: #9eeaf9; } .bd-cyan-300 { color: #000; background-color: #6edff6; } .bd-cyan-400 { color: #000; background-color: #3dd5f3; } .bd-cyan-500 { color: #000; background-color: #0dcaf0; } .bd-cyan-600 { color: #000; background-color: #0aa2c0; } .bd-cyan-700 { color: #fff; background-color: #087990; } .bd-cyan-800 { color: #fff; background-color: #055160; } .bd-cyan-900 { color: #fff; background-color: #032830; } .bd-gray-100 { color: #000; background-color: #f8f9fa; } .bd-gray-200 { color: #000; background-color: #e9ecef; } .bd-gray-300 { color: #000; background-color: #dee2e6; } .bd-gray-400 { color: #000; background-color: #ced4da; } .bd-gray-500 { color: #000; background-color: #adb5bd; } .bd-gray-600 { color: #fff; background-color: #6c757d; } .bd-gray-700 { color: #fff; background-color: #495057; } .bd-gray-800 { color: #fff; background-color: #343a40; } .bd-gray-900 { color: #fff; background-color: #212529; } .bd-white { color: #000; background-color: #fff; } .bd-black { color: #fff; background-color: #000; } .bd-clipboard, .bd-edit { position: relative; display: none; float: right; } .bd-clipboard + .highlight, .bd-edit + .highlight { margin-top: 0; } @media (min-width: 768px) { .bd-clipboard, .bd-edit { display: block; } } .btn-clipboard, .btn-edit { display: block; padding: 0.5em; line-height: 1; color: var(--bs-body-color); background-color: var(--bd-pre-bg); border: 0; border-radius: 0.25rem; } .btn-clipboard:hover, .btn-edit:hover { color: var(--bs-link-hover-color); } .btn-clipboard:focus, .btn-edit:focus { z-index: 3; } .btn-clipboard { position: relative; z-index: 2; margin-top: 1.25rem; margin-right: 0.75rem; } .bd-placeholder-img { font-size: 1.125rem; -webkit-user-select: none; -moz-user-select: none; user-select: none; text-anchor: middle; } .bd-placeholder-img-lg { font-size: calc(1.475rem + 2.7vw); } @media (min-width: 1200px) { .bd-placeholder-img-lg { font-size: 3.5rem; } } main a, main button, main h2, main h3, main h4, main [tabindex="0"] { scroll-margin-top: 80px; scroll-margin-bottom: 100px; } :root, [data-bs-theme="light"] { --base02: #c8c8fa; --base03: #565c64; --base04: #666; --base05: #333; --base06: #fff; --base07: #13795b; --base08: #c6303e; --base09: #087990; --base0A: #6f42c1; --base0B: #084298; --base0C: #084298; --base0D: #6f42c1; --base0E: #ab296a; --base0F: #333; } [data-bs-theme="dark"] { --base02: #3e4451; --base03: #868e96; --base04: #868e96; --base05: #abb2bf; --base06: #b6bdca; --base07: #feb272; --base08: #6edff6; --base09: #feb272; --base0A: #ffe69c; --base0B: #79dfc1; --base0C: #79dfc1; --base0D: #6ea8fe; --base0E: #c29ffa; --base0F: #ea868f; } [data-bs-theme="dark"] .language-diff .gd { color: #e35d6a; } [data-bs-theme="dark"] .language-diff .gi { color: #479f76; } .hl { background-color: var(--base02); } .c { color: var(--base03); } .err { color: var(--base08); } .k { color: var(--base0E); } .l { color: var(----base09); } .n { color: var(--base08); } .o { color: var(--base05); } .p { color: var(--base05); } .cm { color: var(--base04); } .cp { color: var(--base08); } .c1 { color: var(--base03); } .cs { color: var(--base04); } .gd { color: var(--base08); } .ge { font-style: italic; } .gh { font-weight: 600; color: var(--base0A); } .gi { color: var(--bs-success); } .gp { font-weight: 600; color: var(--base04); } .gs { font-weight: 600; } .gu { font-weight: 600; color: var(--base0C); } .kc { color: var(--base0E); } .kd { color: var(--base0E); } .kn { color: var(--base0C); } .kp { color: var(--base0E); } .kr { color: var(--base0E); } .kt { color: var(--base0A); } .ld { color: var(--base0C); } .m { color: var(--base09); } .s { color: var(--base0C); } .na { color: var(--base0A); } .nb { color: var(--base05); } .nc { color: var(--base07); } .no { color: var(--base08); } .nd { color: var(--base07); } .ni { color: var(--base08); } .ne { color: var(--base08); } .nf { color: var(--base0B); } .nl { color: var(--base05); } .nn { color: var(--base0A); } .nx { color: var(--base0A); } .py { color: var(--base08); } .nt { color: var(--base08); } .nv { color: var(--base08); } .ow { color: var(--base0C); } .w { color: #fff; } .mf { color: var(--base09); } .mh { color: var(--base09); } .mi { color: var(--base09); } .mo { color: var(--base09); } .sb { color: var(--base0C); } .sc { color: #fff; } .sd { color: var(--base04); } .s2 { color: var(--base0C); } .se { color: var(--base09); } .sh { color: var(--base0C); } .si { color: var(--base09); } .sx { color: var(--base0C); } .sr { color: var(--base0C); } .s1 { color: var(--base0C); } .ss { color: var(--base0C); } .bp { color: var(--base05); } .vc { color: var(--base08); } .vg { color: var(--base08); } .vi { color: var(--base08); } .il { color: var(--base09); } .m + .o { color: var(--base03); } .language-sh .c { color: var(--base03); } .chroma .language-bash .line::before, .chroma .language-sh .line::before { color: var(--base03); content: "$ "; -webkit-user-select: none; -moz-user-select: none; user-select: none; } .chroma .language-powershell::before { color: var(--base0C); content: "PM> "; -webkit-user-select: none; -moz-user-select: none; user-select: none; } .anchor-link { padding: 0 0.175rem; font-weight: 400; color: rgba(13, 110, 253, 0.5); text-decoration: none; opacity: 0; transition: color 0.15s ease-in-out, opacity 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .anchor-link { transition: none; } } .anchor-link::after { content: "#"; } .anchor-link:focus, .anchor-link:hover, :hover > .anchor-link, :target > .anchor-link { color: #0d6efd; text-decoration: none; opacity: 1; } a2d-2.0.5/a2d/static/bootstrap/css/cdn_bootstrap_5-3_min.css000066400000000000000000010451501464731662700235560ustar00rootroot00000000000000@charset "UTF-8"; /*! * Bootstrap v5.3.0 (https://getbootstrap.com/) * Copyright 2011-2023 The Bootstrap Authors * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ :root, [data-bs-theme="light"] { --bs-blue: #0d6efd; --bs-indigo: #6610f2; --bs-purple: #6f42c1; --bs-pink: #d63384; --bs-red: #dc3545; --bs-orange: #fd7e14; --bs-yellow: #ffc107; --bs-green: #198754; --bs-teal: #20c997; --bs-cyan: #0dcaf0; --bs-black: #000; --bs-white: #fff; --bs-gray: #6c757d; --bs-gray-dark: #343a40; --bs-gray-100: #f8f9fa; --bs-gray-200: #e9ecef; --bs-gray-300: #dee2e6; --bs-gray-400: #ced4da; --bs-gray-500: #adb5bd; --bs-gray-600: #6c757d; --bs-gray-700: #495057; --bs-gray-800: #343a40; --bs-gray-900: #212529; --bs-primary: #0d6efd; --bs-secondary: #6c757d; --bs-success: #198754; --bs-info: #0dcaf0; --bs-warning: #ffc107; --bs-danger: #dc3545; --bs-light: #f8f9fa; --bs-dark: #212529; --bs-primary-rgb: 13, 110, 253; --bs-secondary-rgb: 108, 117, 125; --bs-success-rgb: 25, 135, 84; --bs-info-rgb: 13, 202, 240; --bs-warning-rgb: 255, 193, 7; --bs-danger-rgb: 220, 53, 69; --bs-light-rgb: 248, 249, 250; --bs-dark-rgb: 33, 37, 41; --bs-primary-text-emphasis: #052c65; --bs-secondary-text-emphasis: #2b2f32; --bs-success-text-emphasis: #0a3622; --bs-info-text-emphasis: #055160; --bs-warning-text-emphasis: #664d03; --bs-danger-text-emphasis: #58151c; --bs-light-text-emphasis: #495057; --bs-dark-text-emphasis: #495057; --bs-primary-bg-subtle: #cfe2ff; --bs-secondary-bg-subtle: #e2e3e5; --bs-success-bg-subtle: #d1e7dd; --bs-info-bg-subtle: #cff4fc; --bs-warning-bg-subtle: #fff3cd; --bs-danger-bg-subtle: #f8d7da; --bs-light-bg-subtle: #fcfcfd; --bs-dark-bg-subtle: #ced4da; --bs-primary-border-subtle: #9ec5fe; --bs-secondary-border-subtle: #c4c8cb; --bs-success-border-subtle: #a3cfbb; --bs-info-border-subtle: #9eeaf9; --bs-warning-border-subtle: #ffe69c; --bs-danger-border-subtle: #f1aeb5; --bs-light-border-subtle: #e9ecef; --bs-dark-border-subtle: #adb5bd; --bs-white-rgb: 255, 255, 255; --bs-black-rgb: 0, 0, 0; --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --bs-gradient: linear-gradient( 180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) ); --bs-body-font-family: var(--bs-font-sans-serif); --bs-body-font-size: 1rem; --bs-body-font-weight: 400; --bs-body-line-height: 1.5; --bs-body-color: #212529; --bs-body-color-rgb: 33, 37, 41; --bs-body-bg: #fff; --bs-body-bg-rgb: 255, 255, 255; --bs-emphasis-color: #000; --bs-emphasis-color-rgb: 0, 0, 0; --bs-secondary-color: rgba(33, 37, 41, 0.75); --bs-secondary-color-rgb: 33, 37, 41; --bs-secondary-bg: #e9ecef; --bs-secondary-bg-rgb: 233, 236, 239; --bs-tertiary-color: rgba(33, 37, 41, 0.5); --bs-tertiary-color-rgb: 33, 37, 41; --bs-tertiary-bg: #f8f9fa; --bs-tertiary-bg-rgb: 248, 249, 250; --bs-heading-color: inherit; --bs-link-color: #0d6efd; --bs-link-color-rgb: 13, 110, 253; --bs-link-decoration: underline; --bs-link-hover-color: #0a58ca; --bs-link-hover-color-rgb: 10, 88, 202; --bs-code-color: #d63384; --bs-highlight-bg: #fff3cd; --bs-border-width: 1px; --bs-border-style: solid; --bs-border-color: #dee2e6; --bs-border-color-translucent: rgba(0, 0, 0, 0.175); --bs-border-radius: 0.375rem; --bs-border-radius-sm: 0.25rem; --bs-border-radius-lg: 0.5rem; --bs-border-radius-xl: 1rem; --bs-border-radius-xxl: 2rem; --bs-border-radius-2xl: var(--bs-border-radius-xxl); --bs-border-radius-pill: 50rem; --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); --bs-focus-ring-width: 0.25rem; --bs-focus-ring-opacity: 0.25; --bs-focus-ring-color: rgba(13, 110, 253, 0.25); --bs-form-valid-color: #198754; --bs-form-valid-border-color: #198754; --bs-form-invalid-color: #dc3545; --bs-form-invalid-border-color: #dc3545; } [data-bs-theme="dark"] { color-scheme: dark; --bs-body-color: #adb5bd; --bs-body-color-rgb: 173, 181, 189; --bs-body-bg: #212529; --bs-body-bg-rgb: 33, 37, 41; --bs-emphasis-color: #fff; --bs-emphasis-color-rgb: 255, 255, 255; --bs-secondary-color: rgba(173, 181, 189, 0.75); --bs-secondary-color-rgb: 173, 181, 189; --bs-secondary-bg: #343a40; --bs-secondary-bg-rgb: 52, 58, 64; --bs-tertiary-color: rgba(173, 181, 189, 0.5); --bs-tertiary-color-rgb: 173, 181, 189; --bs-tertiary-bg: #2b3035; --bs-tertiary-bg-rgb: 43, 48, 53; --bs-primary-text-emphasis: #6ea8fe; --bs-secondary-text-emphasis: #a7acb1; --bs-success-text-emphasis: #75b798; --bs-info-text-emphasis: #6edff6; --bs-warning-text-emphasis: #ffda6a; --bs-danger-text-emphasis: #ea868f; --bs-light-text-emphasis: #f8f9fa; --bs-dark-text-emphasis: #dee2e6; --bs-primary-bg-subtle: #031633; --bs-secondary-bg-subtle: #161719; --bs-success-bg-subtle: #051b11; --bs-info-bg-subtle: #032830; --bs-warning-bg-subtle: #332701; --bs-danger-bg-subtle: #2c0b0e; --bs-light-bg-subtle: #343a40; --bs-dark-bg-subtle: #1a1d20; --bs-primary-border-subtle: #084298; --bs-secondary-border-subtle: #41464b; --bs-success-border-subtle: #0f5132; --bs-info-border-subtle: #087990; --bs-warning-border-subtle: #997404; --bs-danger-border-subtle: #842029; --bs-light-border-subtle: #495057; --bs-dark-border-subtle: #343a40; --bs-heading-color: inherit; --bs-link-color: #6ea8fe; --bs-link-hover-color: #8bb9fe; --bs-link-color-rgb: 110, 168, 254; --bs-link-hover-color-rgb: 139, 185, 254; --bs-code-color: #e685b5; --bs-border-color: #495057; --bs-border-color-translucent: rgba(255, 255, 255, 0.15); --bs-form-valid-color: #75b798; --bs-form-valid-border-color: #75b798; --bs-form-invalid-color: #ea868f; --bs-form-invalid-border-color: #ea868f; } *, ::after, ::before { box-sizing: border-box; } @media (prefers-reduced-motion: no-preference) { :root { scroll-behavior: smooth; } } body { margin: 0; font-family: var(--bs-body-font-family); font-size: var(--bs-body-font-size); font-weight: var(--bs-body-font-weight); line-height: var(--bs-body-line-height); color: var(--bs-body-color); text-align: var(--bs-body-text-align); background-color: var(--bs-body-bg); -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: transparent; } hr { margin: 1rem 0; color: inherit; border: 0; border-top: var(--bs-border-width) solid; opacity: 0.25; } .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { margin-top: 0; margin-bottom: 0.5rem; font-weight: 500; line-height: 1.2; color: var(--bs-heading-color); } .h1, h1 { font-size: calc(1.375rem + 1.5vw); } @media (min-width: 1200px) { .h1, h1 { font-size: 2.5rem; } } .h2, h2 { font-size: calc(1.325rem + 0.9vw); } @media (min-width: 1200px) { .h2, h2 { font-size: 2rem; } } .h3, h3 { font-size: calc(1.3rem + 0.6vw); } @media (min-width: 1200px) { .h3, h3 { font-size: 1.75rem; } } .h4, h4 { font-size: calc(1.275rem + 0.3vw); } @media (min-width: 1200px) { .h4, h4 { font-size: 1.5rem; } } .h5, h5 { font-size: 1.25rem; } .h6, h6 { font-size: 1rem; } p { margin-top: 0; margin-bottom: 1rem; } abbr[title] { -webkit-text-decoration: underline dotted; text-decoration: underline dotted; cursor: help; -webkit-text-decoration-skip-ink: none; text-decoration-skip-ink: none; } address { margin-bottom: 1rem; font-style: normal; line-height: inherit; } ol, ul { padding-left: 2rem; } dl, ol, ul { margin-top: 0; margin-bottom: 1rem; } ol ol, ol ul, ul ol, ul ul { margin-bottom: 0; } dt { font-weight: 700; } dd { margin-bottom: 0.5rem; margin-left: 0; } blockquote { margin: 0 0 1rem; } b, strong { font-weight: bolder; } .small, small { font-size: 0.875em; } .mark, mark { padding: 0.1875em; background-color: var(--bs-highlight-bg); } sub, sup { position: relative; font-size: 0.75em; line-height: 0; vertical-align: baseline; } sub { bottom: -0.25em; } sup { top: -0.5em; } a { color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); text-decoration: underline; } a:hover { --bs-link-color-rgb: var(--bs-link-hover-color-rgb); } a:not([href]):not([class]), a:not([href]):not([class]):hover { color: inherit; text-decoration: none; } code, kbd, pre, samp { font-family: var(--bs-font-monospace); font-size: 1em; } pre { display: block; margin-top: 0; margin-bottom: 1rem; overflow: auto; font-size: 0.875em; } pre code { font-size: inherit; color: inherit; word-break: normal; } code { font-size: 0.875em; color: var(--bs-code-color); word-wrap: break-word; } a > code { color: inherit; } kbd { padding: 0.1875rem 0.375rem; font-size: 0.875em; color: var(--bs-body-bg); background-color: var(--bs-body-color); border-radius: 0.25rem; } 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: 0.5rem; padding-bottom: 0.5rem; color: var(--bs-secondary-color); text-align: left; } th { text-align: inherit; text-align: -webkit-match-parent; } tbody, td, tfoot, th, thead, tr { border-color: inherit; border-style: solid; border-width: 0; } label { display: inline-block; } button { border-radius: 0; } button:focus:not(:focus-visible) { outline: 0; } button, input, optgroup, select, 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; } [type="button"], [type="reset"], [type="submit"], button { -webkit-appearance: button; } [type="button"]:not(:disabled), [type="reset"]:not(:disabled), [type="submit"]:not(:disabled), button: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: 0.5rem; font-size: calc(1.275rem + 0.3vw); line-height: inherit; } @media (min-width: 1200px) { legend { font-size: 1.5rem; } } legend + * { clear: left; } ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-fields-wrapper, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-text, ::-webkit-datetime-edit-year-field { padding: 0; } ::-webkit-inner-spin-button { height: auto; } [type="search"] { outline-offset: -2px; -webkit-appearance: textfield; } ::-webkit-search-decoration { -webkit-appearance: none; } ::-webkit-color-swatch-wrapper { padding: 0; } ::-webkit-file-upload-button { font: inherit; -webkit-appearance: button; } ::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: 1.25rem; font-weight: 300; } .display-1 { font-size: calc(1.625rem + 4.5vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-1 { font-size: 5rem; } } .display-2 { font-size: calc(1.575rem + 3.9vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-2 { font-size: 4.5rem; } } .display-3 { font-size: calc(1.525rem + 3.3vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-3 { font-size: 4rem; } } .display-4 { font-size: calc(1.475rem + 2.7vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-4 { font-size: 3.5rem; } } .display-5 { font-size: calc(1.425rem + 2.1vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-5 { font-size: 3rem; } } .display-6 { font-size: calc(1.375rem + 1.5vw); font-weight: 300; line-height: 1.2; } @media (min-width: 1200px) { .display-6 { font-size: 2.5rem; } } .list-unstyled { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; list-style: none; } .list-inline-item { display: inline-block; } .list-inline-item:not(:last-child) { margin-right: 0.5rem; } .initialism { font-size: 0.875em; text-transform: uppercase; } .blockquote { margin-bottom: 1rem; font-size: 1.25rem; } .blockquote > :last-child { margin-bottom: 0; } .blockquote-footer { margin-top: -1rem; margin-bottom: 1rem; font-size: 0.875em; color: #6c757d; } .blockquote-footer::before { content: "— "; } .img-fluid { max-width: 100%; height: auto; } .img-thumbnail { padding: 0.25rem; background-color: var(--bs-body-bg); border: var(--bs-border-width) solid var(--bs-border-color); border-radius: var(--bs-border-radius); max-width: 100%; height: auto; } .figure { display: inline-block; } .figure-img { margin-bottom: 0.5rem; line-height: 1; } .figure-caption { font-size: 0.875em; color: var(--bs-secondary-color); } .container, .container-fluid, .container-lg, .container-md, .container-sm, .container-xl, .container-xxl { --bs-gutter-x: 1.5rem; --bs-gutter-y: 0; width: 100%; padding-right: calc(var(--bs-gutter-x) * 0.5); padding-left: calc(var(--bs-gutter-x) * 0.5); margin-right: auto; margin-left: auto; } @media (min-width: 576px) { .container, .container-sm { max-width: 540px; } } @media (min-width: 768px) { .container, .container-md, .container-sm { max-width: 720px; } } @media (min-width: 992px) { .container, .container-lg, .container-md, .container-sm { max-width: 960px; } } @media (min-width: 1200px) { .container, .container-lg, .container-md, .container-sm, .container-xl { max-width: 1140px; } } @media (min-width: 1400px) { .container, .container-lg, .container-md, .container-sm, .container-xl, .container-xxl { max-width: 1320px; } } :root { --bs-breakpoint-xs: 0; --bs-breakpoint-sm: 576px; --bs-breakpoint-md: 768px; --bs-breakpoint-lg: 992px; --bs-breakpoint-xl: 1200px; --bs-breakpoint-xxl: 1400px; } .row { --bs-gutter-x: 1.5rem; --bs-gutter-y: 0; display: flex; flex-wrap: wrap; margin-top: calc(-1 * var(--bs-gutter-y)); margin-right: calc(-0.5 * var(--bs-gutter-x)); margin-left: calc(-0.5 * var(--bs-gutter-x)); } .row > * { flex-shrink: 0; width: 100%; max-width: 100%; padding-right: calc(var(--bs-gutter-x) * 0.5); padding-left: calc(var(--bs-gutter-x) * 0.5); margin-top: var(--bs-gutter-y); } .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.3333333333%; } .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.6666666667%; } .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 { --bs-gutter-x: 0; } .g-0, .gy-0 { --bs-gutter-y: 0; } .g-1, .gx-1 { --bs-gutter-x: 0.25rem; } .g-1, .gy-1 { --bs-gutter-y: 0.25rem; } .g-2, .gx-2 { --bs-gutter-x: 0.5rem; } .g-2, .gy-2 { --bs-gutter-y: 0.5rem; } .g-3, .gx-3 { --bs-gutter-x: 1rem; } .g-3, .gy-3 { --bs-gutter-y: 1rem; } .g-4, .gx-4 { --bs-gutter-x: 1.5rem; } .g-4, .gy-4 { --bs-gutter-y: 1.5rem; } .g-5, .gx-5 { --bs-gutter-x: 3rem; } .g-5, .gy-5 { --bs-gutter-y: 3rem; } @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.3333333333%; } .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.6666666667%; } .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 { --bs-gutter-x: 0; } .g-sm-0, .gy-sm-0 { --bs-gutter-y: 0; } .g-sm-1, .gx-sm-1 { --bs-gutter-x: 0.25rem; } .g-sm-1, .gy-sm-1 { --bs-gutter-y: 0.25rem; } .g-sm-2, .gx-sm-2 { --bs-gutter-x: 0.5rem; } .g-sm-2, .gy-sm-2 { --bs-gutter-y: 0.5rem; } .g-sm-3, .gx-sm-3 { --bs-gutter-x: 1rem; } .g-sm-3, .gy-sm-3 { --bs-gutter-y: 1rem; } .g-sm-4, .gx-sm-4 { --bs-gutter-x: 1.5rem; } .g-sm-4, .gy-sm-4 { --bs-gutter-y: 1.5rem; } .g-sm-5, .gx-sm-5 { --bs-gutter-x: 3rem; } .g-sm-5, .gy-sm-5 { --bs-gutter-y: 3rem; } } @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.3333333333%; } .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.6666666667%; } .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 { --bs-gutter-x: 0; } .g-md-0, .gy-md-0 { --bs-gutter-y: 0; } .g-md-1, .gx-md-1 { --bs-gutter-x: 0.25rem; } .g-md-1, .gy-md-1 { --bs-gutter-y: 0.25rem; } .g-md-2, .gx-md-2 { --bs-gutter-x: 0.5rem; } .g-md-2, .gy-md-2 { --bs-gutter-y: 0.5rem; } .g-md-3, .gx-md-3 { --bs-gutter-x: 1rem; } .g-md-3, .gy-md-3 { --bs-gutter-y: 1rem; } .g-md-4, .gx-md-4 { --bs-gutter-x: 1.5rem; } .g-md-4, .gy-md-4 { --bs-gutter-y: 1.5rem; } .g-md-5, .gx-md-5 { --bs-gutter-x: 3rem; } .g-md-5, .gy-md-5 { --bs-gutter-y: 3rem; } } @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.3333333333%; } .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.6666666667%; } .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 { --bs-gutter-x: 0; } .g-lg-0, .gy-lg-0 { --bs-gutter-y: 0; } .g-lg-1, .gx-lg-1 { --bs-gutter-x: 0.25rem; } .g-lg-1, .gy-lg-1 { --bs-gutter-y: 0.25rem; } .g-lg-2, .gx-lg-2 { --bs-gutter-x: 0.5rem; } .g-lg-2, .gy-lg-2 { --bs-gutter-y: 0.5rem; } .g-lg-3, .gx-lg-3 { --bs-gutter-x: 1rem; } .g-lg-3, .gy-lg-3 { --bs-gutter-y: 1rem; } .g-lg-4, .gx-lg-4 { --bs-gutter-x: 1.5rem; } .g-lg-4, .gy-lg-4 { --bs-gutter-y: 1.5rem; } .g-lg-5, .gx-lg-5 { --bs-gutter-x: 3rem; } .g-lg-5, .gy-lg-5 { --bs-gutter-y: 3rem; } } @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.3333333333%; } .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.6666666667%; } .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 { --bs-gutter-x: 0; } .g-xl-0, .gy-xl-0 { --bs-gutter-y: 0; } .g-xl-1, .gx-xl-1 { --bs-gutter-x: 0.25rem; } .g-xl-1, .gy-xl-1 { --bs-gutter-y: 0.25rem; } .g-xl-2, .gx-xl-2 { --bs-gutter-x: 0.5rem; } .g-xl-2, .gy-xl-2 { --bs-gutter-y: 0.5rem; } .g-xl-3, .gx-xl-3 { --bs-gutter-x: 1rem; } .g-xl-3, .gy-xl-3 { --bs-gutter-y: 1rem; } .g-xl-4, .gx-xl-4 { --bs-gutter-x: 1.5rem; } .g-xl-4, .gy-xl-4 { --bs-gutter-y: 1.5rem; } .g-xl-5, .gx-xl-5 { --bs-gutter-x: 3rem; } .g-xl-5, .gy-xl-5 { --bs-gutter-y: 3rem; } } @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.3333333333%; } .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.6666666667%; } .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 { --bs-gutter-x: 0; } .g-xxl-0, .gy-xxl-0 { --bs-gutter-y: 0; } .g-xxl-1, .gx-xxl-1 { --bs-gutter-x: 0.25rem; } .g-xxl-1, .gy-xxl-1 { --bs-gutter-y: 0.25rem; } .g-xxl-2, .gx-xxl-2 { --bs-gutter-x: 0.5rem; } .g-xxl-2, .gy-xxl-2 { --bs-gutter-y: 0.5rem; } .g-xxl-3, .gx-xxl-3 { --bs-gutter-x: 1rem; } .g-xxl-3, .gy-xxl-3 { --bs-gutter-y: 1rem; } .g-xxl-4, .gx-xxl-4 { --bs-gutter-x: 1.5rem; } .g-xxl-4, .gy-xxl-4 { --bs-gutter-y: 1.5rem; } .g-xxl-5, .gx-xxl-5 { --bs-gutter-x: 3rem; } .g-xxl-5, .gy-xxl-5 { --bs-gutter-y: 3rem; } } .table { --bs-table-color-type: initial; --bs-table-bg-type: initial; --bs-table-color-state: initial; --bs-table-bg-state: initial; --bs-table-color: var(--bs-body-color); --bs-table-bg: var(--bs-body-bg); --bs-table-border-color: var(--bs-border-color); --bs-table-accent-bg: transparent; --bs-table-striped-color: var(--bs-body-color); --bs-table-striped-bg: rgba(0, 0, 0, 0.05); --bs-table-active-color: var(--bs-body-color); --bs-table-active-bg: rgba(0, 0, 0, 0.1); --bs-table-hover-color: var(--bs-body-color); --bs-table-hover-bg: rgba(0, 0, 0, 0.075); width: 100%; margin-bottom: 1rem; vertical-align: top; border-color: var(--bs-table-border-color); } .table > :not(caption) > * > * { padding: 0.5rem 0.5rem; color: var( --bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)) ); background-color: var(--bs-table-bg); border-bottom-width: var(--bs-border-width); box-shadow: inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg))); } .table > tbody { vertical-align: inherit; } .table > thead { vertical-align: bottom; } .table-group-divider { border-top: calc(var(--bs-border-width) * 2) solid currentcolor; } .caption-top { caption-side: top; } .table-sm > :not(caption) > * > * { padding: 0.25rem 0.25rem; } .table-bordered > :not(caption) > * { border-width: var(--bs-border-width) 0; } .table-bordered > :not(caption) > * > * { border-width: 0 var(--bs-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(odd) > * { --bs-table-color-type: var(--bs-table-striped-color); --bs-table-bg-type: var(--bs-table-striped-bg); } .table-striped-columns > :not(caption) > tr > :nth-child(2n) { --bs-table-color-type: var(--bs-table-striped-color); --bs-table-bg-type: var(--bs-table-striped-bg); } .table-active { --bs-table-color-state: var(--bs-table-active-color); --bs-table-bg-state: var(--bs-table-active-bg); } .table-hover > tbody > tr:hover > * { --bs-table-color-state: var(--bs-table-hover-color); --bs-table-bg-state: var(--bs-table-hover-bg); } .table-primary { --bs-table-color: #000; --bs-table-bg: #cfe2ff; --bs-table-border-color: #bacbe6; --bs-table-striped-bg: #c5d7f2; --bs-table-striped-color: #000; --bs-table-active-bg: #bacbe6; --bs-table-active-color: #000; --bs-table-hover-bg: #bfd1ec; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-secondary { --bs-table-color: #000; --bs-table-bg: #e2e3e5; --bs-table-border-color: #cbccce; --bs-table-striped-bg: #d7d8da; --bs-table-striped-color: #000; --bs-table-active-bg: #cbccce; --bs-table-active-color: #000; --bs-table-hover-bg: #d1d2d4; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-success { --bs-table-color: #000; --bs-table-bg: #d1e7dd; --bs-table-border-color: #bcd0c7; --bs-table-striped-bg: #c7dbd2; --bs-table-striped-color: #000; --bs-table-active-bg: #bcd0c7; --bs-table-active-color: #000; --bs-table-hover-bg: #c1d6cc; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-info { --bs-table-color: #000; --bs-table-bg: #cff4fc; --bs-table-border-color: #badce3; --bs-table-striped-bg: #c5e8ef; --bs-table-striped-color: #000; --bs-table-active-bg: #badce3; --bs-table-active-color: #000; --bs-table-hover-bg: #bfe2e9; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-warning { --bs-table-color: #000; --bs-table-bg: #fff3cd; --bs-table-border-color: #e6dbb9; --bs-table-striped-bg: #f2e7c3; --bs-table-striped-color: #000; --bs-table-active-bg: #e6dbb9; --bs-table-active-color: #000; --bs-table-hover-bg: #ece1be; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-danger { --bs-table-color: #000; --bs-table-bg: #f8d7da; --bs-table-border-color: #dfc2c4; --bs-table-striped-bg: #eccccf; --bs-table-striped-color: #000; --bs-table-active-bg: #dfc2c4; --bs-table-active-color: #000; --bs-table-hover-bg: #e5c7ca; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-light { --bs-table-color: #000; --bs-table-bg: #f8f9fa; --bs-table-border-color: #dfe0e1; --bs-table-striped-bg: #ecedee; --bs-table-striped-color: #000; --bs-table-active-bg: #dfe0e1; --bs-table-active-color: #000; --bs-table-hover-bg: #e5e6e7; --bs-table-hover-color: #000; color: var(--bs-table-color); border-color: var(--bs-table-border-color); } .table-dark { --bs-table-color: #fff; --bs-table-bg: #212529; --bs-table-border-color: #373b3e; --bs-table-striped-bg: #2c3034; --bs-table-striped-color: #fff; --bs-table-active-bg: #373b3e; --bs-table-active-color: #fff; --bs-table-hover-bg: #323539; --bs-table-hover-color: #fff; color: var(--bs-table-color); border-color: var(--bs-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: 0.5rem; } .col-form-label { padding-top: calc(0.375rem + var(--bs-border-width)); padding-bottom: calc(0.375rem + var(--bs-border-width)); margin-bottom: 0; font-size: inherit; line-height: 1.5; } .col-form-label-lg { padding-top: calc(0.5rem + var(--bs-border-width)); padding-bottom: calc(0.5rem + var(--bs-border-width)); font-size: 1.25rem; } .col-form-label-sm { padding-top: calc(0.25rem + var(--bs-border-width)); padding-bottom: calc(0.25rem + var(--bs-border-width)); font-size: 0.875rem; } .form-text { margin-top: 0.25rem; font-size: 0.875em; color: var(--bs-secondary-color); } .form-control { display: block; width: 100%; padding: 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: var(--bs-body-color); background-color: var(--bs-body-bg); background-clip: padding-box; border: var(--bs-border-width) solid var(--bs-border-color); -webkit-appearance: none; -moz-appearance: none; appearance: none; border-radius: var(--bs-border-radius); transition: border-color 0.15s ease-in-out, box-shadow 0.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(--bs-body-color); background-color: var(--bs-body-bg); border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-control::-webkit-date-and-time-value { min-width: 85px; height: 1.5em; margin: 0; } .form-control::-webkit-datetime-edit { display: block; padding: 0; } .form-control::-moz-placeholder { color: var(--bs-secondary-color); opacity: 1; } .form-control::placeholder { color: var(--bs-secondary-color); opacity: 1; } .form-control:disabled { background-color: var(--bs-secondary-bg); opacity: 1; } .form-control::-webkit-file-upload-button { padding: 0.375rem 0.75rem; margin: -0.375rem -0.75rem; -webkit-margin-end: 0.75rem; margin-inline-end: 0.75rem; color: var(--bs-body-color); background-color: var(--bs-tertiary-bg); pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; border-inline-end-width: var(--bs-border-width); border-radius: 0; -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } .form-control::file-selector-button { padding: 0.375rem 0.75rem; margin: -0.375rem -0.75rem; -webkit-margin-end: 0.75rem; margin-inline-end: 0.75rem; color: var(--bs-body-color); background-color: var(--bs-tertiary-bg); pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; border-inline-end-width: var(--bs-border-width); border-radius: 0; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-control::-webkit-file-upload-button { -webkit-transition: none; transition: none; } .form-control::file-selector-button { transition: none; } } .form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button { background-color: var(--bs-secondary-bg); } .form-control:hover:not(:disabled):not([readonly])::file-selector-button { background-color: var(--bs-secondary-bg); } .form-control-plaintext { display: block; width: 100%; padding: 0.375rem 0; margin-bottom: 0; line-height: 1.5; color: var(--bs-body-color); background-color: transparent; border: solid transparent; border-width: var(--bs-border-width) 0; } .form-control-plaintext:focus { outline: 0; } .form-control-plaintext.form-control-lg, .form-control-plaintext.form-control-sm { padding-right: 0; padding-left: 0; } .form-control-sm { min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); padding: 0.25rem 0.5rem; font-size: 0.875rem; border-radius: var(--bs-border-radius-sm); } .form-control-sm::-webkit-file-upload-button { padding: 0.25rem 0.5rem; margin: -0.25rem -0.5rem; -webkit-margin-end: 0.5rem; margin-inline-end: 0.5rem; } .form-control-sm::file-selector-button { padding: 0.25rem 0.5rem; margin: -0.25rem -0.5rem; -webkit-margin-end: 0.5rem; margin-inline-end: 0.5rem; } .form-control-lg { min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); padding: 0.5rem 1rem; font-size: 1.25rem; border-radius: var(--bs-border-radius-lg); } .form-control-lg::-webkit-file-upload-button { padding: 0.5rem 1rem; margin: -0.5rem -1rem; -webkit-margin-end: 1rem; margin-inline-end: 1rem; } .form-control-lg::file-selector-button { padding: 0.5rem 1rem; margin: -0.5rem -1rem; -webkit-margin-end: 1rem; margin-inline-end: 1rem; } textarea.form-control { min-height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); } textarea.form-control-sm { min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); } textarea.form-control-lg { min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); } .form-control-color { width: 3rem; height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); padding: 0.375rem; } .form-control-color:not(:disabled):not([readonly]) { cursor: pointer; } .form-control-color::-moz-color-swatch { border: 0 !important; border-radius: var(--bs-border-radius); } .form-control-color::-webkit-color-swatch { border: 0 !important; border-radius: var(--bs-border-radius); } .form-control-color.form-control-sm { height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); } .form-control-color.form-control-lg { height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); } .form-select { --bs-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='%23343a40' 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: 0.375rem 2.25rem 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: var(--bs-body-color); background-color: var(--bs-body-bg); background-image: var(--bs-form-select-bg-img), var(--bs-form-select-bg-icon, none); background-repeat: no-repeat; background-position: right 0.75rem center; background-size: 16px 12px; border: var(--bs-border-width) solid var(--bs-border-color); border-radius: var(--bs-border-radius); transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -webkit-appearance: none; -moz-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-select { transition: none; } } .form-select:focus { border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-select[multiple], .form-select[size]:not([size="1"]) { padding-right: 0.75rem; background-image: none; } .form-select:disabled { background-color: var(--bs-secondary-bg); } .form-select:-moz-focusring { color: transparent; text-shadow: 0 0 0 var(--bs-body-color); } .form-select-sm { padding-top: 0.25rem; padding-bottom: 0.25rem; padding-left: 0.5rem; font-size: 0.875rem; border-radius: var(--bs-border-radius-sm); } .form-select-lg { padding-top: 0.5rem; padding-bottom: 0.5rem; padding-left: 1rem; font-size: 1.25rem; border-radius: var(--bs-border-radius-lg); } [data-bs-theme="dark"] .form-select { --bs-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='%23adb5bd' 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.5rem; padding-left: 1.5em; margin-bottom: 0.125rem; } .form-check .form-check-input { float: left; margin-left: -1.5em; } .form-check-reverse { padding-right: 1.5em; padding-left: 0; text-align: right; } .form-check-reverse .form-check-input { float: right; margin-right: -1.5em; margin-left: 0; } .form-check-input { --bs-form-check-bg: var(--bs-body-bg); width: 1em; height: 1em; margin-top: 0.25em; vertical-align: top; background-color: var(--bs-form-check-bg); background-image: var(--bs-form-check-bg-image); background-repeat: no-repeat; background-position: center; background-size: contain; border: var(--bs-border-width) solid var(--bs-border-color); -webkit-appearance: none; -moz-appearance: none; appearance: none; -webkit-print-color-adjust: exact; color-adjust: exact; print-color-adjust: exact; } .form-check-input[type="checkbox"] { border-radius: 0.25em; } .form-check-input[type="radio"] { border-radius: 50%; } .form-check-input:active { filter: brightness(90%); } .form-check-input:focus { border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-check-input:checked { background-color: #0d6efd; border-color: #0d6efd; } .form-check-input:checked[type="checkbox"] { --bs-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='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); } .form-check-input:checked[type="radio"] { --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); } .form-check-input[type="checkbox"]:indeterminate { background-color: #0d6efd; border-color: #0d6efd; --bs-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='%23fff' 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: 0.5; } .form-check-input:disabled ~ .form-check-label, .form-check-input[disabled] ~ .form-check-label { cursor: default; opacity: 0.5; } .form-switch { padding-left: 2.5em; } .form-switch .form-check-input { --bs-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%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); width: 2em; margin-left: -2.5em; background-image: var(--bs-form-switch-bg); background-position: left center; border-radius: 2em; transition: background-position 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .form-switch .form-check-input { transition: none; } } .form-switch .form-check-input:focus { --bs-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='%2386b7fe'/%3e%3c/svg%3e"); } .form-switch .form-check-input:checked { background-position: right center; --bs-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='%23fff'/%3e%3c/svg%3e"); } .form-switch.form-check-reverse { padding-right: 2.5em; padding-left: 0; } .form-switch.form-check-reverse .form-check-input { margin-right: -2.5em; 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: 0.65; } [data-bs-theme="dark"] .form-switch .form-check-input:not(:checked):not(:focus) { --bs-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.5rem; padding: 0; background-color: transparent; -webkit-appearance: none; -moz-appearance: none; appearance: none; } .form-range:focus { outline: 0; } .form-range:focus::-webkit-slider-thumb { box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-range:focus::-moz-range-thumb { box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-range::-moz-focus-outer { border: 0; } .form-range::-webkit-slider-thumb { width: 1rem; height: 1rem; margin-top: -0.25rem; background-color: #0d6efd; border: 0; border-radius: 1rem; -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -webkit-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-range::-webkit-slider-thumb { -webkit-transition: none; transition: none; } } .form-range::-webkit-slider-thumb:active { background-color: #b6d4fe; } .form-range::-webkit-slider-runnable-track { width: 100%; height: 0.5rem; color: transparent; cursor: pointer; background-color: var(--bs-tertiary-bg); border-color: transparent; border-radius: 1rem; } .form-range::-moz-range-thumb { width: 1rem; height: 1rem; background-color: #0d6efd; border: 0; border-radius: 1rem; -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -moz-appearance: none; appearance: none; } @media (prefers-reduced-motion: reduce) { .form-range::-moz-range-thumb { -moz-transition: none; transition: none; } } .form-range::-moz-range-thumb:active { background-color: #b6d4fe; } .form-range::-moz-range-track { width: 100%; height: 0.5rem; color: transparent; cursor: pointer; background-color: var(--bs-tertiary-bg); border-color: transparent; border-radius: 1rem; } .form-range:disabled { pointer-events: none; } .form-range:disabled::-webkit-slider-thumb { background-color: var(--bs-secondary-color); } .form-range:disabled::-moz-range-thumb { background-color: var(--bs-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(--bs-border-width) * 2)); min-height: calc(3.5rem + calc(var(--bs-border-width) * 2)); line-height: 1.25; } .form-floating > label { position: absolute; top: 0; left: 0; z-index: 2; height: 100%; padding: 1rem 0.75rem; overflow: hidden; text-align: start; text-overflow: ellipsis; white-space: nowrap; pointer-events: none; border: var(--bs-border-width) solid transparent; transform-origin: 0 0; transition: opacity 0.1s ease-in-out, transform 0.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 0.75rem; } .form-floating > .form-control-plaintext::-moz-placeholder, .form-floating > .form-control::-moz-placeholder { color: transparent; } .form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder { color: transparent; } .form-floating > .form-control-plaintext:not(:-moz-placeholder-shown), .form-floating > .form-control:not(:-moz-placeholder-shown) { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-control-plaintext:focus, .form-floating > .form-control-plaintext:not(:placeholder-shown), .form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown) { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-control-plaintext:-webkit-autofill, .form-floating > .form-control:-webkit-autofill { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-select { padding-top: 1.625rem; padding-bottom: 0.625rem; } .form-floating > .form-control:not(:-moz-placeholder-shown) ~ label { color: rgba(var(--bs-body-color-rgb), 0.65); transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } .form-floating > .form-control-plaintext ~ label, .form-floating > .form-control:focus ~ label, .form-floating > .form-control:not(:placeholder-shown) ~ label, .form-floating > .form-select ~ label { color: rgba(var(--bs-body-color-rgb), 0.65); transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } .form-floating > .form-control:not(:-moz-placeholder-shown) ~ label::after { position: absolute; inset: 1rem 0.375rem; z-index: -1; height: 1.5em; content: ""; background-color: var(--bs-body-bg); border-radius: var(--bs-border-radius); } .form-floating > .form-control-plaintext ~ label::after, .form-floating > .form-control:focus ~ label::after, .form-floating > .form-control:not(:placeholder-shown) ~ label::after, .form-floating > .form-select ~ label::after { position: absolute; inset: 1rem 0.375rem; z-index: -1; height: 1.5em; content: ""; background-color: var(--bs-body-bg); border-radius: var(--bs-border-radius); } .form-floating > .form-control:-webkit-autofill ~ label { color: rgba(var(--bs-body-color-rgb), 0.65); transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } .form-floating > .form-control-plaintext ~ label { border-width: var(--bs-border-width) 0; } .form-floating > :disabled ~ label { color: #6c757d; } .form-floating > :disabled ~ label::after { background-color: var(--bs-secondary-bg); } .input-group { position: relative; display: flex; flex-wrap: wrap; align-items: stretch; width: 100%; } .input-group > .form-control, .input-group > .form-floating, .input-group > .form-select { position: relative; flex: 1 1 auto; width: 1%; min-width: 0; } .input-group > .form-control:focus, .input-group > .form-floating:focus-within, .input-group > .form-select:focus { 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: 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: var(--bs-body-color); text-align: center; white-space: nowrap; background-color: var(--bs-tertiary-bg); border: var(--bs-border-width) solid var(--bs-border-color); border-radius: var(--bs-border-radius); } .input-group-lg > .btn, .input-group-lg > .form-control, .input-group-lg > .form-select, .input-group-lg > .input-group-text { padding: 0.5rem 1rem; font-size: 1.25rem; border-radius: var(--bs-border-radius-lg); } .input-group-sm > .btn, .input-group-sm > .form-control, .input-group-sm > .form-select, .input-group-sm > .input-group-text { padding: 0.25rem 0.5rem; font-size: 0.875rem; border-radius: var(--bs-border-radius-sm); } .input-group-lg > .form-select, .input-group-sm > .form-select { padding-right: 3rem; } .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, .input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not( .form-floating ) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .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, .input-group.has-validation > :nth-last-child(n + 3):not(.dropdown-toggle):not(.dropdown-menu):not( .form-floating ) { 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(--bs-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: 0.25rem; font-size: 0.875em; color: var(--bs-form-valid-color); } .valid-tooltip { position: absolute; top: 100%; z-index: 5; display: none; max-width: 100%; padding: 0.25rem 0.5rem; margin-top: 0.1rem; font-size: 0.875rem; color: #fff; background-color: var(--bs-success); border-radius: var(--bs-border-radius); } .is-valid ~ .valid-feedback, .is-valid ~ .valid-tooltip, .was-validated :valid ~ .valid-feedback, .was-validated :valid ~ .valid-tooltip { display: block; } .form-control.is-valid, .was-validated .form-control:valid { border-color: var(--bs-form-valid-border-color); padding-right: calc(1.5em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .form-control.is-valid:focus, .was-validated .form-control:valid:focus { border-color: var(--bs-form-valid-border-color); box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } .was-validated textarea.form-control:valid, textarea.form-control.is-valid { padding-right: calc(1.5em + 0.75rem); background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } .form-select.is-valid, .was-validated .form-select:valid { border-color: var(--bs-form-valid-border-color); } .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size="1"], .was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"] { --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); padding-right: 4.125rem; background-position: right 0.75rem center, center right 2.25rem; background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .form-select.is-valid:focus, .was-validated .form-select:valid:focus { border-color: var(--bs-form-valid-border-color); box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } .form-control-color.is-valid, .was-validated .form-control-color:valid { width: calc(3rem + calc(1.5em + 0.75rem)); } .form-check-input.is-valid, .was-validated .form-check-input:valid { border-color: var(--bs-form-valid-border-color); } .form-check-input.is-valid:checked, .was-validated .form-check-input:valid:checked { background-color: var(--bs-form-valid-color); } .form-check-input.is-valid:focus, .was-validated .form-check-input:valid:focus { box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } .form-check-input.is-valid ~ .form-check-label, .was-validated .form-check-input:valid ~ .form-check-label { color: var(--bs-form-valid-color); } .form-check-inline .form-check-input ~ .valid-feedback { margin-left: 0.5em; } .input-group > .form-control:not(:focus).is-valid, .input-group > .form-floating:not(:focus-within).is-valid, .input-group > .form-select:not(:focus).is-valid, .was-validated .input-group > .form-control:not(:focus):valid, .was-validated .input-group > .form-floating:not(:focus-within):valid, .was-validated .input-group > .form-select:not(:focus):valid { z-index: 3; } .invalid-feedback { display: none; width: 100%; margin-top: 0.25rem; font-size: 0.875em; color: var(--bs-form-invalid-color); } .invalid-tooltip { position: absolute; top: 100%; z-index: 5; display: none; max-width: 100%; padding: 0.25rem 0.5rem; margin-top: 0.1rem; font-size: 0.875rem; color: #fff; background-color: var(--bs-danger); border-radius: var(--bs-border-radius); } .is-invalid ~ .invalid-feedback, .is-invalid ~ .invalid-tooltip, .was-validated :invalid ~ .invalid-feedback, .was-validated :invalid ~ .invalid-tooltip { display: block; } .form-control.is-invalid, .was-validated .form-control:invalid { border-color: var(--bs-form-invalid-border-color); padding-right: calc(1.5em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .form-control.is-invalid:focus, .was-validated .form-control:invalid:focus { border-color: var(--bs-form-invalid-border-color); box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } .was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { padding-right: calc(1.5em + 0.75rem); background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } .form-select.is-invalid, .was-validated .form-select:invalid { border-color: var(--bs-form-invalid-border-color); } .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size="1"], .was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"] { --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); padding-right: 4.125rem; background-position: right 0.75rem center, center right 2.25rem; background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .form-select.is-invalid:focus, .was-validated .form-select:invalid:focus { border-color: var(--bs-form-invalid-border-color); box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } .form-control-color.is-invalid, .was-validated .form-control-color:invalid { width: calc(3rem + calc(1.5em + 0.75rem)); } .form-check-input.is-invalid, .was-validated .form-check-input:invalid { border-color: var(--bs-form-invalid-border-color); } .form-check-input.is-invalid:checked, .was-validated .form-check-input:invalid:checked { background-color: var(--bs-form-invalid-color); } .form-check-input.is-invalid:focus, .was-validated .form-check-input:invalid:focus { box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } .form-check-input.is-invalid ~ .form-check-label, .was-validated .form-check-input:invalid ~ .form-check-label { color: var(--bs-form-invalid-color); } .form-check-inline .form-check-input ~ .invalid-feedback { margin-left: 0.5em; } .input-group > .form-control:not(:focus).is-invalid, .input-group > .form-floating:not(:focus-within).is-invalid, .input-group > .form-select:not(:focus).is-invalid, .was-validated .input-group > .form-control:not(:focus):invalid, .was-validated .input-group > .form-floating:not(:focus-within):invalid, .was-validated .input-group > .form-select:not(:focus):invalid { z-index: 4; } .btn { --bs-btn-padding-x: 0.75rem; --bs-btn-padding-y: 0.375rem; --bs-btn-font-family: ; --bs-btn-font-size: 1rem; --bs-btn-font-weight: 400; --bs-btn-line-height: 1.5; --bs-btn-color: var(--bs-body-color); --bs-btn-bg: transparent; --bs-btn-border-width: var(--bs-border-width); --bs-btn-border-color: transparent; --bs-btn-border-radius: var(--bs-border-radius); --bs-btn-hover-border-color: transparent; --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); --bs-btn-disabled-opacity: 0.65; --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), 0.5); display: inline-block; padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x); font-family: var(--bs-btn-font-family); font-size: var(--bs-btn-font-size); font-weight: var(--bs-btn-font-weight); line-height: var(--bs-btn-line-height); color: var(--bs-btn-color); text-align: center; text-decoration: none; vertical-align: middle; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; user-select: none; border: var(--bs-btn-border-width) solid var(--bs-btn-border-color); border-radius: var(--bs-btn-border-radius); background-color: var(--bs-btn-bg); transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .btn { transition: none; } } .btn:hover { color: var(--bs-btn-hover-color); background-color: var(--bs-btn-hover-bg); border-color: var(--bs-btn-hover-border-color); } .btn-check + .btn:hover { color: var(--bs-btn-color); background-color: var(--bs-btn-bg); border-color: var(--bs-btn-border-color); } .btn:focus-visible { color: var(--bs-btn-hover-color); background-color: var(--bs-btn-hover-bg); border-color: var(--bs-btn-hover-border-color); outline: 0; box-shadow: var(--bs-btn-focus-box-shadow); } .btn-check:focus-visible + .btn { border-color: var(--bs-btn-hover-border-color); outline: 0; box-shadow: var(--bs-btn-focus-box-shadow); } .btn-check:checked + .btn, .btn.active, .btn.show, .btn:first-child:active, :not(.btn-check) + .btn:active { color: var(--bs-btn-active-color); background-color: var(--bs-btn-active-bg); border-color: var(--bs-btn-active-border-color); } .btn-check:checked + .btn:focus-visible, .btn.active:focus-visible, .btn.show:focus-visible, .btn:first-child:active:focus-visible, :not(.btn-check) + .btn:active:focus-visible { box-shadow: var(--bs-btn-focus-box-shadow); } .btn.disabled, .btn:disabled, fieldset:disabled .btn { color: var(--bs-btn-disabled-color); pointer-events: none; background-color: var(--bs-btn-disabled-bg); border-color: var(--bs-btn-disabled-border-color); opacity: var(--bs-btn-disabled-opacity); } .btn-primary { --bs-btn-color: #fff; --bs-btn-bg: #0d6efd; --bs-btn-border-color: #0d6efd; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #0b5ed7; --bs-btn-hover-border-color: #0a58ca; --bs-btn-focus-shadow-rgb: 49, 132, 253; --bs-btn-active-color: #fff; --bs-btn-active-bg: #0a58ca; --bs-btn-active-border-color: #0a53be; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #fff; --bs-btn-disabled-bg: #0d6efd; --bs-btn-disabled-border-color: #0d6efd; } .btn-secondary { --bs-btn-color: #fff; --bs-btn-bg: #6c757d; --bs-btn-border-color: #6c757d; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #5c636a; --bs-btn-hover-border-color: #565e64; --bs-btn-focus-shadow-rgb: 130, 138, 145; --bs-btn-active-color: #fff; --bs-btn-active-bg: #565e64; --bs-btn-active-border-color: #51585e; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #fff; --bs-btn-disabled-bg: #6c757d; --bs-btn-disabled-border-color: #6c757d; } .btn-success { --bs-btn-color: #fff; --bs-btn-bg: #198754; --bs-btn-border-color: #198754; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #157347; --bs-btn-hover-border-color: #146c43; --bs-btn-focus-shadow-rgb: 60, 153, 110; --bs-btn-active-color: #fff; --bs-btn-active-bg: #146c43; --bs-btn-active-border-color: #13653f; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #fff; --bs-btn-disabled-bg: #198754; --bs-btn-disabled-border-color: #198754; } .btn-info { --bs-btn-color: #000; --bs-btn-bg: #0dcaf0; --bs-btn-border-color: #0dcaf0; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #31d2f2; --bs-btn-hover-border-color: #25cff2; --bs-btn-focus-shadow-rgb: 11, 172, 204; --bs-btn-active-color: #000; --bs-btn-active-bg: #3dd5f3; --bs-btn-active-border-color: #25cff2; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #000; --bs-btn-disabled-bg: #0dcaf0; --bs-btn-disabled-border-color: #0dcaf0; } .btn-warning { --bs-btn-color: #000; --bs-btn-bg: #ffc107; --bs-btn-border-color: #ffc107; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #ffca2c; --bs-btn-hover-border-color: #ffc720; --bs-btn-focus-shadow-rgb: 217, 164, 6; --bs-btn-active-color: #000; --bs-btn-active-bg: #ffcd39; --bs-btn-active-border-color: #ffc720; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #000; --bs-btn-disabled-bg: #ffc107; --bs-btn-disabled-border-color: #ffc107; } .btn-danger { --bs-btn-color: #fff; --bs-btn-bg: #dc3545; --bs-btn-border-color: #dc3545; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #bb2d3b; --bs-btn-hover-border-color: #b02a37; --bs-btn-focus-shadow-rgb: 225, 83, 97; --bs-btn-active-color: #fff; --bs-btn-active-bg: #b02a37; --bs-btn-active-border-color: #a52834; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #fff; --bs-btn-disabled-bg: #dc3545; --bs-btn-disabled-border-color: #dc3545; } .btn-light { --bs-btn-color: #000; --bs-btn-bg: #f8f9fa; --bs-btn-border-color: #f8f9fa; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #d3d4d5; --bs-btn-hover-border-color: #c6c7c8; --bs-btn-focus-shadow-rgb: 211, 212, 213; --bs-btn-active-color: #000; --bs-btn-active-bg: #c6c7c8; --bs-btn-active-border-color: #babbbc; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #000; --bs-btn-disabled-bg: #f8f9fa; --bs-btn-disabled-border-color: #f8f9fa; } .btn-dark { --bs-btn-color: #fff; --bs-btn-bg: #212529; --bs-btn-border-color: #212529; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #424649; --bs-btn-hover-border-color: #373b3e; --bs-btn-focus-shadow-rgb: 66, 70, 73; --bs-btn-active-color: #fff; --bs-btn-active-bg: #4d5154; --bs-btn-active-border-color: #373b3e; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #fff; --bs-btn-disabled-bg: #212529; --bs-btn-disabled-border-color: #212529; } .btn-outline-primary { --bs-btn-color: #0d6efd; --bs-btn-border-color: #0d6efd; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #0d6efd; --bs-btn-hover-border-color: #0d6efd; --bs-btn-focus-shadow-rgb: 13, 110, 253; --bs-btn-active-color: #fff; --bs-btn-active-bg: #0d6efd; --bs-btn-active-border-color: #0d6efd; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #0d6efd; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #0d6efd; --bs-gradient: none; } .btn-outline-secondary { --bs-btn-color: #6c757d; --bs-btn-border-color: #6c757d; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #6c757d; --bs-btn-hover-border-color: #6c757d; --bs-btn-focus-shadow-rgb: 108, 117, 125; --bs-btn-active-color: #fff; --bs-btn-active-bg: #6c757d; --bs-btn-active-border-color: #6c757d; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #6c757d; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #6c757d; --bs-gradient: none; } .btn-outline-success { --bs-btn-color: #198754; --bs-btn-border-color: #198754; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #198754; --bs-btn-hover-border-color: #198754; --bs-btn-focus-shadow-rgb: 25, 135, 84; --bs-btn-active-color: #fff; --bs-btn-active-bg: #198754; --bs-btn-active-border-color: #198754; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #198754; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #198754; --bs-gradient: none; } .btn-outline-info { --bs-btn-color: #0dcaf0; --bs-btn-border-color: #0dcaf0; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #0dcaf0; --bs-btn-hover-border-color: #0dcaf0; --bs-btn-focus-shadow-rgb: 13, 202, 240; --bs-btn-active-color: #000; --bs-btn-active-bg: #0dcaf0; --bs-btn-active-border-color: #0dcaf0; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #0dcaf0; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #0dcaf0; --bs-gradient: none; } .btn-outline-warning { --bs-btn-color: #ffc107; --bs-btn-border-color: #ffc107; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #ffc107; --bs-btn-hover-border-color: #ffc107; --bs-btn-focus-shadow-rgb: 255, 193, 7; --bs-btn-active-color: #000; --bs-btn-active-bg: #ffc107; --bs-btn-active-border-color: #ffc107; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #ffc107; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #ffc107; --bs-gradient: none; } .btn-outline-danger { --bs-btn-color: #dc3545; --bs-btn-border-color: #dc3545; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #dc3545; --bs-btn-hover-border-color: #dc3545; --bs-btn-focus-shadow-rgb: 220, 53, 69; --bs-btn-active-color: #fff; --bs-btn-active-bg: #dc3545; --bs-btn-active-border-color: #dc3545; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #dc3545; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #dc3545; --bs-gradient: none; } .btn-outline-light { --bs-btn-color: #f8f9fa; --bs-btn-border-color: #f8f9fa; --bs-btn-hover-color: #000; --bs-btn-hover-bg: #f8f9fa; --bs-btn-hover-border-color: #f8f9fa; --bs-btn-focus-shadow-rgb: 248, 249, 250; --bs-btn-active-color: #000; --bs-btn-active-bg: #f8f9fa; --bs-btn-active-border-color: #f8f9fa; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #f8f9fa; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #f8f9fa; --bs-gradient: none; } .btn-outline-dark { --bs-btn-color: #212529; --bs-btn-border-color: #212529; --bs-btn-hover-color: #fff; --bs-btn-hover-bg: #212529; --bs-btn-hover-border-color: #212529; --bs-btn-focus-shadow-rgb: 33, 37, 41; --bs-btn-active-color: #fff; --bs-btn-active-bg: #212529; --bs-btn-active-border-color: #212529; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); --bs-btn-disabled-color: #212529; --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: #212529; --bs-gradient: none; } .btn-link { --bs-btn-font-weight: 400; --bs-btn-color: var(--bs-link-color); --bs-btn-bg: transparent; --bs-btn-border-color: transparent; --bs-btn-hover-color: var(--bs-link-hover-color); --bs-btn-hover-border-color: transparent; --bs-btn-active-color: var(--bs-link-hover-color); --bs-btn-active-border-color: transparent; --bs-btn-disabled-color: #6c757d; --bs-btn-disabled-border-color: transparent; --bs-btn-box-shadow: 0 0 0 #000; --bs-btn-focus-shadow-rgb: 49, 132, 253; text-decoration: underline; } .btn-link:focus-visible { color: var(--bs-btn-color); } .btn-link:hover { color: var(--bs-btn-hover-color); } .btn-group-lg > .btn, .btn-lg { --bs-btn-padding-y: 0.5rem; --bs-btn-padding-x: 1rem; --bs-btn-font-size: 1.25rem; --bs-btn-border-radius: var(--bs-border-radius-lg); } .btn-group-sm > .btn, .btn-sm { --bs-btn-padding-y: 0.25rem; --bs-btn-padding-x: 0.5rem; --bs-btn-font-size: 0.875rem; --bs-btn-border-radius: var(--bs-border-radius-sm); } .fade { transition: opacity 0.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 0.35s ease; } @media (prefers-reduced-motion: reduce) { .collapsing { transition: none; } } .collapsing.collapse-horizontal { width: 0; height: auto; transition: width 0.35s ease; } @media (prefers-reduced-motion: reduce) { .collapsing.collapse-horizontal { transition: none; } } .dropdown, .dropdown-center, .dropend, .dropstart, .dropup, .dropup-center { position: relative; } .dropdown-toggle { white-space: nowrap; } .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid; border-right: 0.3em solid transparent; border-bottom: 0; border-left: 0.3em solid transparent; } .dropdown-toggle:empty::after { margin-left: 0; } .dropdown-menu { --bs-dropdown-zindex: 1000; --bs-dropdown-min-width: 10rem; --bs-dropdown-padding-x: 0; --bs-dropdown-padding-y: 0.5rem; --bs-dropdown-spacer: 0.125rem; --bs-dropdown-font-size: 1rem; --bs-dropdown-color: var(--bs-body-color); --bs-dropdown-bg: var(--bs-body-bg); --bs-dropdown-border-color: var(--bs-border-color-translucent); --bs-dropdown-border-radius: var(--bs-border-radius); --bs-dropdown-border-width: var(--bs-border-width); --bs-dropdown-inner-border-radius: calc( var(--bs-border-radius) - var(--bs-border-width) ); --bs-dropdown-divider-bg: var(--bs-border-color-translucent); --bs-dropdown-divider-margin-y: 0.5rem; --bs-dropdown-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); --bs-dropdown-link-color: var(--bs-body-color); --bs-dropdown-link-hover-color: var(--bs-body-color); --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg); --bs-dropdown-link-active-color: #fff; --bs-dropdown-link-active-bg: #0d6efd; --bs-dropdown-link-disabled-color: var(--bs-tertiary-color); --bs-dropdown-item-padding-x: 1rem; --bs-dropdown-item-padding-y: 0.25rem; --bs-dropdown-header-color: #6c757d; --bs-dropdown-header-padding-x: 1rem; --bs-dropdown-header-padding-y: 0.5rem; position: absolute; z-index: var(--bs-dropdown-zindex); display: none; min-width: var(--bs-dropdown-min-width); padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x); margin: 0; font-size: var(--bs-dropdown-font-size); color: var(--bs-dropdown-color); text-align: left; list-style: none; background-color: var(--bs-dropdown-bg); background-clip: padding-box; border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color); border-radius: var(--bs-dropdown-border-radius); } .dropdown-menu[data-bs-popper] { top: 100%; left: 0; margin-top: var(--bs-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(--bs-dropdown-spacer); } .dropup .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0; border-right: 0.3em solid transparent; border-bottom: 0.3em solid; border-left: 0.3em solid transparent; } .dropup .dropdown-toggle:empty::after { margin-left: 0; } .dropend .dropdown-menu[data-bs-popper] { top: 0; right: auto; left: 100%; margin-top: 0; margin-left: var(--bs-dropdown-spacer); } .dropend .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid transparent; border-right: 0; border-bottom: 0.3em solid transparent; border-left: 0.3em solid; } .dropend .dropdown-toggle:empty::after { margin-left: 0; } .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(--bs-dropdown-spacer); } .dropstart .dropdown-toggle::after { display: inline-block; margin-left: 0.255em; vertical-align: 0.255em; content: ""; } .dropstart .dropdown-toggle::after { display: none; } .dropstart .dropdown-toggle::before { display: inline-block; margin-right: 0.255em; vertical-align: 0.255em; content: ""; border-top: 0.3em solid transparent; border-right: 0.3em solid; border-bottom: 0.3em solid transparent; } .dropstart .dropdown-toggle:empty::after { margin-left: 0; } .dropstart .dropdown-toggle::before { vertical-align: 0; } .dropdown-divider { height: 0; margin: var(--bs-dropdown-divider-margin-y) 0; overflow: hidden; border-top: 1px solid var(--bs-dropdown-divider-bg); opacity: 1; } .dropdown-item { display: block; width: 100%; padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); clear: both; font-weight: 400; color: var(--bs-dropdown-link-color); text-align: inherit; text-decoration: none; white-space: nowrap; background-color: transparent; border: 0; border-radius: var(--bs-dropdown-item-border-radius, 0); } .dropdown-item:focus, .dropdown-item:hover { color: var(--bs-dropdown-link-hover-color); background-color: var(--bs-dropdown-link-hover-bg); } .dropdown-item.active, .dropdown-item:active { color: var(--bs-dropdown-link-active-color); text-decoration: none; background-color: var(--bs-dropdown-link-active-bg); } .dropdown-item.disabled, .dropdown-item:disabled { color: var(--bs-dropdown-link-disabled-color); pointer-events: none; background-color: transparent; } .dropdown-menu.show { display: block; } .dropdown-header { display: block; padding: var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x); margin-bottom: 0; font-size: 0.875rem; color: var(--bs-dropdown-header-color); white-space: nowrap; } .dropdown-item-text { display: block; padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); color: var(--bs-dropdown-link-color); } .dropdown-menu-dark { --bs-dropdown-color: #dee2e6; --bs-dropdown-bg: #343a40; --bs-dropdown-border-color: var(--bs-border-color-translucent); --bs-dropdown-box-shadow: ; --bs-dropdown-link-color: #dee2e6; --bs-dropdown-link-hover-color: #fff; --bs-dropdown-divider-bg: var(--bs-border-color-translucent); --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15); --bs-dropdown-link-active-color: #fff; --bs-dropdown-link-active-bg: #0d6efd; --bs-dropdown-link-disabled-color: #adb5bd; --bs-dropdown-header-color: #adb5bd; } .btn-group, .btn-group-vertical { position: relative; display: inline-flex; vertical-align: middle; } .btn-group-vertical > .btn, .btn-group > .btn { position: relative; flex: 1 1 auto; } .btn-group-vertical > .btn-check:checked + .btn, .btn-group-vertical > .btn-check:focus + .btn, .btn-group-vertical > .btn.active, .btn-group-vertical > .btn:active, .btn-group-vertical > .btn:focus, .btn-group-vertical > .btn:hover, .btn-group > .btn-check:checked + .btn, .btn-group > .btn-check:focus + .btn, .btn-group > .btn.active, .btn-group > .btn:active, .btn-group > .btn:focus, .btn-group > .btn:hover { 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(--bs-border-radius); } .btn-group > .btn-group:not(:first-child), .btn-group > :not(.btn-check:first-child) + .btn { margin-left: calc(var(--bs-border-width) * -1); } .btn-group > .btn-group:not(:last-child) > .btn, .btn-group > .btn.dropdown-toggle-split:first-child, .btn-group > .btn:not(:last-child):not(.dropdown-toggle) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn-group:not(:first-child) > .btn, .btn-group > .btn:nth-child(n + 3), .btn-group > :not(.btn-check) + .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; } .dropdown-toggle-split { padding-right: 0.5625rem; padding-left: 0.5625rem; } .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after { margin-left: 0; } .dropstart .dropdown-toggle-split::before { margin-right: 0; } .btn-group-sm > .btn + .dropdown-toggle-split, .btn-sm + .dropdown-toggle-split { padding-right: 0.375rem; padding-left: 0.375rem; } .btn-group-lg > .btn + .dropdown-toggle-split, .btn-lg + .dropdown-toggle-split { padding-right: 0.75rem; padding-left: 0.75rem; } .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-group:not(:first-child), .btn-group-vertical > .btn:not(:first-child) { margin-top: calc(var(--bs-border-width) * -1); } .btn-group-vertical > .btn-group:not(:last-child) > .btn, .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle) { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn-group:not(:first-child) > .btn, .btn-group-vertical > .btn ~ .btn { border-top-left-radius: 0; border-top-right-radius: 0; } .nav { --bs-nav-link-padding-x: 1rem; --bs-nav-link-padding-y: 0.5rem; --bs-nav-link-font-weight: ; --bs-nav-link-color: var(--bs-link-color); --bs-nav-link-hover-color: var(--bs-link-hover-color); --bs-nav-link-disabled-color: var(--bs-secondary-color); display: flex; flex-wrap: wrap; padding-left: 0; margin-bottom: 0; list-style: none; } .nav-link { display: block; padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x); font-size: var(--bs-nav-link-font-size); font-weight: var(--bs-nav-link-font-weight); color: var(--bs-nav-link-color); text-decoration: none; background: 0 0; border: 0; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .nav-link { transition: none; } } .nav-link:focus, .nav-link:hover { color: var(--bs-nav-link-hover-color); } .nav-link:focus-visible { outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .nav-link.disabled { color: var(--bs-nav-link-disabled-color); pointer-events: none; cursor: default; } .nav-tabs { --bs-nav-tabs-border-width: var(--bs-border-width); --bs-nav-tabs-border-color: var(--bs-border-color); --bs-nav-tabs-border-radius: var(--bs-border-radius); --bs-nav-tabs-link-hover-border-color: var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color); --bs-nav-tabs-link-active-color: var(--bs-emphasis-color); --bs-nav-tabs-link-active-bg: var(--bs-body-bg); --bs-nav-tabs-link-active-border-color: var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg); border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color); } .nav-tabs .nav-link { margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width)); border: var(--bs-nav-tabs-border-width) solid transparent; border-top-left-radius: var(--bs-nav-tabs-border-radius); border-top-right-radius: var(--bs-nav-tabs-border-radius); } .nav-tabs .nav-link:focus, .nav-tabs .nav-link:hover { isolation: isolate; border-color: var(--bs-nav-tabs-link-hover-border-color); } .nav-tabs .nav-link.disabled, .nav-tabs .nav-link:disabled { color: var(--bs-nav-link-disabled-color); background-color: transparent; border-color: transparent; } .nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active { color: var(--bs-nav-tabs-link-active-color); background-color: var(--bs-nav-tabs-link-active-bg); border-color: var(--bs-nav-tabs-link-active-border-color); } .nav-tabs .dropdown-menu { margin-top: calc(-1 * var(--bs-nav-tabs-border-width)); border-top-left-radius: 0; border-top-right-radius: 0; } .nav-pills { --bs-nav-pills-border-radius: var(--bs-border-radius); --bs-nav-pills-link-active-color: #fff; --bs-nav-pills-link-active-bg: #0d6efd; } .nav-pills .nav-link { border-radius: var(--bs-nav-pills-border-radius); } .nav-pills .nav-link:disabled { color: var(--bs-nav-link-disabled-color); background-color: transparent; border-color: transparent; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: var(--bs-nav-pills-link-active-color); background-color: var(--bs-nav-pills-link-active-bg); } .nav-underline { --bs-nav-underline-gap: 1rem; --bs-nav-underline-border-width: 0.125rem; --bs-nav-underline-link-active-color: var(--bs-emphasis-color); gap: var(--bs-nav-underline-gap); } .nav-underline .nav-link { padding-right: 0; padding-left: 0; border-bottom: var(--bs-nav-underline-border-width) solid transparent; } .nav-underline .nav-link:focus, .nav-underline .nav-link:hover { border-bottom-color: currentcolor; } .nav-underline .nav-link.active, .nav-underline .show > .nav-link { font-weight: 700; color: var(--bs-nav-underline-link-active-color); border-bottom-color: currentcolor; } .nav-fill .nav-item, .nav-fill > .nav-link { flex: 1 1 auto; text-align: center; } .nav-justified .nav-item, .nav-justified > .nav-link { 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 { --bs-navbar-padding-x: 0; --bs-navbar-padding-y: 0.5rem; --bs-navbar-color: rgba(var(--bs-emphasis-color-rgb), 0.65); --bs-navbar-hover-color: rgba(var(--bs-emphasis-color-rgb), 0.8); --bs-navbar-disabled-color: rgba(var(--bs-emphasis-color-rgb), 0.3); --bs-navbar-active-color: rgba(var(--bs-emphasis-color-rgb), 1); --bs-navbar-brand-padding-y: 0.3125rem; --bs-navbar-brand-margin-end: 1rem; --bs-navbar-brand-font-size: 1.25rem; --bs-navbar-brand-color: rgba(var(--bs-emphasis-color-rgb), 1); --bs-navbar-brand-hover-color: rgba(var(--bs-emphasis-color-rgb), 1); --bs-navbar-nav-link-padding-x: 0.5rem; --bs-navbar-toggler-padding-y: 0.25rem; --bs-navbar-toggler-padding-x: 0.75rem; --bs-navbar-toggler-font-size: 1.25rem; --bs-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%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); --bs-navbar-toggler-border-color: rgba(var(--bs-emphasis-color-rgb), 0.15); --bs-navbar-toggler-border-radius: var(--bs-border-radius); --bs-navbar-toggler-focus-width: 0.25rem; --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out; position: relative; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x); } .navbar > .container, .navbar > .container-fluid, .navbar > .container-lg, .navbar > .container-md, .navbar > .container-sm, .navbar > .container-xl, .navbar > .container-xxl { display: flex; flex-wrap: inherit; align-items: center; justify-content: space-between; } .navbar-brand { padding-top: var(--bs-navbar-brand-padding-y); padding-bottom: var(--bs-navbar-brand-padding-y); margin-right: var(--bs-navbar-brand-margin-end); font-size: var(--bs-navbar-brand-font-size); color: var(--bs-navbar-brand-color); text-decoration: none; white-space: nowrap; } .navbar-brand:focus, .navbar-brand:hover { color: var(--bs-navbar-brand-hover-color); } .navbar-nav { --bs-nav-link-padding-x: 0; --bs-nav-link-padding-y: 0.5rem; --bs-nav-link-font-weight: ; --bs-nav-link-color: var(--bs-navbar-color); --bs-nav-link-hover-color: var(--bs-navbar-hover-color); --bs-nav-link-disabled-color: var(--bs-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(--bs-navbar-active-color); } .navbar-nav .dropdown-menu { position: static; } .navbar-text { padding-top: 0.5rem; padding-bottom: 0.5rem; color: var(--bs-navbar-color); } .navbar-text a, .navbar-text a:focus, .navbar-text a:hover { color: var(--bs-navbar-active-color); } .navbar-collapse { flex-basis: 100%; flex-grow: 1; align-items: center; } .navbar-toggler { padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x); font-size: var(--bs-navbar-toggler-font-size); line-height: 1; color: var(--bs-navbar-color); background-color: transparent; border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color); border-radius: var(--bs-navbar-toggler-border-radius); transition: var(--bs-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(--bs-navbar-toggler-focus-width); } .navbar-toggler-icon { display: inline-block; width: 1.5em; height: 1.5em; vertical-align: middle; background-image: var(--bs-navbar-toggler-icon-bg); background-repeat: no-repeat; background-position: center; background-size: 100%; } .navbar-nav-scroll { max-height: var(--bs-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(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-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; 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(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-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; 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(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-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; 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(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-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; 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(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-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; 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(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-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; 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"] { --bs-navbar-color: rgba(255, 255, 255, 0.55); --bs-navbar-hover-color: rgba(255, 255, 255, 0.75); --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25); --bs-navbar-active-color: #fff; --bs-navbar-brand-color: #fff; --bs-navbar-brand-hover-color: #fff; --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1); --bs-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.55%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 { --bs-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.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .card { --bs-card-spacer-y: 1rem; --bs-card-spacer-x: 1rem; --bs-card-title-spacer-y: 0.5rem; --bs-card-title-color: ; --bs-card-subtitle-color: ; --bs-card-border-width: var(--bs-border-width); --bs-card-border-color: var(--bs-border-color-translucent); --bs-card-border-radius: var(--bs-border-radius); --bs-card-box-shadow: ; --bs-card-inner-border-radius: calc( var(--bs-border-radius) - (var(--bs-border-width)) ); --bs-card-cap-padding-y: 0.5rem; --bs-card-cap-padding-x: 1rem; --bs-card-cap-bg: rgba(var(--bs-body-color-rgb), 0.03); --bs-card-cap-color: ; --bs-card-height: ; --bs-card-color: ; --bs-card-bg: var(--bs-body-bg); --bs-card-img-overlay-padding: 1rem; --bs-card-group-margin: 0.75rem; position: relative; display: flex; flex-direction: column; min-width: 0; height: var(--bs-card-height); color: var(--bs-body-color); word-wrap: break-word; background-color: var(--bs-card-bg); background-clip: border-box; border: var(--bs-card-border-width) solid var(--bs-card-border-color); border-radius: var(--bs-card-border-radius); } .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(--bs-card-inner-border-radius); border-top-right-radius: var(--bs-card-inner-border-radius); } .card > .list-group:last-child { border-bottom-width: 0; border-bottom-right-radius: var(--bs-card-inner-border-radius); border-bottom-left-radius: var(--bs-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(--bs-card-spacer-y) var(--bs-card-spacer-x); color: var(--bs-card-color); } .card-title { margin-bottom: var(--bs-card-title-spacer-y); color: var(--bs-card-title-color); } .card-subtitle { margin-top: calc(-0.5 * var(--bs-card-title-spacer-y)); margin-bottom: 0; color: var(--bs-card-subtitle-color); } .card-text:last-child { margin-bottom: 0; } .card-link + .card-link { margin-left: var(--bs-card-spacer-x); } .card-header { padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); margin-bottom: 0; color: var(--bs-card-cap-color); background-color: var(--bs-card-cap-bg); border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color); } .card-header:first-child { border-radius: var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0; } .card-footer { padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); color: var(--bs-card-cap-color); background-color: var(--bs-card-cap-bg); border-top: var(--bs-card-border-width) solid var(--bs-card-border-color); } .card-footer:last-child { border-radius: 0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius); } .card-header-tabs { margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); margin-bottom: calc(-1 * var(--bs-card-cap-padding-y)); margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); border-bottom: 0; } .card-header-tabs .nav-link.active { background-color: var(--bs-card-bg); border-bottom-color: var(--bs-card-bg); } .card-header-pills { margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); } .card-img-overlay { position: absolute; top: 0; right: 0; bottom: 0; left: 0; padding: var(--bs-card-img-overlay-padding); border-radius: var(--bs-card-inner-border-radius); } .card-img, .card-img-bottom, .card-img-top { width: 100%; } .card-img, .card-img-top { border-top-left-radius: var(--bs-card-inner-border-radius); border-top-right-radius: var(--bs-card-inner-border-radius); } .card-img, .card-img-bottom { border-bottom-right-radius: var(--bs-card-inner-border-radius); border-bottom-left-radius: var(--bs-card-inner-border-radius); } .card-group > .card { margin-bottom: var(--bs-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-header, .card-group > .card:not(:last-child) .card-img-top { border-top-right-radius: 0; } .card-group > .card:not(:last-child) .card-footer, .card-group > .card:not(:last-child) .card-img-bottom { 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-header, .card-group > .card:not(:first-child) .card-img-top { border-top-left-radius: 0; } .card-group > .card:not(:first-child) .card-footer, .card-group > .card:not(:first-child) .card-img-bottom { border-bottom-left-radius: 0; } } .accordion { --bs-accordion-color: var(--bs-body-color); --bs-accordion-bg: var(--bs-body-bg); --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease; --bs-accordion-border-color: var(--bs-border-color); --bs-accordion-border-width: var(--bs-border-width); --bs-accordion-border-radius: var(--bs-border-radius); --bs-accordion-inner-border-radius: calc( var(--bs-border-radius) - (var(--bs-border-width)) ); --bs-accordion-btn-padding-x: 1.25rem; --bs-accordion-btn-padding-y: 1rem; --bs-accordion-btn-color: var(--bs-body-color); --bs-accordion-btn-bg: var(--bs-accordion-bg); --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%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"); --bs-accordion-btn-icon-width: 1.25rem; --bs-accordion-btn-icon-transform: rotate(-180deg); --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out; --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23052c65'%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"); --bs-accordion-btn-focus-border-color: #86b7fe; --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); --bs-accordion-body-padding-x: 1.25rem; --bs-accordion-body-padding-y: 1rem; --bs-accordion-active-color: var(--bs-primary-text-emphasis); --bs-accordion-active-bg: var(--bs-primary-bg-subtle); } .accordion-button { position: relative; display: flex; align-items: center; width: 100%; padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x); font-size: 1rem; color: var(--bs-accordion-btn-color); text-align: left; background-color: var(--bs-accordion-btn-bg); border: 0; border-radius: 0; overflow-anchor: none; transition: var(--bs-accordion-transition); } @media (prefers-reduced-motion: reduce) { .accordion-button { transition: none; } } .accordion-button:not(.collapsed) { color: var(--bs-accordion-active-color); background-color: var(--bs-accordion-active-bg); box-shadow: inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color); } .accordion-button:not(.collapsed)::after { background-image: var(--bs-accordion-btn-active-icon); transform: var(--bs-accordion-btn-icon-transform); } .accordion-button::after { flex-shrink: 0; width: var(--bs-accordion-btn-icon-width); height: var(--bs-accordion-btn-icon-width); margin-left: auto; content: ""; background-image: var(--bs-accordion-btn-icon); background-repeat: no-repeat; background-size: var(--bs-accordion-btn-icon-width); transition: var(--bs-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; border-color: var(--bs-accordion-btn-focus-border-color); outline: 0; box-shadow: var(--bs-accordion-btn-focus-box-shadow); } .accordion-header { margin-bottom: 0; } .accordion-item { color: var(--bs-accordion-color); background-color: var(--bs-accordion-bg); border: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color); } .accordion-item:first-of-type { border-top-left-radius: var(--bs-accordion-border-radius); border-top-right-radius: var(--bs-accordion-border-radius); } .accordion-item:first-of-type .accordion-button { border-top-left-radius: var(--bs-accordion-inner-border-radius); border-top-right-radius: var(--bs-accordion-inner-border-radius); } .accordion-item:not(:first-of-type) { border-top: 0; } .accordion-item:last-of-type { border-bottom-right-radius: var(--bs-accordion-border-radius); border-bottom-left-radius: var(--bs-accordion-border-radius); } .accordion-item:last-of-type .accordion-button.collapsed { border-bottom-right-radius: var(--bs-accordion-inner-border-radius); border-bottom-left-radius: var(--bs-accordion-inner-border-radius); } .accordion-item:last-of-type .accordion-collapse { border-bottom-right-radius: var(--bs-accordion-border-radius); border-bottom-left-radius: var(--bs-accordion-border-radius); } .accordion-body { padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x); } .accordion-flush .accordion-collapse { border-width: 0; } .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-button, .accordion-flush .accordion-item .accordion-button.collapsed { border-radius: 0; } [data-bs-theme="dark"] .accordion-button::after { --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%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"); --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%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 { --bs-breadcrumb-padding-x: 0; --bs-breadcrumb-padding-y: 0; --bs-breadcrumb-margin-bottom: 1rem; --bs-breadcrumb-bg: ; --bs-breadcrumb-border-radius: ; --bs-breadcrumb-divider-color: var(--bs-secondary-color); --bs-breadcrumb-item-padding-x: 0.5rem; --bs-breadcrumb-item-active-color: var(--bs-secondary-color); display: flex; flex-wrap: wrap; padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x); margin-bottom: var(--bs-breadcrumb-margin-bottom); font-size: var(--bs-breadcrumb-font-size); list-style: none; background-color: var(--bs-breadcrumb-bg); border-radius: var(--bs-breadcrumb-border-radius); } .breadcrumb-item + .breadcrumb-item { padding-left: var(--bs-breadcrumb-item-padding-x); } .breadcrumb-item + .breadcrumb-item::before { float: left; padding-right: var(--bs-breadcrumb-item-padding-x); color: var(--bs-breadcrumb-divider-color); content: var(--bs-breadcrumb-divider, "/"); } .breadcrumb-item.active { color: var(--bs-breadcrumb-item-active-color); } .pagination { --bs-pagination-padding-x: 0.75rem; --bs-pagination-padding-y: 0.375rem; --bs-pagination-font-size: 1rem; --bs-pagination-color: var(--bs-link-color); --bs-pagination-bg: var(--bs-body-bg); --bs-pagination-border-width: var(--bs-border-width); --bs-pagination-border-color: var(--bs-border-color); --bs-pagination-border-radius: var(--bs-border-radius); --bs-pagination-hover-color: var(--bs-link-hover-color); --bs-pagination-hover-bg: var(--bs-tertiary-bg); --bs-pagination-hover-border-color: var(--bs-border-color); --bs-pagination-focus-color: var(--bs-link-hover-color); --bs-pagination-focus-bg: var(--bs-secondary-bg); --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); --bs-pagination-active-color: #fff; --bs-pagination-active-bg: #0d6efd; --bs-pagination-active-border-color: #0d6efd; --bs-pagination-disabled-color: var(--bs-secondary-color); --bs-pagination-disabled-bg: var(--bs-secondary-bg); --bs-pagination-disabled-border-color: var(--bs-border-color); display: flex; padding-left: 0; list-style: none; } .page-link { position: relative; display: block; padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x); font-size: var(--bs-pagination-font-size); color: var(--bs-pagination-color); text-decoration: none; background-color: var(--bs-pagination-bg); border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color); transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { .page-link { transition: none; } } .page-link:hover { z-index: 2; color: var(--bs-pagination-hover-color); background-color: var(--bs-pagination-hover-bg); border-color: var(--bs-pagination-hover-border-color); } .page-link:focus { z-index: 3; color: var(--bs-pagination-focus-color); background-color: var(--bs-pagination-focus-bg); outline: 0; box-shadow: var(--bs-pagination-focus-box-shadow); } .active > .page-link, .page-link.active { z-index: 3; color: var(--bs-pagination-active-color); background-color: var(--bs-pagination-active-bg); border-color: var(--bs-pagination-active-border-color); } .disabled > .page-link, .page-link.disabled { color: var(--bs-pagination-disabled-color); pointer-events: none; background-color: var(--bs-pagination-disabled-bg); border-color: var(--bs-pagination-disabled-border-color); } .page-item:not(:first-child) .page-link { margin-left: calc(var(--bs-border-width) * -1); } .page-item:first-child .page-link { border-top-left-radius: var(--bs-pagination-border-radius); border-bottom-left-radius: var(--bs-pagination-border-radius); } .page-item:last-child .page-link { border-top-right-radius: var(--bs-pagination-border-radius); border-bottom-right-radius: var(--bs-pagination-border-radius); } .pagination-lg { --bs-pagination-padding-x: 1.5rem; --bs-pagination-padding-y: 0.75rem; --bs-pagination-font-size: 1.25rem; --bs-pagination-border-radius: var(--bs-border-radius-lg); } .pagination-sm { --bs-pagination-padding-x: 0.5rem; --bs-pagination-padding-y: 0.25rem; --bs-pagination-font-size: 0.875rem; --bs-pagination-border-radius: var(--bs-border-radius-sm); } .badge { --bs-badge-padding-x: 0.65em; --bs-badge-padding-y: 0.35em; --bs-badge-font-size: 0.75em; --bs-badge-font-weight: 700; --bs-badge-color: #fff; --bs-badge-border-radius: var(--bs-border-radius); display: inline-block; padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x); font-size: var(--bs-badge-font-size); font-weight: var(--bs-badge-font-weight); line-height: 1; color: var(--bs-badge-color); text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: var(--bs-badge-border-radius); } .badge:empty { display: none; } .btn .badge { position: relative; top: -1px; } .alert { --bs-alert-bg: transparent; --bs-alert-padding-x: 1rem; --bs-alert-padding-y: 1rem; --bs-alert-margin-bottom: 1rem; --bs-alert-color: inherit; --bs-alert-border-color: transparent; --bs-alert-border: var(--bs-border-width) solid var(--bs-alert-border-color); --bs-alert-border-radius: var(--bs-border-radius); --bs-alert-link-color: inherit; position: relative; padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x); margin-bottom: var(--bs-alert-margin-bottom); color: var(--bs-alert-color); background-color: var(--bs-alert-bg); border: var(--bs-alert-border); border-radius: var(--bs-alert-border-radius); } .alert-heading { color: inherit; } .alert-link { font-weight: 700; color: var(--bs-alert-link-color); } .alert-dismissible { padding-right: 3rem; } .alert-dismissible .btn-close { position: absolute; top: 0; right: 0; z-index: 2; padding: 1.25rem 1rem; } .alert-primary { --bs-alert-color: var(--bs-primary-text-emphasis); --bs-alert-bg: var(--bs-primary-bg-subtle); --bs-alert-border-color: var(--bs-primary-border-subtle); --bs-alert-link-color: var(--bs-primary-text-emphasis); } .alert-secondary { --bs-alert-color: var(--bs-secondary-text-emphasis); --bs-alert-bg: var(--bs-secondary-bg-subtle); --bs-alert-border-color: var(--bs-secondary-border-subtle); --bs-alert-link-color: var(--bs-secondary-text-emphasis); } .alert-success { --bs-alert-color: var(--bs-success-text-emphasis); --bs-alert-bg: var(--bs-success-bg-subtle); --bs-alert-border-color: var(--bs-success-border-subtle); --bs-alert-link-color: var(--bs-success-text-emphasis); } .alert-info { --bs-alert-color: var(--bs-info-text-emphasis); --bs-alert-bg: var(--bs-info-bg-subtle); --bs-alert-border-color: var(--bs-info-border-subtle); --bs-alert-link-color: var(--bs-info-text-emphasis); } .alert-warning { --bs-alert-color: var(--bs-warning-text-emphasis); --bs-alert-bg: var(--bs-warning-bg-subtle); --bs-alert-border-color: var(--bs-warning-border-subtle); --bs-alert-link-color: var(--bs-warning-text-emphasis); } .alert-danger { --bs-alert-color: var(--bs-danger-text-emphasis); --bs-alert-bg: var(--bs-danger-bg-subtle); --bs-alert-border-color: var(--bs-danger-border-subtle); --bs-alert-link-color: var(--bs-danger-text-emphasis); } .alert-light { --bs-alert-color: var(--bs-light-text-emphasis); --bs-alert-bg: var(--bs-light-bg-subtle); --bs-alert-border-color: var(--bs-light-border-subtle); --bs-alert-link-color: var(--bs-light-text-emphasis); } .alert-dark { --bs-alert-color: var(--bs-dark-text-emphasis); --bs-alert-bg: var(--bs-dark-bg-subtle); --bs-alert-border-color: var(--bs-dark-border-subtle); --bs-alert-link-color: var(--bs-dark-text-emphasis); } @keyframes progress-bar-stripes { 0% { background-position-x: 1rem; } } .progress, .progress-stacked { --bs-progress-height: 1rem; --bs-progress-font-size: 0.75rem; --bs-progress-bg: var(--bs-secondary-bg); --bs-progress-border-radius: var(--bs-border-radius); --bs-progress-box-shadow: var(--bs-box-shadow-inset); --bs-progress-bar-color: #fff; --bs-progress-bar-bg: #0d6efd; --bs-progress-bar-transition: width 0.6s ease; display: flex; height: var(--bs-progress-height); overflow: hidden; font-size: var(--bs-progress-font-size); background-color: var(--bs-progress-bg); border-radius: var(--bs-progress-border-radius); } .progress-bar { display: flex; flex-direction: column; justify-content: center; overflow: hidden; color: var(--bs-progress-bar-color); text-align: center; white-space: nowrap; background-color: var(--bs-progress-bar-bg); transition: var(--bs-progress-bar-transition); } @media (prefers-reduced-motion: reduce) { .progress-bar { transition: none; } } .progress-bar-striped { background-image: linear-gradient( 45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent ); background-size: var(--bs-progress-height) var(--bs-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 { --bs-list-group-color: var(--bs-body-color); --bs-list-group-bg: var(--bs-body-bg); --bs-list-group-border-color: var(--bs-border-color); --bs-list-group-border-width: var(--bs-border-width); --bs-list-group-border-radius: var(--bs-border-radius); --bs-list-group-item-padding-x: 1rem; --bs-list-group-item-padding-y: 0.5rem; --bs-list-group-action-color: var(--bs-secondary-color); --bs-list-group-action-hover-color: var(--bs-emphasis-color); --bs-list-group-action-hover-bg: var(--bs-tertiary-bg); --bs-list-group-action-active-color: var(--bs-body-color); --bs-list-group-action-active-bg: var(--bs-secondary-bg); --bs-list-group-disabled-color: var(--bs-secondary-color); --bs-list-group-disabled-bg: var(--bs-body-bg); --bs-list-group-active-color: #fff; --bs-list-group-active-bg: #0d6efd; --bs-list-group-active-border-color: #0d6efd; display: flex; flex-direction: column; padding-left: 0; margin-bottom: 0; border-radius: var(--bs-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(--bs-list-group-action-color); text-align: inherit; } .list-group-item-action:focus, .list-group-item-action:hover { z-index: 1; color: var(--bs-list-group-action-hover-color); text-decoration: none; background-color: var(--bs-list-group-action-hover-bg); } .list-group-item-action:active { color: var(--bs-list-group-action-active-color); background-color: var(--bs-list-group-action-active-bg); } .list-group-item { position: relative; display: block; padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x); color: var(--bs-list-group-color); text-decoration: none; background-color: var(--bs-list-group-bg); border: var(--bs-list-group-border-width) solid var(--bs-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(--bs-list-group-disabled-color); pointer-events: none; background-color: var(--bs-list-group-disabled-bg); } .list-group-item.active { z-index: 2; color: var(--bs-list-group-active-color); background-color: var(--bs-list-group-active-bg); border-color: var(--bs-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(--bs-list-group-border-width)); border-top-width: var(--bs-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(--bs-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(--bs-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(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-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(--bs-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(--bs-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(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal-sm > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-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(--bs-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(--bs-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(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal-md > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-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(--bs-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(--bs-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(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal-lg > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-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(--bs-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(--bs-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(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal-xl > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-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(--bs-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(--bs-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(--bs-list-group-border-width); border-left-width: 0; } .list-group-horizontal-xxl > .list-group-item + .list-group-item.active { margin-left: calc(-1 * var(--bs-list-group-border-width)); border-left-width: var(--bs-list-group-border-width); } } .list-group-flush { border-radius: 0; } .list-group-flush > .list-group-item { border-width: 0 0 var(--bs-list-group-border-width); } .list-group-flush > .list-group-item:last-child { border-bottom-width: 0; } .list-group-item-primary { --bs-list-group-color: var(--bs-primary-text-emphasis); --bs-list-group-bg: var(--bs-primary-bg-subtle); --bs-list-group-border-color: var(--bs-primary-border-subtle); --bs-list-group-action-hover-color: var(--bs-emphasis-color); --bs-list-group-action-hover-bg: var(--bs-primary-border-subtle); --bs-list-group-action-active-color: var(--bs-emphasis-color); --bs-list-group-action-active-bg: var(--bs-primary-border-subtle); --bs-list-group-active-color: var(--bs-primary-bg-subtle); --bs-list-group-active-bg: var(--bs-primary-text-emphasis); --bs-list-group-active-border-color: var(--bs-primary-text-emphasis); } .list-group-item-secondary { --bs-list-group-color: var(--bs-secondary-text-emphasis); --bs-list-group-bg: var(--bs-secondary-bg-subtle); --bs-list-group-border-color: var(--bs-secondary-border-subtle); --bs-list-group-action-hover-color: var(--bs-emphasis-color); --bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle); --bs-list-group-action-active-color: var(--bs-emphasis-color); --bs-list-group-action-active-bg: var(--bs-secondary-border-subtle); --bs-list-group-active-color: var(--bs-secondary-bg-subtle); --bs-list-group-active-bg: var(--bs-secondary-text-emphasis); --bs-list-group-active-border-color: var(--bs-secondary-text-emphasis); } .list-group-item-success { --bs-list-group-color: var(--bs-success-text-emphasis); --bs-list-group-bg: var(--bs-success-bg-subtle); --bs-list-group-border-color: var(--bs-success-border-subtle); --bs-list-group-action-hover-color: var(--bs-emphasis-color); --bs-list-group-action-hover-bg: var(--bs-success-border-subtle); --bs-list-group-action-active-color: var(--bs-emphasis-color); --bs-list-group-action-active-bg: var(--bs-success-border-subtle); --bs-list-group-active-color: var(--bs-success-bg-subtle); --bs-list-group-active-bg: var(--bs-success-text-emphasis); --bs-list-group-active-border-color: var(--bs-success-text-emphasis); } .list-group-item-info { --bs-list-group-color: var(--bs-info-text-emphasis); --bs-list-group-bg: var(--bs-info-bg-subtle); --bs-list-group-border-color: var(--bs-info-border-subtle); --bs-list-group-action-hover-color: var(--bs-emphasis-color); --bs-list-group-action-hover-bg: var(--bs-info-border-subtle); --bs-list-group-action-active-color: var(--bs-emphasis-color); --bs-list-group-action-active-bg: var(--bs-info-border-subtle); --bs-list-group-active-color: var(--bs-info-bg-subtle); --bs-list-group-active-bg: var(--bs-info-text-emphasis); --bs-list-group-active-border-color: var(--bs-info-text-emphasis); } .list-group-item-warning { --bs-list-group-color: var(--bs-warning-text-emphasis); --bs-list-group-bg: var(--bs-warning-bg-subtle); --bs-list-group-border-color: var(--bs-warning-border-subtle); --bs-list-group-action-hover-color: var(--bs-emphasis-color); --bs-list-group-action-hover-bg: var(--bs-warning-border-subtle); --bs-list-group-action-active-color: var(--bs-emphasis-color); --bs-list-group-action-active-bg: var(--bs-warning-border-subtle); --bs-list-group-active-color: var(--bs-warning-bg-subtle); --bs-list-group-active-bg: var(--bs-warning-text-emphasis); --bs-list-group-active-border-color: var(--bs-warning-text-emphasis); } .list-group-item-danger { --bs-list-group-color: var(--bs-danger-text-emphasis); --bs-list-group-bg: var(--bs-danger-bg-subtle); --bs-list-group-border-color: var(--bs-danger-border-subtle); --bs-list-group-action-hover-color: var(--bs-emphasis-color); --bs-list-group-action-hover-bg: var(--bs-danger-border-subtle); --bs-list-group-action-active-color: var(--bs-emphasis-color); --bs-list-group-action-active-bg: var(--bs-danger-border-subtle); --bs-list-group-active-color: var(--bs-danger-bg-subtle); --bs-list-group-active-bg: var(--bs-danger-text-emphasis); --bs-list-group-active-border-color: var(--bs-danger-text-emphasis); } .list-group-item-light { --bs-list-group-color: var(--bs-light-text-emphasis); --bs-list-group-bg: var(--bs-light-bg-subtle); --bs-list-group-border-color: var(--bs-light-border-subtle); --bs-list-group-action-hover-color: var(--bs-emphasis-color); --bs-list-group-action-hover-bg: var(--bs-light-border-subtle); --bs-list-group-action-active-color: var(--bs-emphasis-color); --bs-list-group-action-active-bg: var(--bs-light-border-subtle); --bs-list-group-active-color: var(--bs-light-bg-subtle); --bs-list-group-active-bg: var(--bs-light-text-emphasis); --bs-list-group-active-border-color: var(--bs-light-text-emphasis); } .list-group-item-dark { --bs-list-group-color: var(--bs-dark-text-emphasis); --bs-list-group-bg: var(--bs-dark-bg-subtle); --bs-list-group-border-color: var(--bs-dark-border-subtle); --bs-list-group-action-hover-color: var(--bs-emphasis-color); --bs-list-group-action-hover-bg: var(--bs-dark-border-subtle); --bs-list-group-action-active-color: var(--bs-emphasis-color); --bs-list-group-action-active-bg: var(--bs-dark-border-subtle); --bs-list-group-active-color: var(--bs-dark-bg-subtle); --bs-list-group-active-bg: var(--bs-dark-text-emphasis); --bs-list-group-active-border-color: var(--bs-dark-text-emphasis); } .btn-close { --bs-btn-close-color: #000; --bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%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"); --bs-btn-close-opacity: 0.5; --bs-btn-close-hover-opacity: 0.75; --bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); --bs-btn-close-focus-opacity: 1; --bs-btn-close-disabled-opacity: 0.25; --bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%); box-sizing: content-box; width: 1em; height: 1em; padding: 0.25em 0.25em; color: var(--bs-btn-close-color); background: transparent var(--bs-btn-close-bg) center/1em auto no-repeat; border: 0; border-radius: 0.375rem; opacity: var(--bs-btn-close-opacity); } .btn-close:hover { color: var(--bs-btn-close-color); text-decoration: none; opacity: var(--bs-btn-close-hover-opacity); } .btn-close:focus { outline: 0; box-shadow: var(--bs-btn-close-focus-shadow); opacity: var(--bs-btn-close-focus-opacity); } .btn-close.disabled, .btn-close:disabled { pointer-events: none; -webkit-user-select: none; -moz-user-select: none; user-select: none; opacity: var(--bs-btn-close-disabled-opacity); } .btn-close-white { filter: var(--bs-btn-close-white-filter); } [data-bs-theme="dark"] .btn-close { filter: var(--bs-btn-close-white-filter); } .toast { --bs-toast-zindex: 1090; --bs-toast-padding-x: 0.75rem; --bs-toast-padding-y: 0.5rem; --bs-toast-spacing: 1.5rem; --bs-toast-max-width: 350px; --bs-toast-font-size: 0.875rem; --bs-toast-color: ; --bs-toast-bg: rgba(var(--bs-body-bg-rgb), 0.85); --bs-toast-border-width: var(--bs-border-width); --bs-toast-border-color: var(--bs-border-color-translucent); --bs-toast-border-radius: var(--bs-border-radius); --bs-toast-box-shadow: var(--bs-box-shadow); --bs-toast-header-color: var(--bs-secondary-color); --bs-toast-header-bg: rgba(var(--bs-body-bg-rgb), 0.85); --bs-toast-header-border-color: var(--bs-border-color-translucent); width: var(--bs-toast-max-width); max-width: 100%; font-size: var(--bs-toast-font-size); color: var(--bs-toast-color); pointer-events: auto; background-color: var(--bs-toast-bg); background-clip: padding-box; border: var(--bs-toast-border-width) solid var(--bs-toast-border-color); box-shadow: var(--bs-toast-box-shadow); border-radius: var(--bs-toast-border-radius); } .toast.showing { opacity: 0; } .toast:not(.show) { display: none; } .toast-container { --bs-toast-zindex: 1090; position: absolute; z-index: var(--bs-toast-zindex); width: -webkit-max-content; width: -moz-max-content; width: max-content; max-width: 100%; pointer-events: none; } .toast-container > :not(:last-child) { margin-bottom: var(--bs-toast-spacing); } .toast-header { display: flex; align-items: center; padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x); color: var(--bs-toast-header-color); background-color: var(--bs-toast-header-bg); background-clip: padding-box; border-bottom: var(--bs-toast-border-width) solid var(--bs-toast-header-border-color); border-top-left-radius: calc( var(--bs-toast-border-radius) - var(--bs-toast-border-width) ); border-top-right-radius: calc( var(--bs-toast-border-radius) - var(--bs-toast-border-width) ); } .toast-header .btn-close { margin-right: calc(-0.5 * var(--bs-toast-padding-x)); margin-left: var(--bs-toast-padding-x); } .toast-body { padding: var(--bs-toast-padding-x); word-wrap: break-word; } .modal { --bs-modal-zindex: 1055; --bs-modal-width: 500px; --bs-modal-padding: 1rem; --bs-modal-margin: 0.5rem; --bs-modal-color: ; --bs-modal-bg: var(--bs-body-bg); --bs-modal-border-color: var(--bs-border-color-translucent); --bs-modal-border-width: var(--bs-border-width); --bs-modal-border-radius: var(--bs-border-radius-lg); --bs-modal-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); --bs-modal-inner-border-radius: calc( var(--bs-border-radius-lg) - (var(--bs-border-width)) ); --bs-modal-header-padding-x: 1rem; --bs-modal-header-padding-y: 1rem; --bs-modal-header-padding: 1rem 1rem; --bs-modal-header-border-color: var(--bs-border-color); --bs-modal-header-border-width: var(--bs-border-width); --bs-modal-title-line-height: 1.5; --bs-modal-footer-gap: 0.5rem; --bs-modal-footer-bg: ; --bs-modal-footer-border-color: var(--bs-border-color); --bs-modal-footer-border-width: var(--bs-border-width); position: fixed; top: 0; left: 0; z-index: var(--bs-modal-zindex); display: none; width: 100%; height: 100%; overflow-x: hidden; overflow-y: auto; outline: 0; } .modal-dialog { position: relative; width: auto; margin: var(--bs-modal-margin); pointer-events: none; } .modal.fade .modal-dialog { transition: transform 0.3s ease-out; transform: translate(0, -50px); } @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(--bs-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(--bs-modal-margin) * 2); } .modal-content { position: relative; display: flex; flex-direction: column; width: 100%; color: var(--bs-modal-color); pointer-events: auto; background-color: var(--bs-modal-bg); background-clip: padding-box; border: var(--bs-modal-border-width) solid var(--bs-modal-border-color); border-radius: var(--bs-modal-border-radius); outline: 0; } .modal-backdrop { --bs-backdrop-zindex: 1050; --bs-backdrop-bg: #000; --bs-backdrop-opacity: 0.5; position: fixed; top: 0; left: 0; z-index: var(--bs-backdrop-zindex); width: 100vw; height: 100vh; background-color: var(--bs-backdrop-bg); } .modal-backdrop.fade { opacity: 0; } .modal-backdrop.show { opacity: var(--bs-backdrop-opacity); } .modal-header { display: flex; flex-shrink: 0; align-items: center; justify-content: space-between; padding: var(--bs-modal-header-padding); border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color); border-top-left-radius: var(--bs-modal-inner-border-radius); border-top-right-radius: var(--bs-modal-inner-border-radius); } .modal-header .btn-close { padding: calc(var(--bs-modal-header-padding-y) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5); margin: calc(-0.5 * var(--bs-modal-header-padding-y)) calc(-0.5 * var(--bs-modal-header-padding-x)) calc(-0.5 * var(--bs-modal-header-padding-y)) auto; } .modal-title { margin-bottom: 0; line-height: var(--bs-modal-title-line-height); } .modal-body { position: relative; flex: 1 1 auto; padding: var(--bs-modal-padding); } .modal-footer { display: flex; flex-shrink: 0; flex-wrap: wrap; align-items: center; justify-content: flex-end; padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5); background-color: var(--bs-modal-footer-bg); border-top: var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color); border-bottom-right-radius: var(--bs-modal-inner-border-radius); border-bottom-left-radius: var(--bs-modal-inner-border-radius); } .modal-footer > * { margin: calc(var(--bs-modal-footer-gap) * 0.5); } @media (min-width: 576px) { .modal { --bs-modal-margin: 1.75rem; --bs-modal-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); } .modal-dialog { max-width: var(--bs-modal-width); margin-right: auto; margin-left: auto; } .modal-sm { --bs-modal-width: 300px; } } @media (min-width: 992px) { .modal-lg, .modal-xl { --bs-modal-width: 800px; } } @media (min-width: 1200px) { .modal-xl { --bs-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-footer, .modal-fullscreen .modal-header { 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-footer, .modal-fullscreen-sm-down .modal-header { 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-footer, .modal-fullscreen-md-down .modal-header { 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-footer, .modal-fullscreen-lg-down .modal-header { 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-footer, .modal-fullscreen-xl-down .modal-header { 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-footer, .modal-fullscreen-xxl-down .modal-header { border-radius: 0; } .modal-fullscreen-xxl-down .modal-body { overflow-y: auto; } } .tooltip { --bs-tooltip-zindex: 1080; --bs-tooltip-max-width: 200px; --bs-tooltip-padding-x: 0.5rem; --bs-tooltip-padding-y: 0.25rem; --bs-tooltip-margin: ; --bs-tooltip-font-size: 0.875rem; --bs-tooltip-color: var(--bs-body-bg); --bs-tooltip-bg: var(--bs-emphasis-color); --bs-tooltip-border-radius: var(--bs-border-radius); --bs-tooltip-opacity: 0.9; --bs-tooltip-arrow-width: 0.8rem; --bs-tooltip-arrow-height: 0.4rem; z-index: var(--bs-tooltip-zindex); display: block; margin: var(--bs-tooltip-margin); font-family: var(--bs-font-sans-serif); font-style: normal; font-weight: 400; line-height: 1.5; 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(--bs-tooltip-font-size); word-wrap: break-word; opacity: 0; } .tooltip.show { opacity: var(--bs-tooltip-opacity); } .tooltip .tooltip-arrow { display: block; width: var(--bs-tooltip-arrow-width); height: var(--bs-tooltip-arrow-height); } .tooltip .tooltip-arrow::before { position: absolute; content: ""; border-color: transparent; border-style: solid; } .bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow, .bs-tooltip-top .tooltip-arrow { bottom: calc(-1 * var(--bs-tooltip-arrow-height)); } .bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow::before, .bs-tooltip-top .tooltip-arrow::before { top: -1px; border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; border-top-color: var(--bs-tooltip-bg); } .bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow, .bs-tooltip-end .tooltip-arrow { left: calc(-1 * var(--bs-tooltip-arrow-height)); width: var(--bs-tooltip-arrow-height); height: var(--bs-tooltip-arrow-width); } .bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow::before, .bs-tooltip-end .tooltip-arrow::before { right: -1px; border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; border-right-color: var(--bs-tooltip-bg); } .bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow, .bs-tooltip-bottom .tooltip-arrow { top: calc(-1 * var(--bs-tooltip-arrow-height)); } .bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow::before, .bs-tooltip-bottom .tooltip-arrow::before { bottom: -1px; border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); border-bottom-color: var(--bs-tooltip-bg); } .bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow, .bs-tooltip-start .tooltip-arrow { right: calc(-1 * var(--bs-tooltip-arrow-height)); width: var(--bs-tooltip-arrow-height); height: var(--bs-tooltip-arrow-width); } .bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow::before, .bs-tooltip-start .tooltip-arrow::before { left: -1px; border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); border-left-color: var(--bs-tooltip-bg); } .tooltip-inner { max-width: var(--bs-tooltip-max-width); padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x); color: var(--bs-tooltip-color); text-align: center; background-color: var(--bs-tooltip-bg); border-radius: var(--bs-tooltip-border-radius); } .popover { --bs-popover-zindex: 1070; --bs-popover-max-width: 276px; --bs-popover-font-size: 0.875rem; --bs-popover-bg: var(--bs-body-bg); --bs-popover-border-width: var(--bs-border-width); --bs-popover-border-color: var(--bs-border-color-translucent); --bs-popover-border-radius: var(--bs-border-radius-lg); --bs-popover-inner-border-radius: calc( var(--bs-border-radius-lg) - var(--bs-border-width) ); --bs-popover-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); --bs-popover-header-padding-x: 1rem; --bs-popover-header-padding-y: 0.5rem; --bs-popover-header-font-size: 1rem; --bs-popover-header-color: inherit; --bs-popover-header-bg: var(--bs-secondary-bg); --bs-popover-body-padding-x: 1rem; --bs-popover-body-padding-y: 1rem; --bs-popover-body-color: var(--bs-body-color); --bs-popover-arrow-width: 1rem; --bs-popover-arrow-height: 0.5rem; --bs-popover-arrow-border: var(--bs-popover-border-color); z-index: var(--bs-popover-zindex); display: block; max-width: var(--bs-popover-max-width); font-family: var(--bs-font-sans-serif); font-style: normal; font-weight: 400; line-height: 1.5; 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(--bs-popover-font-size); word-wrap: break-word; background-color: var(--bs-popover-bg); background-clip: padding-box; border: var(--bs-popover-border-width) solid var(--bs-popover-border-color); border-radius: var(--bs-popover-border-radius); } .popover .popover-arrow { display: block; width: var(--bs-popover-arrow-width); height: var(--bs-popover-arrow-height); } .popover .popover-arrow::after, .popover .popover-arrow::before { position: absolute; display: block; content: ""; border-color: transparent; border-style: solid; border-width: 0; } .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow, .bs-popover-top > .popover-arrow { bottom: calc( -1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width) ); } .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow::before, .bs-popover-top > .popover-arrow::after, .bs-popover-top > .popover-arrow::before { border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; } .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow::before, .bs-popover-top > .popover-arrow::before { bottom: 0; border-top-color: var(--bs-popover-arrow-border); } .bs-popover-auto[data-popper-placement^="top"] > .popover-arrow::after, .bs-popover-top > .popover-arrow::after { bottom: var(--bs-popover-border-width); border-top-color: var(--bs-popover-bg); } .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow, .bs-popover-end > .popover-arrow { left: calc( -1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width) ); width: var(--bs-popover-arrow-height); height: var(--bs-popover-arrow-width); } .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow::before, .bs-popover-end > .popover-arrow::after, .bs-popover-end > .popover-arrow::before { border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; } .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow::before, .bs-popover-end > .popover-arrow::before { left: 0; border-right-color: var(--bs-popover-arrow-border); } .bs-popover-auto[data-popper-placement^="right"] > .popover-arrow::after, .bs-popover-end > .popover-arrow::after { left: var(--bs-popover-border-width); border-right-color: var(--bs-popover-bg); } .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow, .bs-popover-bottom > .popover-arrow { top: calc( -1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width) ); } .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::before, .bs-popover-bottom > .popover-arrow::after, .bs-popover-bottom > .popover-arrow::before { border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); } .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::before, .bs-popover-bottom > .popover-arrow::before { top: 0; border-bottom-color: var(--bs-popover-arrow-border); } .bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::after, .bs-popover-bottom > .popover-arrow::after { top: var(--bs-popover-border-width); border-bottom-color: var(--bs-popover-bg); } .bs-popover-auto[data-popper-placement^="bottom"] .popover-header::before, .bs-popover-bottom .popover-header::before { position: absolute; top: 0; left: 50%; display: block; width: var(--bs-popover-arrow-width); margin-left: calc(-0.5 * var(--bs-popover-arrow-width)); content: ""; border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg); } .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow, .bs-popover-start > .popover-arrow { right: calc( -1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width) ); width: var(--bs-popover-arrow-height); height: var(--bs-popover-arrow-width); } .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow::before, .bs-popover-start > .popover-arrow::after, .bs-popover-start > .popover-arrow::before { border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); } .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow::before, .bs-popover-start > .popover-arrow::before { right: 0; border-left-color: var(--bs-popover-arrow-border); } .bs-popover-auto[data-popper-placement^="left"] > .popover-arrow::after, .bs-popover-start > .popover-arrow::after { right: var(--bs-popover-border-width); border-left-color: var(--bs-popover-bg); } .popover-header { padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x); margin-bottom: 0; font-size: var(--bs-popover-header-font-size); color: var(--bs-popover-header-color); background-color: var(--bs-popover-header-bg); border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-border-color); border-top-left-radius: var(--bs-popover-inner-border-radius); border-top-right-radius: var(--bs-popover-inner-border-radius); } .popover-header:empty { display: none; } .popover-body { padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x); color: var(--bs-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%; -webkit-backface-visibility: hidden; backface-visibility: hidden; transition: transform 0.6s ease-in-out; } @media (prefers-reduced-motion: reduce) { .carousel-item { transition: none; } } .carousel-item-next, .carousel-item-prev, .carousel-item.active { display: block; } .active.carousel-item-end, .carousel-item-next:not(.carousel-item-start) { transform: translateX(100%); } .active.carousel-item-start, .carousel-item-prev:not(.carousel-item-end) { transform: translateX(-100%); } .carousel-fade .carousel-item { opacity: 0; transition-property: opacity; transform: none; } .carousel-fade .carousel-item-next.carousel-item-start, .carousel-fade .carousel-item-prev.carousel-item-end, .carousel-fade .carousel-item.active { z-index: 1; opacity: 1; } .carousel-fade .active.carousel-item-end, .carousel-fade .active.carousel-item-start { z-index: 0; opacity: 0; transition: opacity 0s 0.6s; } @media (prefers-reduced-motion: reduce) { .carousel-fade .active.carousel-item-end, .carousel-fade .active.carousel-item-start { transition: none; } } .carousel-control-next, .carousel-control-prev { 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: 0 0; border: 0; opacity: 0.5; transition: opacity 0.15s ease; } @media (prefers-reduced-motion: reduce) { .carousel-control-next, .carousel-control-prev { transition: none; } } .carousel-control-next:focus, .carousel-control-next:hover, .carousel-control-prev:focus, .carousel-control-prev:hover { color: #fff; text-decoration: none; outline: 0; opacity: 0.9; } .carousel-control-prev { left: 0; } .carousel-control-next { right: 0; } .carousel-control-next-icon, .carousel-control-prev-icon { display: inline-block; width: 2rem; height: 2rem; 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' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e"); } .carousel-control-next-icon { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%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: 0.5; transition: opacity 0.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-next-icon, .carousel-dark .carousel-control-prev-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-next-icon, [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 { 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-border, .spinner-grow { display: inline-block; width: var(--bs-spinner-width); height: var(--bs-spinner-height); vertical-align: var(--bs-spinner-vertical-align); border-radius: 50%; animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name); } @keyframes spinner-border { to { transform: rotate(360deg); } } .spinner-border { --bs-spinner-width: 2rem; --bs-spinner-height: 2rem; --bs-spinner-vertical-align: -0.125em; --bs-spinner-border-width: 0.25em; --bs-spinner-animation-speed: 0.75s; --bs-spinner-animation-name: spinner-border; border: var(--bs-spinner-border-width) solid currentcolor; border-right-color: transparent; } .spinner-border-sm { --bs-spinner-width: 1rem; --bs-spinner-height: 1rem; --bs-spinner-border-width: 0.2em; } @keyframes spinner-grow { 0% { transform: scale(0); } 50% { opacity: 1; transform: none; } } .spinner-grow { --bs-spinner-width: 2rem; --bs-spinner-height: 2rem; --bs-spinner-vertical-align: -0.125em; --bs-spinner-animation-speed: 0.75s; --bs-spinner-animation-name: spinner-grow; background-color: currentcolor; opacity: 0; } .spinner-grow-sm { --bs-spinner-width: 1rem; --bs-spinner-height: 1rem; } @media (prefers-reduced-motion: reduce) { .spinner-border, .spinner-grow { --bs-spinner-animation-speed: 1.5s; } } .offcanvas, .offcanvas-lg, .offcanvas-md, .offcanvas-sm, .offcanvas-xl, .offcanvas-xxl { --bs-offcanvas-zindex: 1045; --bs-offcanvas-width: 400px; --bs-offcanvas-height: 30vh; --bs-offcanvas-padding-x: 1rem; --bs-offcanvas-padding-y: 1rem; --bs-offcanvas-color: var(--bs-body-color); --bs-offcanvas-bg: var(--bs-body-bg); --bs-offcanvas-border-width: var(--bs-border-width); --bs-offcanvas-border-color: var(--bs-border-color-translucent); --bs-offcanvas-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); --bs-offcanvas-transition: transform 0.3s ease-in-out; --bs-offcanvas-title-line-height: 1.5; } @media (max-width: 575.98px) { .offcanvas-sm { position: fixed; bottom: 0; z-index: var(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: var(--bs-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(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } .offcanvas-sm.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } .offcanvas-sm.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } .offcanvas-sm.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } .offcanvas-sm.show:not(.hiding), .offcanvas-sm.showing { transform: none; } .offcanvas-sm.hiding, .offcanvas-sm.show, .offcanvas-sm.showing { visibility: visible; } } @media (min-width: 576px) { .offcanvas-sm { --bs-offcanvas-height: auto; --bs-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(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: var(--bs-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(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } .offcanvas-md.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } .offcanvas-md.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } .offcanvas-md.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } .offcanvas-md.show:not(.hiding), .offcanvas-md.showing { transform: none; } .offcanvas-md.hiding, .offcanvas-md.show, .offcanvas-md.showing { visibility: visible; } } @media (min-width: 768px) { .offcanvas-md { --bs-offcanvas-height: auto; --bs-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(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: var(--bs-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(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } .offcanvas-lg.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } .offcanvas-lg.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } .offcanvas-lg.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } .offcanvas-lg.show:not(.hiding), .offcanvas-lg.showing { transform: none; } .offcanvas-lg.hiding, .offcanvas-lg.show, .offcanvas-lg.showing { visibility: visible; } } @media (min-width: 992px) { .offcanvas-lg { --bs-offcanvas-height: auto; --bs-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(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: var(--bs-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(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } .offcanvas-xl.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } .offcanvas-xl.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } .offcanvas-xl.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } .offcanvas-xl.show:not(.hiding), .offcanvas-xl.showing { transform: none; } .offcanvas-xl.hiding, .offcanvas-xl.show, .offcanvas-xl.showing { visibility: visible; } } @media (min-width: 1200px) { .offcanvas-xl { --bs-offcanvas-height: auto; --bs-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(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: var(--bs-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(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } .offcanvas-xxl.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } .offcanvas-xxl.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } .offcanvas-xxl.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } .offcanvas-xxl.show:not(.hiding), .offcanvas-xxl.showing { transform: none; } .offcanvas-xxl.hiding, .offcanvas-xxl.show, .offcanvas-xxl.showing { visibility: visible; } } @media (min-width: 1400px) { .offcanvas-xxl { --bs-offcanvas-height: auto; --bs-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(--bs-offcanvas-zindex); display: flex; flex-direction: column; max-width: 100%; color: var(--bs-offcanvas-color); visibility: hidden; background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; transition: var(--bs-offcanvas-transition); } @media (prefers-reduced-motion: reduce) { .offcanvas { transition: none; } } .offcanvas.offcanvas-start { top: 0; left: 0; width: var(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } .offcanvas.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } .offcanvas.offcanvas-top { top: 0; right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } .offcanvas.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } .offcanvas.show:not(.hiding), .offcanvas.showing { transform: none; } .offcanvas.hiding, .offcanvas.show, .offcanvas.showing { visibility: visible; } .offcanvas-backdrop { position: fixed; top: 0; left: 0; z-index: 1040; width: 100vw; height: 100vh; background-color: #000; } .offcanvas-backdrop.fade { opacity: 0; } .offcanvas-backdrop.show { opacity: 0.5; } .offcanvas-header { display: flex; align-items: center; justify-content: space-between; padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); } .offcanvas-header .btn-close { padding: calc(var(--bs-offcanvas-padding-y) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5); margin-top: calc(-0.5 * var(--bs-offcanvas-padding-y)); margin-right: calc(-0.5 * var(--bs-offcanvas-padding-x)); margin-bottom: calc(-0.5 * var(--bs-offcanvas-padding-y)); } .offcanvas-title { margin-bottom: 0; line-height: var(--bs-offcanvas-title-line-height); } .offcanvas-body { flex-grow: 1; padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); overflow-y: auto; } .placeholder { display: inline-block; min-height: 1em; vertical-align: middle; cursor: wait; background-color: currentcolor; opacity: 0.5; } .placeholder.btn::before { display: inline-block; content: ""; } .placeholder-xs { min-height: 0.6em; } .placeholder-sm { min-height: 0.8em; } .placeholder-lg { min-height: 1.2em; } .placeholder-glow .placeholder { animation: placeholder-glow 2s ease-in-out infinite; } @keyframes placeholder-glow { 50% { opacity: 0.2; } } .placeholder-wave { -webkit-mask-image: linear-gradient( 130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95% ); mask-image: linear-gradient( 130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95% ); -webkit-mask-size: 200% 100%; mask-size: 200% 100%; animation: placeholder-wave 2s linear infinite; } @keyframes placeholder-wave { 100% { -webkit-mask-position: -200% 0%; mask-position: -200% 0%; } } .clearfix::after { display: block; clear: both; content: ""; } .text-bg-primary { color: #fff !important; background-color: RGBA(13, 110, 253, var(--bs-bg-opacity, 1)) !important; } .text-bg-secondary { color: #fff !important; background-color: RGBA(108, 117, 125, var(--bs-bg-opacity, 1)) !important; } .text-bg-success { color: #fff !important; background-color: RGBA(25, 135, 84, var(--bs-bg-opacity, 1)) !important; } .text-bg-info { color: #000 !important; background-color: RGBA(13, 202, 240, var(--bs-bg-opacity, 1)) !important; } .text-bg-warning { color: #000 !important; background-color: RGBA(255, 193, 7, var(--bs-bg-opacity, 1)) !important; } .text-bg-danger { color: #fff !important; background-color: RGBA(220, 53, 69, var(--bs-bg-opacity, 1)) !important; } .text-bg-light { color: #000 !important; background-color: RGBA(248, 249, 250, var(--bs-bg-opacity, 1)) !important; } .text-bg-dark { color: #fff !important; background-color: RGBA(33, 37, 41, var(--bs-bg-opacity, 1)) !important; } .link-primary { color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1) ) !important; } .link-primary:focus, .link-primary:hover { color: RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( 10, 88, 202, var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( 10, 88, 202, var(--bs-link-underline-opacity, 1) ) !important; } .link-secondary { color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1) ) !important; } .link-secondary:focus, .link-secondary:hover { color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( 86, 94, 100, var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( 86, 94, 100, var(--bs-link-underline-opacity, 1) ) !important; } .link-success { color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( var(--bs-success-rgb), var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( var(--bs-success-rgb), var(--bs-link-underline-opacity, 1) ) !important; } .link-success:focus, .link-success:hover { color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( 20, 108, 67, var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( 20, 108, 67, var(--bs-link-underline-opacity, 1) ) !important; } .link-info { color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( var(--bs-info-rgb), var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( var(--bs-info-rgb), var(--bs-link-underline-opacity, 1) ) !important; } .link-info:focus, .link-info:hover { color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( 61, 213, 243, var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( 61, 213, 243, var(--bs-link-underline-opacity, 1) ) !important; } .link-warning { color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1) ) !important; } .link-warning:focus, .link-warning:hover { color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( 255, 205, 57, var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( 255, 205, 57, var(--bs-link-underline-opacity, 1) ) !important; } .link-danger { color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1) ) !important; } .link-danger:focus, .link-danger:hover { color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( 176, 42, 55, var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( 176, 42, 55, var(--bs-link-underline-opacity, 1) ) !important; } .link-light { color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( var(--bs-light-rgb), var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( var(--bs-light-rgb), var(--bs-link-underline-opacity, 1) ) !important; } .link-light:focus, .link-light:hover { color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( 249, 250, 251, var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( 249, 250, 251, var(--bs-link-underline-opacity, 1) ) !important; } .link-dark { color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1) ) !important; } .link-dark:focus, .link-dark:hover { color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important; -webkit-text-decoration-color: RGBA( 26, 30, 33, var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( 26, 30, 33, var(--bs-link-underline-opacity, 1) ) !important; } .link-body-emphasis { color: RGBA( var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1) ) !important; -webkit-text-decoration-color: RGBA( var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: RGBA( var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1) ) !important; } .link-body-emphasis:focus, .link-body-emphasis:hover { color: RGBA( var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75) ) !important; -webkit-text-decoration-color: RGBA( var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75) ) !important; text-decoration-color: RGBA( var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75) ) !important; } .focus-ring:focus { outline: 0; box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); } .icon-link { display: inline-flex; gap: 0.375rem; align-items: center; -webkit-text-decoration-color: rgba( var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5) ); text-decoration-color: rgba( var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5) ); text-underline-offset: 0.25em; -webkit-backface-visibility: hidden; backface-visibility: hidden; } .icon-link > .bi { flex-shrink: 0; width: 1em; height: 1em; fill: currentcolor; transition: 0.2s ease-in-out transform; } @media (prefers-reduced-motion: reduce) { .icon-link > .bi { transition: none; } } .icon-link-hover:focus-visible > .bi, .icon-link-hover:hover > .bi { transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0)); } .ratio { position: relative; width: 100%; } .ratio::before { display: block; padding-top: var(--bs-aspect-ratio); content: ""; } .ratio > * { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .ratio-1x1 { --bs-aspect-ratio: 100%; } .ratio-4x3 { --bs-aspect-ratio: 75%; } .ratio-16x9 { --bs-aspect-ratio: 56.25%; } .ratio-21x9 { --bs-aspect-ratio: 42.8571428571%; } .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: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } .sticky-bottom { position: -webkit-sticky; position: sticky; bottom: 0; z-index: 1020; } @media (min-width: 576px) { .sticky-sm-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } .sticky-sm-bottom { position: -webkit-sticky; position: sticky; bottom: 0; z-index: 1020; } } @media (min-width: 768px) { .sticky-md-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } .sticky-md-bottom { position: -webkit-sticky; position: sticky; bottom: 0; z-index: 1020; } } @media (min-width: 992px) { .sticky-lg-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } .sticky-lg-bottom { position: -webkit-sticky; position: sticky; bottom: 0; z-index: 1020; } } @media (min-width: 1200px) { .sticky-xl-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } .sticky-xl-bottom { position: -webkit-sticky; position: sticky; bottom: 0; z-index: 1020; } } @media (min-width: 1400px) { .sticky-xxl-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; } .sticky-xxl-bottom { position: -webkit-sticky; 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-focusable:not(:focus):not(:focus-within):not(caption), .visually-hidden:not(caption) { position: absolute !important; } .stretched-link::after { position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 1; content: ""; } .text-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .vr { display: inline-block; align-self: stretch; width: 1px; min-height: 1em; background-color: currentcolor; opacity: 0.25; } .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 { -o-object-fit: contain !important; object-fit: contain !important; } .object-fit-cover { -o-object-fit: cover !important; object-fit: cover !important; } .object-fit-fill { -o-object-fit: fill !important; object-fit: fill !important; } .object-fit-scale { -o-object-fit: scale-down !important; object-fit: scale-down !important; } .object-fit-none { -o-object-fit: none !important; object-fit: none !important; } .opacity-0 { opacity: 0 !important; } .opacity-25 { opacity: 0.25 !important; } .opacity-50 { opacity: 0.5 !important; } .opacity-75 { opacity: 0.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: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } .shadow-sm { box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; } .shadow-lg { box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; } .shadow-none { box-shadow: none !important; } .focus-ring-primary { --bs-focus-ring-color: rgba( var(--bs-primary-rgb), var(--bs-focus-ring-opacity) ); } .focus-ring-secondary { --bs-focus-ring-color: rgba( var(--bs-secondary-rgb), var(--bs-focus-ring-opacity) ); } .focus-ring-success { --bs-focus-ring-color: rgba( var(--bs-success-rgb), var(--bs-focus-ring-opacity) ); } .focus-ring-info { --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-warning { --bs-focus-ring-color: rgba( var(--bs-warning-rgb), var(--bs-focus-ring-opacity) ); } .focus-ring-danger { --bs-focus-ring-color: rgba( var(--bs-danger-rgb), var(--bs-focus-ring-opacity) ); } .focus-ring-light { --bs-focus-ring-color: rgba( var(--bs-light-rgb), var(--bs-focus-ring-opacity) ); } .focus-ring-dark { --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-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: -webkit-sticky !important; 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: translateX(-50%) !important; } .translate-middle-y { transform: translateY(-50%) !important; } .border { border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-0 { border: 0 !important; } .border-top { border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-top-0 { border-top: 0 !important; } .border-end { border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-end-0 { border-right: 0 !important; } .border-bottom { border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-bottom-0 { border-bottom: 0 !important; } .border-start { border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-start-0 { border-left: 0 !important; } .border-primary { --bs-border-opacity: 1; border-color: rgba( var(--bs-primary-rgb), var(--bs-border-opacity) ) !important; } .border-secondary { --bs-border-opacity: 1; border-color: rgba( var(--bs-secondary-rgb), var(--bs-border-opacity) ) !important; } .border-success { --bs-border-opacity: 1; border-color: rgba( var(--bs-success-rgb), var(--bs-border-opacity) ) !important; } .border-info { --bs-border-opacity: 1; border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important; } .border-warning { --bs-border-opacity: 1; border-color: rgba( var(--bs-warning-rgb), var(--bs-border-opacity) ) !important; } .border-danger { --bs-border-opacity: 1; border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; } .border-light { --bs-border-opacity: 1; border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; } .border-dark { --bs-border-opacity: 1; border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; } .border-black { --bs-border-opacity: 1; border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important; } .border-white { --bs-border-opacity: 1; border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; } .border-primary-subtle { border-color: var(--bs-primary-border-subtle) !important; } .border-secondary-subtle { border-color: var(--bs-secondary-border-subtle) !important; } .border-success-subtle { border-color: var(--bs-success-border-subtle) !important; } .border-info-subtle { border-color: var(--bs-info-border-subtle) !important; } .border-warning-subtle { border-color: var(--bs-warning-border-subtle) !important; } .border-danger-subtle { border-color: var(--bs-danger-border-subtle) !important; } .border-light-subtle { border-color: var(--bs-light-border-subtle) !important; } .border-dark-subtle { border-color: var(--bs-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 { --bs-border-opacity: 0.1; } .border-opacity-25 { --bs-border-opacity: 0.25; } .border-opacity-50 { --bs-border-opacity: 0.5; } .border-opacity-75 { --bs-border-opacity: 0.75; } .border-opacity-100 { --bs-border-opacity: 1; } .w-25 { width: 25% !important; } .w-50 { width: 50% !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-25 { height: 25% !important; } .h-50 { height: 50% !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: 0.25rem !important; } .m-2 { margin: 0.5rem !important; } .m-3 { margin: 1rem !important; } .m-4 { margin: 1.5rem !important; } .m-5 { margin: 3rem !important; } .m-auto { margin: auto !important; } .mx-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-2 { margin-right: 0.5rem !important; margin-left: 0.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: 3rem !important; margin-left: 3rem !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: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-2 { margin-top: 0.5rem !important; margin-bottom: 0.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: 3rem !important; margin-bottom: 3rem !important; } .my-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-0 { margin-top: 0 !important; } .mt-1 { margin-top: 0.25rem !important; } .mt-2 { margin-top: 0.5rem !important; } .mt-3 { margin-top: 1rem !important; } .mt-4 { margin-top: 1.5rem !important; } .mt-5 { margin-top: 3rem !important; } .mt-auto { margin-top: auto !important; } .me-0 { margin-right: 0 !important; } .me-1 { margin-right: 0.25rem !important; } .me-2 { margin-right: 0.5rem !important; } .me-3 { margin-right: 1rem !important; } .me-4 { margin-right: 1.5rem !important; } .me-5 { margin-right: 3rem !important; } .me-auto { margin-right: auto !important; } .mb-0 { margin-bottom: 0 !important; } .mb-1 { margin-bottom: 0.25rem !important; } .mb-2 { margin-bottom: 0.5rem !important; } .mb-3 { margin-bottom: 1rem !important; } .mb-4 { margin-bottom: 1.5rem !important; } .mb-5 { margin-bottom: 3rem !important; } .mb-auto { margin-bottom: auto !important; } .ms-0 { margin-left: 0 !important; } .ms-1 { margin-left: 0.25rem !important; } .ms-2 { margin-left: 0.5rem !important; } .ms-3 { margin-left: 1rem !important; } .ms-4 { margin-left: 1.5rem !important; } .ms-5 { margin-left: 3rem !important; } .ms-auto { margin-left: auto !important; } .p-0 { padding: 0 !important; } .p-1 { padding: 0.25rem !important; } .p-2 { padding: 0.5rem !important; } .p-3 { padding: 1rem !important; } .p-4 { padding: 1.5rem !important; } .p-5 { padding: 3rem !important; } .px-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-2 { padding-right: 0.5rem !important; padding-left: 0.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: 3rem !important; padding-left: 3rem !important; } .py-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-2 { padding-top: 0.5rem !important; padding-bottom: 0.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: 3rem !important; padding-bottom: 3rem !important; } .pt-0 { padding-top: 0 !important; } .pt-1 { padding-top: 0.25rem !important; } .pt-2 { padding-top: 0.5rem !important; } .pt-3 { padding-top: 1rem !important; } .pt-4 { padding-top: 1.5rem !important; } .pt-5 { padding-top: 3rem !important; } .pe-0 { padding-right: 0 !important; } .pe-1 { padding-right: 0.25rem !important; } .pe-2 { padding-right: 0.5rem !important; } .pe-3 { padding-right: 1rem !important; } .pe-4 { padding-right: 1.5rem !important; } .pe-5 { padding-right: 3rem !important; } .pb-0 { padding-bottom: 0 !important; } .pb-1 { padding-bottom: 0.25rem !important; } .pb-2 { padding-bottom: 0.5rem !important; } .pb-3 { padding-bottom: 1rem !important; } .pb-4 { padding-bottom: 1.5rem !important; } .pb-5 { padding-bottom: 3rem !important; } .ps-0 { padding-left: 0 !important; } .ps-1 { padding-left: 0.25rem !important; } .ps-2 { padding-left: 0.5rem !important; } .ps-3 { padding-left: 1rem !important; } .ps-4 { padding-left: 1.5rem !important; } .ps-5 { padding-left: 3rem !important; } .gap-0 { gap: 0 !important; } .gap-1 { gap: 0.25rem !important; } .gap-2 { gap: 0.5rem !important; } .gap-3 { gap: 1rem !important; } .gap-4 { gap: 1.5rem !important; } .gap-5 { gap: 3rem !important; } .row-gap-0 { row-gap: 0 !important; } .row-gap-1 { row-gap: 0.25rem !important; } .row-gap-2 { row-gap: 0.5rem !important; } .row-gap-3 { row-gap: 1rem !important; } .row-gap-4 { row-gap: 1.5rem !important; } .row-gap-5 { row-gap: 3rem !important; } .column-gap-0 { -moz-column-gap: 0 !important; column-gap: 0 !important; } .column-gap-1 { -moz-column-gap: 0.25rem !important; column-gap: 0.25rem !important; } .column-gap-2 { -moz-column-gap: 0.5rem !important; column-gap: 0.5rem !important; } .column-gap-3 { -moz-column-gap: 1rem !important; column-gap: 1rem !important; } .column-gap-4 { -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important; } .column-gap-5 { -moz-column-gap: 3rem !important; column-gap: 3rem !important; } .font-monospace { font-family: var(--bs-font-monospace) !important; } .fs-1 { font-size: calc(1.375rem + 1.5vw) !important; } .fs-2 { font-size: calc(1.325rem + 0.9vw) !important; } .fs-3 { font-size: calc(1.3rem + 0.6vw) !important; } .fs-4 { font-size: calc(1.275rem + 0.3vw) !important; } .fs-5 { font-size: 1.25rem !important; } .fs-6 { font-size: 1rem !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 { font-weight: 600 !important; } .fw-bold { font-weight: 700 !important; } .fw-bolder { font-weight: bolder !important; } .lh-1 { line-height: 1 !important; } .lh-sm { line-height: 1.25 !important; } .lh-base { line-height: 1.5 !important; } .lh-lg { line-height: 2 !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 { --bs-text-opacity: 1; color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; } .text-secondary { --bs-text-opacity: 1; color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; } .text-success { --bs-text-opacity: 1; color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; } .text-info { --bs-text-opacity: 1; color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; } .text-warning { --bs-text-opacity: 1; color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; } .text-danger { --bs-text-opacity: 1; color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; } .text-light { --bs-text-opacity: 1; color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; } .text-dark { --bs-text-opacity: 1; color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; } .text-black { --bs-text-opacity: 1; color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; } .text-white { --bs-text-opacity: 1; color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; } .text-body { --bs-text-opacity: 1; color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; } .text-muted { --bs-text-opacity: 1; color: var(--bs-secondary-color) !important; } .text-black-50 { --bs-text-opacity: 1; color: rgba(0, 0, 0, 0.5) !important; } .text-white-50 { --bs-text-opacity: 1; color: rgba(255, 255, 255, 0.5) !important; } .text-body-secondary { --bs-text-opacity: 1; color: var(--bs-secondary-color) !important; } .text-body-tertiary { --bs-text-opacity: 1; color: var(--bs-tertiary-color) !important; } .text-body-emphasis { --bs-text-opacity: 1; color: var(--bs-emphasis-color) !important; } .text-reset { --bs-text-opacity: 1; color: inherit !important; } .text-opacity-25 { --bs-text-opacity: 0.25; } .text-opacity-50 { --bs-text-opacity: 0.5; } .text-opacity-75 { --bs-text-opacity: 0.75; } .text-opacity-100 { --bs-text-opacity: 1; } .text-primary-emphasis { color: var(--bs-primary-text-emphasis) !important; } .text-secondary-emphasis { color: var(--bs-secondary-text-emphasis) !important; } .text-success-emphasis { color: var(--bs-success-text-emphasis) !important; } .text-info-emphasis { color: var(--bs-info-text-emphasis) !important; } .text-warning-emphasis { color: var(--bs-warning-text-emphasis) !important; } .text-danger-emphasis { color: var(--bs-danger-text-emphasis) !important; } .text-light-emphasis { color: var(--bs-light-text-emphasis) !important; } .text-dark-emphasis { color: var(--bs-dark-text-emphasis) !important; } .link-opacity-10 { --bs-link-opacity: 0.1; } .link-opacity-10-hover:hover { --bs-link-opacity: 0.1; } .link-opacity-25 { --bs-link-opacity: 0.25; } .link-opacity-25-hover:hover { --bs-link-opacity: 0.25; } .link-opacity-50 { --bs-link-opacity: 0.5; } .link-opacity-50-hover:hover { --bs-link-opacity: 0.5; } .link-opacity-75 { --bs-link-opacity: 0.75; } .link-opacity-75-hover:hover { --bs-link-opacity: 0.75; } .link-opacity-100 { --bs-link-opacity: 1; } .link-opacity-100-hover:hover { --bs-link-opacity: 1; } .link-offset-1 { text-underline-offset: 0.125em !important; } .link-offset-1-hover:hover { text-underline-offset: 0.125em !important; } .link-offset-2 { text-underline-offset: 0.25em !important; } .link-offset-2-hover:hover { text-underline-offset: 0.25em !important; } .link-offset-3 { text-underline-offset: 0.375em !important; } .link-offset-3-hover:hover { text-underline-offset: 0.375em !important; } .link-underline-primary { --bs-link-underline-opacity: 1; -webkit-text-decoration-color: rgba( var(--bs-primary-rgb), var(--bs-link-underline-opacity) ) !important; text-decoration-color: rgba( var(--bs-primary-rgb), var(--bs-link-underline-opacity) ) !important; } .link-underline-secondary { --bs-link-underline-opacity: 1; -webkit-text-decoration-color: rgba( var(--bs-secondary-rgb), var(--bs-link-underline-opacity) ) !important; text-decoration-color: rgba( var(--bs-secondary-rgb), var(--bs-link-underline-opacity) ) !important; } .link-underline-success { --bs-link-underline-opacity: 1; -webkit-text-decoration-color: rgba( var(--bs-success-rgb), var(--bs-link-underline-opacity) ) !important; text-decoration-color: rgba( var(--bs-success-rgb), var(--bs-link-underline-opacity) ) !important; } .link-underline-info { --bs-link-underline-opacity: 1; -webkit-text-decoration-color: rgba( var(--bs-info-rgb), var(--bs-link-underline-opacity) ) !important; text-decoration-color: rgba( var(--bs-info-rgb), var(--bs-link-underline-opacity) ) !important; } .link-underline-warning { --bs-link-underline-opacity: 1; -webkit-text-decoration-color: rgba( var(--bs-warning-rgb), var(--bs-link-underline-opacity) ) !important; text-decoration-color: rgba( var(--bs-warning-rgb), var(--bs-link-underline-opacity) ) !important; } .link-underline-danger { --bs-link-underline-opacity: 1; -webkit-text-decoration-color: rgba( var(--bs-danger-rgb), var(--bs-link-underline-opacity) ) !important; text-decoration-color: rgba( var(--bs-danger-rgb), var(--bs-link-underline-opacity) ) !important; } .link-underline-light { --bs-link-underline-opacity: 1; -webkit-text-decoration-color: rgba( var(--bs-light-rgb), var(--bs-link-underline-opacity) ) !important; text-decoration-color: rgba( var(--bs-light-rgb), var(--bs-link-underline-opacity) ) !important; } .link-underline-dark { --bs-link-underline-opacity: 1; -webkit-text-decoration-color: rgba( var(--bs-dark-rgb), var(--bs-link-underline-opacity) ) !important; text-decoration-color: rgba( var(--bs-dark-rgb), var(--bs-link-underline-opacity) ) !important; } .link-underline { --bs-link-underline-opacity: 1; -webkit-text-decoration-color: rgba( var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1) ) !important; text-decoration-color: rgba( var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1) ) !important; } .link-underline-opacity-0 { --bs-link-underline-opacity: 0; } .link-underline-opacity-0-hover:hover { --bs-link-underline-opacity: 0; } .link-underline-opacity-10 { --bs-link-underline-opacity: 0.1; } .link-underline-opacity-10-hover:hover { --bs-link-underline-opacity: 0.1; } .link-underline-opacity-25 { --bs-link-underline-opacity: 0.25; } .link-underline-opacity-25-hover:hover { --bs-link-underline-opacity: 0.25; } .link-underline-opacity-50 { --bs-link-underline-opacity: 0.5; } .link-underline-opacity-50-hover:hover { --bs-link-underline-opacity: 0.5; } .link-underline-opacity-75 { --bs-link-underline-opacity: 0.75; } .link-underline-opacity-75-hover:hover { --bs-link-underline-opacity: 0.75; } .link-underline-opacity-100 { --bs-link-underline-opacity: 1; } .link-underline-opacity-100-hover:hover { --bs-link-underline-opacity: 1; } .bg-primary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-primary-rgb), var(--bs-bg-opacity) ) !important; } .bg-secondary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-secondary-rgb), var(--bs-bg-opacity) ) !important; } .bg-success { --bs-bg-opacity: 1; background-color: rgba( var(--bs-success-rgb), var(--bs-bg-opacity) ) !important; } .bg-info { --bs-bg-opacity: 1; background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; } .bg-warning { --bs-bg-opacity: 1; background-color: rgba( var(--bs-warning-rgb), var(--bs-bg-opacity) ) !important; } .bg-danger { --bs-bg-opacity: 1; background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; } .bg-light { --bs-bg-opacity: 1; background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; } .bg-dark { --bs-bg-opacity: 1; background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } .bg-black { --bs-bg-opacity: 1; background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; } .bg-white { --bs-bg-opacity: 1; background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; } .bg-body { --bs-bg-opacity: 1; background-color: rgba( var(--bs-body-bg-rgb), var(--bs-bg-opacity) ) !important; } .bg-transparent { --bs-bg-opacity: 1; background-color: transparent !important; } .bg-body-secondary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-secondary-bg-rgb), var(--bs-bg-opacity) ) !important; } .bg-body-tertiary { --bs-bg-opacity: 1; background-color: rgba( var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity) ) !important; } .bg-opacity-10 { --bs-bg-opacity: 0.1; } .bg-opacity-25 { --bs-bg-opacity: 0.25; } .bg-opacity-50 { --bs-bg-opacity: 0.5; } .bg-opacity-75 { --bs-bg-opacity: 0.75; } .bg-opacity-100 { --bs-bg-opacity: 1; } .bg-primary-subtle { background-color: var(--bs-primary-bg-subtle) !important; } .bg-secondary-subtle { background-color: var(--bs-secondary-bg-subtle) !important; } .bg-success-subtle { background-color: var(--bs-success-bg-subtle) !important; } .bg-info-subtle { background-color: var(--bs-info-bg-subtle) !important; } .bg-warning-subtle { background-color: var(--bs-warning-bg-subtle) !important; } .bg-danger-subtle { background-color: var(--bs-danger-bg-subtle) !important; } .bg-light-subtle { background-color: var(--bs-light-bg-subtle) !important; } .bg-dark-subtle { background-color: var(--bs-dark-bg-subtle) !important; } .bg-gradient { background-image: var(--bs-gradient) !important; } .user-select-all { -webkit-user-select: all !important; -moz-user-select: all !important; user-select: all !important; } .user-select-auto { -webkit-user-select: auto !important; -moz-user-select: auto !important; user-select: auto !important; } .user-select-none { -webkit-user-select: none !important; -moz-user-select: none !important; user-select: none !important; } .pe-none { pointer-events: none !important; } .pe-auto { pointer-events: auto !important; } .rounded { border-radius: var(--bs-border-radius) !important; } .rounded-0 { border-radius: 0 !important; } .rounded-1 { border-radius: var(--bs-border-radius-sm) !important; } .rounded-2 { border-radius: var(--bs-border-radius) !important; } .rounded-3 { border-radius: var(--bs-border-radius-lg) !important; } .rounded-4 { border-radius: var(--bs-border-radius-xl) !important; } .rounded-5 { border-radius: var(--bs-border-radius-xxl) !important; } .rounded-circle { border-radius: 50% !important; } .rounded-pill { border-radius: var(--bs-border-radius-pill) !important; } .rounded-top { border-top-left-radius: var(--bs-border-radius) !important; border-top-right-radius: var(--bs-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(--bs-border-radius-sm) !important; border-top-right-radius: var(--bs-border-radius-sm) !important; } .rounded-top-2 { border-top-left-radius: var(--bs-border-radius) !important; border-top-right-radius: var(--bs-border-radius) !important; } .rounded-top-3 { border-top-left-radius: var(--bs-border-radius-lg) !important; border-top-right-radius: var(--bs-border-radius-lg) !important; } .rounded-top-4 { border-top-left-radius: var(--bs-border-radius-xl) !important; border-top-right-radius: var(--bs-border-radius-xl) !important; } .rounded-top-5 { border-top-left-radius: var(--bs-border-radius-xxl) !important; border-top-right-radius: var(--bs-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(--bs-border-radius-pill) !important; border-top-right-radius: var(--bs-border-radius-pill) !important; } .rounded-end { border-top-right-radius: var(--bs-border-radius) !important; border-bottom-right-radius: var(--bs-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(--bs-border-radius-sm) !important; border-bottom-right-radius: var(--bs-border-radius-sm) !important; } .rounded-end-2 { border-top-right-radius: var(--bs-border-radius) !important; border-bottom-right-radius: var(--bs-border-radius) !important; } .rounded-end-3 { border-top-right-radius: var(--bs-border-radius-lg) !important; border-bottom-right-radius: var(--bs-border-radius-lg) !important; } .rounded-end-4 { border-top-right-radius: var(--bs-border-radius-xl) !important; border-bottom-right-radius: var(--bs-border-radius-xl) !important; } .rounded-end-5 { border-top-right-radius: var(--bs-border-radius-xxl) !important; border-bottom-right-radius: var(--bs-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(--bs-border-radius-pill) !important; border-bottom-right-radius: var(--bs-border-radius-pill) !important; } .rounded-bottom { border-bottom-right-radius: var(--bs-border-radius) !important; border-bottom-left-radius: var(--bs-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(--bs-border-radius-sm) !important; border-bottom-left-radius: var(--bs-border-radius-sm) !important; } .rounded-bottom-2 { border-bottom-right-radius: var(--bs-border-radius) !important; border-bottom-left-radius: var(--bs-border-radius) !important; } .rounded-bottom-3 { border-bottom-right-radius: var(--bs-border-radius-lg) !important; border-bottom-left-radius: var(--bs-border-radius-lg) !important; } .rounded-bottom-4 { border-bottom-right-radius: var(--bs-border-radius-xl) !important; border-bottom-left-radius: var(--bs-border-radius-xl) !important; } .rounded-bottom-5 { border-bottom-right-radius: var(--bs-border-radius-xxl) !important; border-bottom-left-radius: var(--bs-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(--bs-border-radius-pill) !important; border-bottom-left-radius: var(--bs-border-radius-pill) !important; } .rounded-start { border-bottom-left-radius: var(--bs-border-radius) !important; border-top-left-radius: var(--bs-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(--bs-border-radius-sm) !important; border-top-left-radius: var(--bs-border-radius-sm) !important; } .rounded-start-2 { border-bottom-left-radius: var(--bs-border-radius) !important; border-top-left-radius: var(--bs-border-radius) !important; } .rounded-start-3 { border-bottom-left-radius: var(--bs-border-radius-lg) !important; border-top-left-radius: var(--bs-border-radius-lg) !important; } .rounded-start-4 { border-bottom-left-radius: var(--bs-border-radius-xl) !important; border-top-left-radius: var(--bs-border-radius-xl) !important; } .rounded-start-5 { border-bottom-left-radius: var(--bs-border-radius-xxl) !important; border-top-left-radius: var(--bs-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(--bs-border-radius-pill) !important; border-top-left-radius: var(--bs-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; } @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 { -o-object-fit: contain !important; object-fit: contain !important; } .object-fit-sm-cover { -o-object-fit: cover !important; object-fit: cover !important; } .object-fit-sm-fill { -o-object-fit: fill !important; object-fit: fill !important; } .object-fit-sm-scale { -o-object-fit: scale-down !important; object-fit: scale-down !important; } .object-fit-sm-none { -o-object-fit: none !important; 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: 0.25rem !important; } .m-sm-2 { margin: 0.5rem !important; } .m-sm-3 { margin: 1rem !important; } .m-sm-4 { margin: 1.5rem !important; } .m-sm-5 { margin: 3rem !important; } .m-sm-auto { margin: auto !important; } .mx-sm-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-sm-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-sm-2 { margin-right: 0.5rem !important; margin-left: 0.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: 3rem !important; margin-left: 3rem !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: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-sm-2 { margin-top: 0.5rem !important; margin-bottom: 0.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: 3rem !important; margin-bottom: 3rem !important; } .my-sm-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-sm-0 { margin-top: 0 !important; } .mt-sm-1 { margin-top: 0.25rem !important; } .mt-sm-2 { margin-top: 0.5rem !important; } .mt-sm-3 { margin-top: 1rem !important; } .mt-sm-4 { margin-top: 1.5rem !important; } .mt-sm-5 { margin-top: 3rem !important; } .mt-sm-auto { margin-top: auto !important; } .me-sm-0 { margin-right: 0 !important; } .me-sm-1 { margin-right: 0.25rem !important; } .me-sm-2 { margin-right: 0.5rem !important; } .me-sm-3 { margin-right: 1rem !important; } .me-sm-4 { margin-right: 1.5rem !important; } .me-sm-5 { margin-right: 3rem !important; } .me-sm-auto { margin-right: auto !important; } .mb-sm-0 { margin-bottom: 0 !important; } .mb-sm-1 { margin-bottom: 0.25rem !important; } .mb-sm-2 { margin-bottom: 0.5rem !important; } .mb-sm-3 { margin-bottom: 1rem !important; } .mb-sm-4 { margin-bottom: 1.5rem !important; } .mb-sm-5 { margin-bottom: 3rem !important; } .mb-sm-auto { margin-bottom: auto !important; } .ms-sm-0 { margin-left: 0 !important; } .ms-sm-1 { margin-left: 0.25rem !important; } .ms-sm-2 { margin-left: 0.5rem !important; } .ms-sm-3 { margin-left: 1rem !important; } .ms-sm-4 { margin-left: 1.5rem !important; } .ms-sm-5 { margin-left: 3rem !important; } .ms-sm-auto { margin-left: auto !important; } .p-sm-0 { padding: 0 !important; } .p-sm-1 { padding: 0.25rem !important; } .p-sm-2 { padding: 0.5rem !important; } .p-sm-3 { padding: 1rem !important; } .p-sm-4 { padding: 1.5rem !important; } .p-sm-5 { padding: 3rem !important; } .px-sm-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-sm-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-sm-2 { padding-right: 0.5rem !important; padding-left: 0.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: 3rem !important; padding-left: 3rem !important; } .py-sm-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-sm-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-sm-2 { padding-top: 0.5rem !important; padding-bottom: 0.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: 3rem !important; padding-bottom: 3rem !important; } .pt-sm-0 { padding-top: 0 !important; } .pt-sm-1 { padding-top: 0.25rem !important; } .pt-sm-2 { padding-top: 0.5rem !important; } .pt-sm-3 { padding-top: 1rem !important; } .pt-sm-4 { padding-top: 1.5rem !important; } .pt-sm-5 { padding-top: 3rem !important; } .pe-sm-0 { padding-right: 0 !important; } .pe-sm-1 { padding-right: 0.25rem !important; } .pe-sm-2 { padding-right: 0.5rem !important; } .pe-sm-3 { padding-right: 1rem !important; } .pe-sm-4 { padding-right: 1.5rem !important; } .pe-sm-5 { padding-right: 3rem !important; } .pb-sm-0 { padding-bottom: 0 !important; } .pb-sm-1 { padding-bottom: 0.25rem !important; } .pb-sm-2 { padding-bottom: 0.5rem !important; } .pb-sm-3 { padding-bottom: 1rem !important; } .pb-sm-4 { padding-bottom: 1.5rem !important; } .pb-sm-5 { padding-bottom: 3rem !important; } .ps-sm-0 { padding-left: 0 !important; } .ps-sm-1 { padding-left: 0.25rem !important; } .ps-sm-2 { padding-left: 0.5rem !important; } .ps-sm-3 { padding-left: 1rem !important; } .ps-sm-4 { padding-left: 1.5rem !important; } .ps-sm-5 { padding-left: 3rem !important; } .gap-sm-0 { gap: 0 !important; } .gap-sm-1 { gap: 0.25rem !important; } .gap-sm-2 { gap: 0.5rem !important; } .gap-sm-3 { gap: 1rem !important; } .gap-sm-4 { gap: 1.5rem !important; } .gap-sm-5 { gap: 3rem !important; } .row-gap-sm-0 { row-gap: 0 !important; } .row-gap-sm-1 { row-gap: 0.25rem !important; } .row-gap-sm-2 { row-gap: 0.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: 3rem !important; } .column-gap-sm-0 { -moz-column-gap: 0 !important; column-gap: 0 !important; } .column-gap-sm-1 { -moz-column-gap: 0.25rem !important; column-gap: 0.25rem !important; } .column-gap-sm-2 { -moz-column-gap: 0.5rem !important; column-gap: 0.5rem !important; } .column-gap-sm-3 { -moz-column-gap: 1rem !important; column-gap: 1rem !important; } .column-gap-sm-4 { -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important; } .column-gap-sm-5 { -moz-column-gap: 3rem !important; column-gap: 3rem !important; } .text-sm-start { text-align: left !important; } .text-sm-end { text-align: right !important; } .text-sm-center { text-align: center !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 { -o-object-fit: contain !important; object-fit: contain !important; } .object-fit-md-cover { -o-object-fit: cover !important; object-fit: cover !important; } .object-fit-md-fill { -o-object-fit: fill !important; object-fit: fill !important; } .object-fit-md-scale { -o-object-fit: scale-down !important; object-fit: scale-down !important; } .object-fit-md-none { -o-object-fit: none !important; 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: 0.25rem !important; } .m-md-2 { margin: 0.5rem !important; } .m-md-3 { margin: 1rem !important; } .m-md-4 { margin: 1.5rem !important; } .m-md-5 { margin: 3rem !important; } .m-md-auto { margin: auto !important; } .mx-md-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-md-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-md-2 { margin-right: 0.5rem !important; margin-left: 0.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: 3rem !important; margin-left: 3rem !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: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-md-2 { margin-top: 0.5rem !important; margin-bottom: 0.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: 3rem !important; margin-bottom: 3rem !important; } .my-md-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-md-0 { margin-top: 0 !important; } .mt-md-1 { margin-top: 0.25rem !important; } .mt-md-2 { margin-top: 0.5rem !important; } .mt-md-3 { margin-top: 1rem !important; } .mt-md-4 { margin-top: 1.5rem !important; } .mt-md-5 { margin-top: 3rem !important; } .mt-md-auto { margin-top: auto !important; } .me-md-0 { margin-right: 0 !important; } .me-md-1 { margin-right: 0.25rem !important; } .me-md-2 { margin-right: 0.5rem !important; } .me-md-3 { margin-right: 1rem !important; } .me-md-4 { margin-right: 1.5rem !important; } .me-md-5 { margin-right: 3rem !important; } .me-md-auto { margin-right: auto !important; } .mb-md-0 { margin-bottom: 0 !important; } .mb-md-1 { margin-bottom: 0.25rem !important; } .mb-md-2 { margin-bottom: 0.5rem !important; } .mb-md-3 { margin-bottom: 1rem !important; } .mb-md-4 { margin-bottom: 1.5rem !important; } .mb-md-5 { margin-bottom: 3rem !important; } .mb-md-auto { margin-bottom: auto !important; } .ms-md-0 { margin-left: 0 !important; } .ms-md-1 { margin-left: 0.25rem !important; } .ms-md-2 { margin-left: 0.5rem !important; } .ms-md-3 { margin-left: 1rem !important; } .ms-md-4 { margin-left: 1.5rem !important; } .ms-md-5 { margin-left: 3rem !important; } .ms-md-auto { margin-left: auto !important; } .p-md-0 { padding: 0 !important; } .p-md-1 { padding: 0.25rem !important; } .p-md-2 { padding: 0.5rem !important; } .p-md-3 { padding: 1rem !important; } .p-md-4 { padding: 1.5rem !important; } .p-md-5 { padding: 3rem !important; } .px-md-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-md-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-md-2 { padding-right: 0.5rem !important; padding-left: 0.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: 3rem !important; padding-left: 3rem !important; } .py-md-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-md-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-md-2 { padding-top: 0.5rem !important; padding-bottom: 0.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: 3rem !important; padding-bottom: 3rem !important; } .pt-md-0 { padding-top: 0 !important; } .pt-md-1 { padding-top: 0.25rem !important; } .pt-md-2 { padding-top: 0.5rem !important; } .pt-md-3 { padding-top: 1rem !important; } .pt-md-4 { padding-top: 1.5rem !important; } .pt-md-5 { padding-top: 3rem !important; } .pe-md-0 { padding-right: 0 !important; } .pe-md-1 { padding-right: 0.25rem !important; } .pe-md-2 { padding-right: 0.5rem !important; } .pe-md-3 { padding-right: 1rem !important; } .pe-md-4 { padding-right: 1.5rem !important; } .pe-md-5 { padding-right: 3rem !important; } .pb-md-0 { padding-bottom: 0 !important; } .pb-md-1 { padding-bottom: 0.25rem !important; } .pb-md-2 { padding-bottom: 0.5rem !important; } .pb-md-3 { padding-bottom: 1rem !important; } .pb-md-4 { padding-bottom: 1.5rem !important; } .pb-md-5 { padding-bottom: 3rem !important; } .ps-md-0 { padding-left: 0 !important; } .ps-md-1 { padding-left: 0.25rem !important; } .ps-md-2 { padding-left: 0.5rem !important; } .ps-md-3 { padding-left: 1rem !important; } .ps-md-4 { padding-left: 1.5rem !important; } .ps-md-5 { padding-left: 3rem !important; } .gap-md-0 { gap: 0 !important; } .gap-md-1 { gap: 0.25rem !important; } .gap-md-2 { gap: 0.5rem !important; } .gap-md-3 { gap: 1rem !important; } .gap-md-4 { gap: 1.5rem !important; } .gap-md-5 { gap: 3rem !important; } .row-gap-md-0 { row-gap: 0 !important; } .row-gap-md-1 { row-gap: 0.25rem !important; } .row-gap-md-2 { row-gap: 0.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: 3rem !important; } .column-gap-md-0 { -moz-column-gap: 0 !important; column-gap: 0 !important; } .column-gap-md-1 { -moz-column-gap: 0.25rem !important; column-gap: 0.25rem !important; } .column-gap-md-2 { -moz-column-gap: 0.5rem !important; column-gap: 0.5rem !important; } .column-gap-md-3 { -moz-column-gap: 1rem !important; column-gap: 1rem !important; } .column-gap-md-4 { -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important; } .column-gap-md-5 { -moz-column-gap: 3rem !important; column-gap: 3rem !important; } .text-md-start { text-align: left !important; } .text-md-end { text-align: right !important; } .text-md-center { text-align: center !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 { -o-object-fit: contain !important; object-fit: contain !important; } .object-fit-lg-cover { -o-object-fit: cover !important; object-fit: cover !important; } .object-fit-lg-fill { -o-object-fit: fill !important; object-fit: fill !important; } .object-fit-lg-scale { -o-object-fit: scale-down !important; object-fit: scale-down !important; } .object-fit-lg-none { -o-object-fit: none !important; 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: 0.25rem !important; } .m-lg-2 { margin: 0.5rem !important; } .m-lg-3 { margin: 1rem !important; } .m-lg-4 { margin: 1.5rem !important; } .m-lg-5 { margin: 3rem !important; } .m-lg-auto { margin: auto !important; } .mx-lg-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-lg-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-lg-2 { margin-right: 0.5rem !important; margin-left: 0.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: 3rem !important; margin-left: 3rem !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: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-lg-2 { margin-top: 0.5rem !important; margin-bottom: 0.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: 3rem !important; margin-bottom: 3rem !important; } .my-lg-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-lg-0 { margin-top: 0 !important; } .mt-lg-1 { margin-top: 0.25rem !important; } .mt-lg-2 { margin-top: 0.5rem !important; } .mt-lg-3 { margin-top: 1rem !important; } .mt-lg-4 { margin-top: 1.5rem !important; } .mt-lg-5 { margin-top: 3rem !important; } .mt-lg-auto { margin-top: auto !important; } .me-lg-0 { margin-right: 0 !important; } .me-lg-1 { margin-right: 0.25rem !important; } .me-lg-2 { margin-right: 0.5rem !important; } .me-lg-3 { margin-right: 1rem !important; } .me-lg-4 { margin-right: 1.5rem !important; } .me-lg-5 { margin-right: 3rem !important; } .me-lg-auto { margin-right: auto !important; } .mb-lg-0 { margin-bottom: 0 !important; } .mb-lg-1 { margin-bottom: 0.25rem !important; } .mb-lg-2 { margin-bottom: 0.5rem !important; } .mb-lg-3 { margin-bottom: 1rem !important; } .mb-lg-4 { margin-bottom: 1.5rem !important; } .mb-lg-5 { margin-bottom: 3rem !important; } .mb-lg-auto { margin-bottom: auto !important; } .ms-lg-0 { margin-left: 0 !important; } .ms-lg-1 { margin-left: 0.25rem !important; } .ms-lg-2 { margin-left: 0.5rem !important; } .ms-lg-3 { margin-left: 1rem !important; } .ms-lg-4 { margin-left: 1.5rem !important; } .ms-lg-5 { margin-left: 3rem !important; } .ms-lg-auto { margin-left: auto !important; } .p-lg-0 { padding: 0 !important; } .p-lg-1 { padding: 0.25rem !important; } .p-lg-2 { padding: 0.5rem !important; } .p-lg-3 { padding: 1rem !important; } .p-lg-4 { padding: 1.5rem !important; } .p-lg-5 { padding: 3rem !important; } .px-lg-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-lg-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-lg-2 { padding-right: 0.5rem !important; padding-left: 0.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: 3rem !important; padding-left: 3rem !important; } .py-lg-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-lg-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-lg-2 { padding-top: 0.5rem !important; padding-bottom: 0.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: 3rem !important; padding-bottom: 3rem !important; } .pt-lg-0 { padding-top: 0 !important; } .pt-lg-1 { padding-top: 0.25rem !important; } .pt-lg-2 { padding-top: 0.5rem !important; } .pt-lg-3 { padding-top: 1rem !important; } .pt-lg-4 { padding-top: 1.5rem !important; } .pt-lg-5 { padding-top: 3rem !important; } .pe-lg-0 { padding-right: 0 !important; } .pe-lg-1 { padding-right: 0.25rem !important; } .pe-lg-2 { padding-right: 0.5rem !important; } .pe-lg-3 { padding-right: 1rem !important; } .pe-lg-4 { padding-right: 1.5rem !important; } .pe-lg-5 { padding-right: 3rem !important; } .pb-lg-0 { padding-bottom: 0 !important; } .pb-lg-1 { padding-bottom: 0.25rem !important; } .pb-lg-2 { padding-bottom: 0.5rem !important; } .pb-lg-3 { padding-bottom: 1rem !important; } .pb-lg-4 { padding-bottom: 1.5rem !important; } .pb-lg-5 { padding-bottom: 3rem !important; } .ps-lg-0 { padding-left: 0 !important; } .ps-lg-1 { padding-left: 0.25rem !important; } .ps-lg-2 { padding-left: 0.5rem !important; } .ps-lg-3 { padding-left: 1rem !important; } .ps-lg-4 { padding-left: 1.5rem !important; } .ps-lg-5 { padding-left: 3rem !important; } .gap-lg-0 { gap: 0 !important; } .gap-lg-1 { gap: 0.25rem !important; } .gap-lg-2 { gap: 0.5rem !important; } .gap-lg-3 { gap: 1rem !important; } .gap-lg-4 { gap: 1.5rem !important; } .gap-lg-5 { gap: 3rem !important; } .row-gap-lg-0 { row-gap: 0 !important; } .row-gap-lg-1 { row-gap: 0.25rem !important; } .row-gap-lg-2 { row-gap: 0.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: 3rem !important; } .column-gap-lg-0 { -moz-column-gap: 0 !important; column-gap: 0 !important; } .column-gap-lg-1 { -moz-column-gap: 0.25rem !important; column-gap: 0.25rem !important; } .column-gap-lg-2 { -moz-column-gap: 0.5rem !important; column-gap: 0.5rem !important; } .column-gap-lg-3 { -moz-column-gap: 1rem !important; column-gap: 1rem !important; } .column-gap-lg-4 { -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important; } .column-gap-lg-5 { -moz-column-gap: 3rem !important; column-gap: 3rem !important; } .text-lg-start { text-align: left !important; } .text-lg-end { text-align: right !important; } .text-lg-center { text-align: center !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 { -o-object-fit: contain !important; object-fit: contain !important; } .object-fit-xl-cover { -o-object-fit: cover !important; object-fit: cover !important; } .object-fit-xl-fill { -o-object-fit: fill !important; object-fit: fill !important; } .object-fit-xl-scale { -o-object-fit: scale-down !important; object-fit: scale-down !important; } .object-fit-xl-none { -o-object-fit: none !important; 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: 0.25rem !important; } .m-xl-2 { margin: 0.5rem !important; } .m-xl-3 { margin: 1rem !important; } .m-xl-4 { margin: 1.5rem !important; } .m-xl-5 { margin: 3rem !important; } .m-xl-auto { margin: auto !important; } .mx-xl-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-xl-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-xl-2 { margin-right: 0.5rem !important; margin-left: 0.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: 3rem !important; margin-left: 3rem !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: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xl-2 { margin-top: 0.5rem !important; margin-bottom: 0.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: 3rem !important; margin-bottom: 3rem !important; } .my-xl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xl-0 { margin-top: 0 !important; } .mt-xl-1 { margin-top: 0.25rem !important; } .mt-xl-2 { margin-top: 0.5rem !important; } .mt-xl-3 { margin-top: 1rem !important; } .mt-xl-4 { margin-top: 1.5rem !important; } .mt-xl-5 { margin-top: 3rem !important; } .mt-xl-auto { margin-top: auto !important; } .me-xl-0 { margin-right: 0 !important; } .me-xl-1 { margin-right: 0.25rem !important; } .me-xl-2 { margin-right: 0.5rem !important; } .me-xl-3 { margin-right: 1rem !important; } .me-xl-4 { margin-right: 1.5rem !important; } .me-xl-5 { margin-right: 3rem !important; } .me-xl-auto { margin-right: auto !important; } .mb-xl-0 { margin-bottom: 0 !important; } .mb-xl-1 { margin-bottom: 0.25rem !important; } .mb-xl-2 { margin-bottom: 0.5rem !important; } .mb-xl-3 { margin-bottom: 1rem !important; } .mb-xl-4 { margin-bottom: 1.5rem !important; } .mb-xl-5 { margin-bottom: 3rem !important; } .mb-xl-auto { margin-bottom: auto !important; } .ms-xl-0 { margin-left: 0 !important; } .ms-xl-1 { margin-left: 0.25rem !important; } .ms-xl-2 { margin-left: 0.5rem !important; } .ms-xl-3 { margin-left: 1rem !important; } .ms-xl-4 { margin-left: 1.5rem !important; } .ms-xl-5 { margin-left: 3rem !important; } .ms-xl-auto { margin-left: auto !important; } .p-xl-0 { padding: 0 !important; } .p-xl-1 { padding: 0.25rem !important; } .p-xl-2 { padding: 0.5rem !important; } .p-xl-3 { padding: 1rem !important; } .p-xl-4 { padding: 1.5rem !important; } .p-xl-5 { padding: 3rem !important; } .px-xl-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-xl-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-xl-2 { padding-right: 0.5rem !important; padding-left: 0.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: 3rem !important; padding-left: 3rem !important; } .py-xl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xl-2 { padding-top: 0.5rem !important; padding-bottom: 0.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: 3rem !important; padding-bottom: 3rem !important; } .pt-xl-0 { padding-top: 0 !important; } .pt-xl-1 { padding-top: 0.25rem !important; } .pt-xl-2 { padding-top: 0.5rem !important; } .pt-xl-3 { padding-top: 1rem !important; } .pt-xl-4 { padding-top: 1.5rem !important; } .pt-xl-5 { padding-top: 3rem !important; } .pe-xl-0 { padding-right: 0 !important; } .pe-xl-1 { padding-right: 0.25rem !important; } .pe-xl-2 { padding-right: 0.5rem !important; } .pe-xl-3 { padding-right: 1rem !important; } .pe-xl-4 { padding-right: 1.5rem !important; } .pe-xl-5 { padding-right: 3rem !important; } .pb-xl-0 { padding-bottom: 0 !important; } .pb-xl-1 { padding-bottom: 0.25rem !important; } .pb-xl-2 { padding-bottom: 0.5rem !important; } .pb-xl-3 { padding-bottom: 1rem !important; } .pb-xl-4 { padding-bottom: 1.5rem !important; } .pb-xl-5 { padding-bottom: 3rem !important; } .ps-xl-0 { padding-left: 0 !important; } .ps-xl-1 { padding-left: 0.25rem !important; } .ps-xl-2 { padding-left: 0.5rem !important; } .ps-xl-3 { padding-left: 1rem !important; } .ps-xl-4 { padding-left: 1.5rem !important; } .ps-xl-5 { padding-left: 3rem !important; } .gap-xl-0 { gap: 0 !important; } .gap-xl-1 { gap: 0.25rem !important; } .gap-xl-2 { gap: 0.5rem !important; } .gap-xl-3 { gap: 1rem !important; } .gap-xl-4 { gap: 1.5rem !important; } .gap-xl-5 { gap: 3rem !important; } .row-gap-xl-0 { row-gap: 0 !important; } .row-gap-xl-1 { row-gap: 0.25rem !important; } .row-gap-xl-2 { row-gap: 0.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: 3rem !important; } .column-gap-xl-0 { -moz-column-gap: 0 !important; column-gap: 0 !important; } .column-gap-xl-1 { -moz-column-gap: 0.25rem !important; column-gap: 0.25rem !important; } .column-gap-xl-2 { -moz-column-gap: 0.5rem !important; column-gap: 0.5rem !important; } .column-gap-xl-3 { -moz-column-gap: 1rem !important; column-gap: 1rem !important; } .column-gap-xl-4 { -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important; } .column-gap-xl-5 { -moz-column-gap: 3rem !important; column-gap: 3rem !important; } .text-xl-start { text-align: left !important; } .text-xl-end { text-align: right !important; } .text-xl-center { text-align: center !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 { -o-object-fit: contain !important; object-fit: contain !important; } .object-fit-xxl-cover { -o-object-fit: cover !important; object-fit: cover !important; } .object-fit-xxl-fill { -o-object-fit: fill !important; object-fit: fill !important; } .object-fit-xxl-scale { -o-object-fit: scale-down !important; object-fit: scale-down !important; } .object-fit-xxl-none { -o-object-fit: none !important; 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: 0.25rem !important; } .m-xxl-2 { margin: 0.5rem !important; } .m-xxl-3 { margin: 1rem !important; } .m-xxl-4 { margin: 1.5rem !important; } .m-xxl-5 { margin: 3rem !important; } .m-xxl-auto { margin: auto !important; } .mx-xxl-0 { margin-right: 0 !important; margin-left: 0 !important; } .mx-xxl-1 { margin-right: 0.25rem !important; margin-left: 0.25rem !important; } .mx-xxl-2 { margin-right: 0.5rem !important; margin-left: 0.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: 3rem !important; margin-left: 3rem !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: 0.25rem !important; margin-bottom: 0.25rem !important; } .my-xxl-2 { margin-top: 0.5rem !important; margin-bottom: 0.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: 3rem !important; margin-bottom: 3rem !important; } .my-xxl-auto { margin-top: auto !important; margin-bottom: auto !important; } .mt-xxl-0 { margin-top: 0 !important; } .mt-xxl-1 { margin-top: 0.25rem !important; } .mt-xxl-2 { margin-top: 0.5rem !important; } .mt-xxl-3 { margin-top: 1rem !important; } .mt-xxl-4 { margin-top: 1.5rem !important; } .mt-xxl-5 { margin-top: 3rem !important; } .mt-xxl-auto { margin-top: auto !important; } .me-xxl-0 { margin-right: 0 !important; } .me-xxl-1 { margin-right: 0.25rem !important; } .me-xxl-2 { margin-right: 0.5rem !important; } .me-xxl-3 { margin-right: 1rem !important; } .me-xxl-4 { margin-right: 1.5rem !important; } .me-xxl-5 { margin-right: 3rem !important; } .me-xxl-auto { margin-right: auto !important; } .mb-xxl-0 { margin-bottom: 0 !important; } .mb-xxl-1 { margin-bottom: 0.25rem !important; } .mb-xxl-2 { margin-bottom: 0.5rem !important; } .mb-xxl-3 { margin-bottom: 1rem !important; } .mb-xxl-4 { margin-bottom: 1.5rem !important; } .mb-xxl-5 { margin-bottom: 3rem !important; } .mb-xxl-auto { margin-bottom: auto !important; } .ms-xxl-0 { margin-left: 0 !important; } .ms-xxl-1 { margin-left: 0.25rem !important; } .ms-xxl-2 { margin-left: 0.5rem !important; } .ms-xxl-3 { margin-left: 1rem !important; } .ms-xxl-4 { margin-left: 1.5rem !important; } .ms-xxl-5 { margin-left: 3rem !important; } .ms-xxl-auto { margin-left: auto !important; } .p-xxl-0 { padding: 0 !important; } .p-xxl-1 { padding: 0.25rem !important; } .p-xxl-2 { padding: 0.5rem !important; } .p-xxl-3 { padding: 1rem !important; } .p-xxl-4 { padding: 1.5rem !important; } .p-xxl-5 { padding: 3rem !important; } .px-xxl-0 { padding-right: 0 !important; padding-left: 0 !important; } .px-xxl-1 { padding-right: 0.25rem !important; padding-left: 0.25rem !important; } .px-xxl-2 { padding-right: 0.5rem !important; padding-left: 0.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: 3rem !important; padding-left: 3rem !important; } .py-xxl-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-xxl-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; } .py-xxl-2 { padding-top: 0.5rem !important; padding-bottom: 0.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: 3rem !important; padding-bottom: 3rem !important; } .pt-xxl-0 { padding-top: 0 !important; } .pt-xxl-1 { padding-top: 0.25rem !important; } .pt-xxl-2 { padding-top: 0.5rem !important; } .pt-xxl-3 { padding-top: 1rem !important; } .pt-xxl-4 { padding-top: 1.5rem !important; } .pt-xxl-5 { padding-top: 3rem !important; } .pe-xxl-0 { padding-right: 0 !important; } .pe-xxl-1 { padding-right: 0.25rem !important; } .pe-xxl-2 { padding-right: 0.5rem !important; } .pe-xxl-3 { padding-right: 1rem !important; } .pe-xxl-4 { padding-right: 1.5rem !important; } .pe-xxl-5 { padding-right: 3rem !important; } .pb-xxl-0 { padding-bottom: 0 !important; } .pb-xxl-1 { padding-bottom: 0.25rem !important; } .pb-xxl-2 { padding-bottom: 0.5rem !important; } .pb-xxl-3 { padding-bottom: 1rem !important; } .pb-xxl-4 { padding-bottom: 1.5rem !important; } .pb-xxl-5 { padding-bottom: 3rem !important; } .ps-xxl-0 { padding-left: 0 !important; } .ps-xxl-1 { padding-left: 0.25rem !important; } .ps-xxl-2 { padding-left: 0.5rem !important; } .ps-xxl-3 { padding-left: 1rem !important; } .ps-xxl-4 { padding-left: 1.5rem !important; } .ps-xxl-5 { padding-left: 3rem !important; } .gap-xxl-0 { gap: 0 !important; } .gap-xxl-1 { gap: 0.25rem !important; } .gap-xxl-2 { gap: 0.5rem !important; } .gap-xxl-3 { gap: 1rem !important; } .gap-xxl-4 { gap: 1.5rem !important; } .gap-xxl-5 { gap: 3rem !important; } .row-gap-xxl-0 { row-gap: 0 !important; } .row-gap-xxl-1 { row-gap: 0.25rem !important; } .row-gap-xxl-2 { row-gap: 0.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: 3rem !important; } .column-gap-xxl-0 { -moz-column-gap: 0 !important; column-gap: 0 !important; } .column-gap-xxl-1 { -moz-column-gap: 0.25rem !important; column-gap: 0.25rem !important; } .column-gap-xxl-2 { -moz-column-gap: 0.5rem !important; column-gap: 0.5rem !important; } .column-gap-xxl-3 { -moz-column-gap: 1rem !important; column-gap: 1rem !important; } .column-gap-xxl-4 { -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important; } .column-gap-xxl-5 { -moz-column-gap: 3rem !important; column-gap: 3rem !important; } .text-xxl-start { text-align: left !important; } .text-xxl-end { text-align: right !important; } .text-xxl-center { text-align: center !important; } } @media (min-width: 1200px) { .fs-1 { font-size: 2.5rem !important; } .fs-2 { font-size: 2rem !important; } .fs-3 { font-size: 1.75rem !important; } .fs-4 { font-size: 1.5rem !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; } } a2d-2.0.5/a2d/static/bootstrap/js/000077500000000000000000000000001464731662700165725ustar00rootroot00000000000000a2d-2.0.5/a2d/static/bootstrap/js/bootstrap_5-3_mode.js000066400000000000000000000052471464731662700225450ustar00rootroot00000000000000/*! Mode * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/) * Copyright 2011-2023 The Bootstrap Authors * Licensed under the Creative Commons Attribution 3.0 Unported License. * For details, see https://creativecommons.org/licenses/by/3.0/. */ (() => { 'use strict' const getStoredTheme = () => localStorage.getItem('theme') const setStoredTheme = theme => localStorage.setItem('theme', theme) const getPreferredTheme = () => { const storedTheme = getStoredTheme() if (storedTheme) { return storedTheme } return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' } const setTheme = theme => { if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) { document.documentElement.setAttribute('data-bs-theme', 'dark') } else { document.documentElement.setAttribute('data-bs-theme', theme) } } setTheme(getPreferredTheme()) const showActiveTheme = (theme, focus = false) => { const themeSwitcher = document.querySelector('#bd-theme') if (!themeSwitcher) { return } const themeSwitcherText = document.querySelector('#bd-theme-text') const activeThemeIcon = document.querySelector('.theme-icon-active use') const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`) const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href') document.querySelectorAll('[data-bs-theme-value]').forEach(element => { element.classList.remove('active') element.setAttribute('aria-pressed', 'false') }) btnToActive.classList.add('active') btnToActive.setAttribute('aria-pressed', 'true') activeThemeIcon.setAttribute('href', svgOfActiveBtn) const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})` themeSwitcher.setAttribute('aria-label', themeSwitcherLabel) if (focus) { themeSwitcher.focus() } } window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { const storedTheme = getStoredTheme() if (storedTheme !== 'light' && storedTheme !== 'dark') { setTheme(getPreferredTheme()) } }) window.addEventListener('DOMContentLoaded', () => { showActiveTheme(getPreferredTheme()) document.querySelectorAll('[data-bs-theme-value]') .forEach(toggle => { toggle.addEventListener('click', () => { const theme = toggle.getAttribute('data-bs-theme-value') setStoredTheme(theme) setTheme(theme) showActiveTheme(theme, true) location.reload(); // Refresh the page after theme selection }) }) }) })() a2d-2.0.5/a2d/static/bootstrap/js/cdn_bootstrap_5-3.js000066400000000000000000003642331464731662700223700ustar00rootroot00000000000000/*! * Bootstrap v5.3.0 (https://getbootstrap.com/) * Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ !(function (t, e) { "object" == typeof exports && "undefined" != typeof module ? (module.exports = e()) : "function" == typeof define && define.amd ? define(e) : ((t = "undefined" != typeof globalThis ? globalThis : t || self).bootstrap = e()); })(this, function () { "use strict"; const t = new Map(), e = { set(e, i, n) { t.has(e) || t.set(e, new Map()); const s = t.get(e); s.has(i) || 0 === s.size ? s.set(i, n) : console.error( `Bootstrap doesn't allow more than one instance per element. Bound instance: ${ Array.from(s.keys())[0] }.` ); }, get: (e, i) => (t.has(e) && t.get(e).get(i)) || null, remove(e, i) { if (!t.has(e)) return; const n = t.get(e); n.delete(i), 0 === n.size && t.delete(e); }, }, i = "transitionend", n = (t) => ( t && window.CSS && window.CSS.escape && (t = t.replace(/#([^\s"#']+)/g, (t, e) => `#${CSS.escape(e)}`)), t ), s = (t) => { t.dispatchEvent(new Event(i)); }, o = (t) => !(!t || "object" != typeof t) && (void 0 !== t.jquery && (t = t[0]), void 0 !== t.nodeType), r = (t) => o(t) ? t.jquery ? t[0] : t : "string" == typeof t && t.length > 0 ? document.querySelector(n(t)) : null, a = (t) => { if (!o(t) || 0 === t.getClientRects().length) return !1; const e = "visible" === getComputedStyle(t).getPropertyValue("visibility"), i = t.closest("details:not([open])"); if (!i) return e; if (i !== t) { const e = t.closest("summary"); if (e && e.parentNode !== i) return !1; if (null === e) return !1; } return e; }, l = (t) => !t || t.nodeType !== Node.ELEMENT_NODE || !!t.classList.contains("disabled") || (void 0 !== t.disabled ? t.disabled : t.hasAttribute("disabled") && "false" !== t.getAttribute("disabled")), c = (t) => { if (!document.documentElement.attachShadow) return null; if ("function" == typeof t.getRootNode) { const e = t.getRootNode(); return e instanceof ShadowRoot ? e : null; } return t instanceof ShadowRoot ? t : t.parentNode ? c(t.parentNode) : null; }, h = () => {}, d = (t) => { t.offsetHeight; }, u = () => window.jQuery && !document.body.hasAttribute("data-bs-no-jquery") ? window.jQuery : null, f = [], p = () => "rtl" === document.documentElement.dir, m = (t) => { var e; (e = () => { const e = u(); if (e) { const i = t.NAME, n = e.fn[i]; (e.fn[i] = t.jQueryInterface), (e.fn[i].Constructor = t), (e.fn[i].noConflict = () => ((e.fn[i] = n), t.jQueryInterface)); } }), "loading" === document.readyState ? (f.length || document.addEventListener("DOMContentLoaded", () => { for (const t of f) t(); }), f.push(e)) : e(); }, g = (t, e = [], i = t) => ("function" == typeof t ? t(...e) : i), _ = (t, e, n = !0) => { if (!n) return void g(t); const o = ((t) => { if (!t) return 0; let { transitionDuration: e, transitionDelay: i } = window.getComputedStyle(t); const n = Number.parseFloat(e), s = Number.parseFloat(i); return n || s ? ((e = e.split(",")[0]), (i = i.split(",")[0]), 1e3 * (Number.parseFloat(e) + Number.parseFloat(i))) : 0; })(e) + 5; let r = !1; const a = ({ target: n }) => { n === e && ((r = !0), e.removeEventListener(i, a), g(t)); }; e.addEventListener(i, a), setTimeout(() => { r || s(e); }, o); }, b = (t, e, i, n) => { const s = t.length; let o = t.indexOf(e); return -1 === o ? !i && n ? t[s - 1] : t[0] : ((o += i ? 1 : -1), n && (o = (o + s) % s), t[Math.max(0, Math.min(o, s - 1))]); }, v = /[^.]*(?=\..*)\.|.*/, y = /\..*/, w = /::\d+$/, A = {}; let E = 1; const T = { mouseenter: "mouseover", mouseleave: "mouseout" }, C = new Set([ "click", "dblclick", "mouseup", "mousedown", "contextmenu", "mousewheel", "DOMMouseScroll", "mouseover", "mouseout", "mousemove", "selectstart", "selectend", "keydown", "keypress", "keyup", "orientationchange", "touchstart", "touchmove", "touchend", "touchcancel", "pointerdown", "pointermove", "pointerup", "pointerleave", "pointercancel", "gesturestart", "gesturechange", "gestureend", "focus", "blur", "change", "reset", "select", "submit", "focusin", "focusout", "load", "unload", "beforeunload", "resize", "move", "DOMContentLoaded", "readystatechange", "error", "abort", "scroll", ]); function O(t, e) { return (e && `${e}::${E++}`) || t.uidEvent || E++; } function x(t) { const e = O(t); return (t.uidEvent = e), (A[e] = A[e] || {}), A[e]; } function k(t, e, i = null) { return Object.values(t).find( (t) => t.callable === e && t.delegationSelector === i ); } function L(t, e, i) { const n = "string" == typeof e, s = n ? i : e || i; let o = N(t); return C.has(o) || (o = t), [n, s, o]; } function S(t, e, i, n, s) { if ("string" != typeof e || !t) return; let [o, r, a] = L(e, i, n); if (e in T) { const t = (t) => function (e) { if ( !e.relatedTarget || (e.relatedTarget !== e.delegateTarget && !e.delegateTarget.contains(e.relatedTarget)) ) return t.call(this, e); }; r = t(r); } const l = x(t), c = l[a] || (l[a] = {}), h = k(c, r, o ? i : null); if (h) return void (h.oneOff = h.oneOff && s); const d = O(r, e.replace(v, "")), u = o ? (function (t, e, i) { return function n(s) { const o = t.querySelectorAll(e); for (let { target: r } = s; r && r !== this; r = r.parentNode) for (const a of o) if (a === r) return ( M(s, { delegateTarget: r }), n.oneOff && P.off(t, s.type, e, i), i.apply(r, [s]) ); }; })(t, i, r) : (function (t, e) { return function i(n) { return ( M(n, { delegateTarget: t }), i.oneOff && P.off(t, n.type, e), e.apply(t, [n]) ); }; })(t, r); (u.delegationSelector = o ? i : null), (u.callable = r), (u.oneOff = s), (u.uidEvent = d), (c[d] = u), t.addEventListener(a, u, o); } function D(t, e, i, n, s) { const o = k(e[i], n, s); o && (t.removeEventListener(i, o, Boolean(s)), delete e[i][o.uidEvent]); } function I(t, e, i, n) { const s = e[i] || {}; for (const [o, r] of Object.entries(s)) o.includes(n) && D(t, e, i, r.callable, r.delegationSelector); } function N(t) { return (t = t.replace(y, "")), T[t] || t; } const P = { on(t, e, i, n) { S(t, e, i, n, !1); }, one(t, e, i, n) { S(t, e, i, n, !0); }, off(t, e, i, n) { if ("string" != typeof e || !t) return; const [s, o, r] = L(e, i, n), a = r !== e, l = x(t), c = l[r] || {}, h = e.startsWith("."); if (void 0 === o) { if (h) for (const i of Object.keys(l)) I(t, l, i, e.slice(1)); for (const [i, n] of Object.entries(c)) { const s = i.replace(w, ""); (a && !e.includes(s)) || D(t, l, r, n.callable, n.delegationSelector); } } else { if (!Object.keys(c).length) return; D(t, l, r, o, s ? i : null); } }, trigger(t, e, i) { if ("string" != typeof e || !t) return null; const n = u(); let s = null, o = !0, r = !0, a = !1; e !== N(e) && n && ((s = n.Event(e, i)), n(t).trigger(s), (o = !s.isPropagationStopped()), (r = !s.isImmediatePropagationStopped()), (a = s.isDefaultPrevented())); const l = M(new Event(e, { bubbles: o, cancelable: !0 }), i); return ( a && l.preventDefault(), r && t.dispatchEvent(l), l.defaultPrevented && s && s.preventDefault(), l ); }, }; function M(t, e = {}) { for (const [i, n] of Object.entries(e)) try { t[i] = n; } catch (e) { Object.defineProperty(t, i, { configurable: !0, get: () => n }); } return t; } function j(t) { if ("true" === t) return !0; if ("false" === t) return !1; if (t === Number(t).toString()) return Number(t); if ("" === t || "null" === t) return null; if ("string" != typeof t) return t; try { return JSON.parse(decodeURIComponent(t)); } catch (e) { return t; } } function F(t) { return t.replace(/[A-Z]/g, (t) => `-${t.toLowerCase()}`); } const H = { setDataAttribute(t, e, i) { t.setAttribute(`data-bs-${F(e)}`, i); }, removeDataAttribute(t, e) { t.removeAttribute(`data-bs-${F(e)}`); }, getDataAttributes(t) { if (!t) return {}; const e = {}, i = Object.keys(t.dataset).filter( (t) => t.startsWith("bs") && !t.startsWith("bsConfig") ); for (const n of i) { let i = n.replace(/^bs/, ""); (i = i.charAt(0).toLowerCase() + i.slice(1, i.length)), (e[i] = j(t.dataset[n])); } return e; }, getDataAttribute: (t, e) => j(t.getAttribute(`data-bs-${F(e)}`)), }; class $ { static get Default() { return {}; } static get DefaultType() { return {}; } static get NAME() { throw new Error( 'You have to implement the static method "NAME", for each component!' ); } _getConfig(t) { return ( (t = this._mergeConfigObj(t)), (t = this._configAfterMerge(t)), this._typeCheckConfig(t), t ); } _configAfterMerge(t) { return t; } _mergeConfigObj(t, e) { const i = o(e) ? H.getDataAttribute(e, "config") : {}; return { ...this.constructor.Default, ...("object" == typeof i ? i : {}), ...(o(e) ? H.getDataAttributes(e) : {}), ...("object" == typeof t ? t : {}), }; } _typeCheckConfig(t, e = this.constructor.DefaultType) { for (const [n, s] of Object.entries(e)) { const e = t[n], r = o(e) ? "element" : null == (i = e) ? `${i}` : Object.prototype.toString .call(i) .match(/\s([a-z]+)/i)[1] .toLowerCase(); if (!new RegExp(s).test(r)) throw new TypeError( `${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${r}" but expected type "${s}".` ); } var i; } } class W extends $ { constructor(t, i) { super(), (t = r(t)) && ((this._element = t), (this._config = this._getConfig(i)), e.set(this._element, this.constructor.DATA_KEY, this)); } dispose() { e.remove(this._element, this.constructor.DATA_KEY), P.off(this._element, this.constructor.EVENT_KEY); for (const t of Object.getOwnPropertyNames(this)) this[t] = null; } _queueCallback(t, e, i = !0) { _(t, e, i); } _getConfig(t) { return ( (t = this._mergeConfigObj(t, this._element)), (t = this._configAfterMerge(t)), this._typeCheckConfig(t), t ); } static getInstance(t) { return e.get(r(t), this.DATA_KEY); } static getOrCreateInstance(t, e = {}) { return ( this.getInstance(t) || new this(t, "object" == typeof e ? e : null) ); } static get VERSION() { return "5.3.0"; } static get DATA_KEY() { return `bs.${this.NAME}`; } static get EVENT_KEY() { return `.${this.DATA_KEY}`; } static eventName(t) { return `${t}${this.EVENT_KEY}`; } } const B = (t) => { let e = t.getAttribute("data-bs-target"); if (!e || "#" === e) { let i = t.getAttribute("href"); if (!i || (!i.includes("#") && !i.startsWith("."))) return null; i.includes("#") && !i.startsWith("#") && (i = `#${i.split("#")[1]}`), (e = i && "#" !== i ? i.trim() : null); } return n(e); }, z = { find: (t, e = document.documentElement) => [].concat(...Element.prototype.querySelectorAll.call(e, t)), findOne: (t, e = document.documentElement) => Element.prototype.querySelector.call(e, t), children: (t, e) => [].concat(...t.children).filter((t) => t.matches(e)), parents(t, e) { const i = []; let n = t.parentNode.closest(e); for (; n; ) i.push(n), (n = n.parentNode.closest(e)); return i; }, prev(t, e) { let i = t.previousElementSibling; for (; i; ) { if (i.matches(e)) return [i]; i = i.previousElementSibling; } return []; }, next(t, e) { let i = t.nextElementSibling; for (; i; ) { if (i.matches(e)) return [i]; i = i.nextElementSibling; } return []; }, focusableChildren(t) { const e = [ "a", "button", "input", "textarea", "select", "details", "[tabindex]", '[contenteditable="true"]', ] .map((t) => `${t}:not([tabindex^="-"])`) .join(","); return this.find(e, t).filter((t) => !l(t) && a(t)); }, getSelectorFromElement(t) { const e = B(t); return e && z.findOne(e) ? e : null; }, getElementFromSelector(t) { const e = B(t); return e ? z.findOne(e) : null; }, getMultipleElementsFromSelector(t) { const e = B(t); return e ? z.find(e) : []; }, }, R = (t, e = "hide") => { const i = `click.dismiss${t.EVENT_KEY}`, n = t.NAME; P.on(document, i, `[data-bs-dismiss="${n}"]`, function (i) { if ( (["A", "AREA"].includes(this.tagName) && i.preventDefault(), l(this)) ) return; const s = z.getElementFromSelector(this) || this.closest(`.${n}`); t.getOrCreateInstance(s)[e](); }); }; class q extends W { static get NAME() { return "alert"; } close() { if (P.trigger(this._element, "close.bs.alert").defaultPrevented) return; this._element.classList.remove("show"); const t = this._element.classList.contains("fade"); this._queueCallback(() => this._destroyElement(), this._element, t); } _destroyElement() { this._element.remove(), P.trigger(this._element, "closed.bs.alert"), this.dispose(); } static jQueryInterface(t) { return this.each(function () { const e = q.getOrCreateInstance(this); if ("string" == typeof t) { if (void 0 === e[t] || t.startsWith("_") || "constructor" === t) throw new TypeError(`No method named "${t}"`); e[t](this); } }); } } R(q, "close"), m(q); const V = '[data-bs-toggle="button"]'; class K extends W { static get NAME() { return "button"; } toggle() { this._element.setAttribute( "aria-pressed", this._element.classList.toggle("active") ); } static jQueryInterface(t) { return this.each(function () { const e = K.getOrCreateInstance(this); "toggle" === t && e[t](); }); } } P.on(document, "click.bs.button.data-api", V, (t) => { t.preventDefault(); const e = t.target.closest(V); K.getOrCreateInstance(e).toggle(); }), m(K); const Q = { endCallback: null, leftCallback: null, rightCallback: null }, X = { endCallback: "(function|null)", leftCallback: "(function|null)", rightCallback: "(function|null)", }; class Y extends $ { constructor(t, e) { super(), (this._element = t), t && Y.isSupported() && ((this._config = this._getConfig(e)), (this._deltaX = 0), (this._supportPointerEvents = Boolean(window.PointerEvent)), this._initEvents()); } static get Default() { return Q; } static get DefaultType() { return X; } static get NAME() { return "swipe"; } dispose() { P.off(this._element, ".bs.swipe"); } _start(t) { this._supportPointerEvents ? this._eventIsPointerPenTouch(t) && (this._deltaX = t.clientX) : (this._deltaX = t.touches[0].clientX); } _end(t) { this._eventIsPointerPenTouch(t) && (this._deltaX = t.clientX - this._deltaX), this._handleSwipe(), g(this._config.endCallback); } _move(t) { this._deltaX = t.touches && t.touches.length > 1 ? 0 : t.touches[0].clientX - this._deltaX; } _handleSwipe() { const t = Math.abs(this._deltaX); if (t <= 40) return; const e = t / this._deltaX; (this._deltaX = 0), e && g(e > 0 ? this._config.rightCallback : this._config.leftCallback); } _initEvents() { this._supportPointerEvents ? (P.on(this._element, "pointerdown.bs.swipe", (t) => this._start(t)), P.on(this._element, "pointerup.bs.swipe", (t) => this._end(t)), this._element.classList.add("pointer-event")) : (P.on(this._element, "touchstart.bs.swipe", (t) => this._start(t)), P.on(this._element, "touchmove.bs.swipe", (t) => this._move(t)), P.on(this._element, "touchend.bs.swipe", (t) => this._end(t))); } _eventIsPointerPenTouch(t) { return ( this._supportPointerEvents && ("pen" === t.pointerType || "touch" === t.pointerType) ); } static isSupported() { return ( "ontouchstart" in document.documentElement || navigator.maxTouchPoints > 0 ); } } const U = "next", G = "prev", J = "left", Z = "right", tt = "slid.bs.carousel", et = "carousel", it = "active", nt = { ArrowLeft: Z, ArrowRight: J }, st = { interval: 5e3, keyboard: !0, pause: "hover", ride: !1, touch: !0, wrap: !0, }, ot = { interval: "(number|boolean)", keyboard: "boolean", pause: "(string|boolean)", ride: "(boolean|string)", touch: "boolean", wrap: "boolean", }; class rt extends W { constructor(t, e) { super(t, e), (this._interval = null), (this._activeElement = null), (this._isSliding = !1), (this.touchTimeout = null), (this._swipeHelper = null), (this._indicatorsElement = z.findOne( ".carousel-indicators", this._element )), this._addEventListeners(), this._config.ride === et && this.cycle(); } static get Default() { return st; } static get DefaultType() { return ot; } static get NAME() { return "carousel"; } next() { this._slide(U); } nextWhenVisible() { !document.hidden && a(this._element) && this.next(); } prev() { this._slide(G); } pause() { this._isSliding && s(this._element), this._clearInterval(); } cycle() { this._clearInterval(), this._updateInterval(), (this._interval = setInterval( () => this.nextWhenVisible(), this._config.interval )); } _maybeEnableCycle() { this._config.ride && (this._isSliding ? P.one(this._element, tt, () => this.cycle()) : this.cycle()); } to(t) { const e = this._getItems(); if (t > e.length - 1 || t < 0) return; if (this._isSliding) return void P.one(this._element, tt, () => this.to(t)); const i = this._getItemIndex(this._getActive()); if (i === t) return; const n = t > i ? U : G; this._slide(n, e[t]); } dispose() { this._swipeHelper && this._swipeHelper.dispose(), super.dispose(); } _configAfterMerge(t) { return (t.defaultInterval = t.interval), t; } _addEventListeners() { this._config.keyboard && P.on(this._element, "keydown.bs.carousel", (t) => this._keydown(t)), "hover" === this._config.pause && (P.on(this._element, "mouseenter.bs.carousel", () => this.pause()), P.on(this._element, "mouseleave.bs.carousel", () => this._maybeEnableCycle() )), this._config.touch && Y.isSupported() && this._addTouchEventListeners(); } _addTouchEventListeners() { for (const t of z.find(".carousel-item img", this._element)) P.on(t, "dragstart.bs.carousel", (t) => t.preventDefault()); const t = { leftCallback: () => this._slide(this._directionToOrder(J)), rightCallback: () => this._slide(this._directionToOrder(Z)), endCallback: () => { "hover" === this._config.pause && (this.pause(), this.touchTimeout && clearTimeout(this.touchTimeout), (this.touchTimeout = setTimeout( () => this._maybeEnableCycle(), 500 + this._config.interval ))); }, }; this._swipeHelper = new Y(this._element, t); } _keydown(t) { if (/input|textarea/i.test(t.target.tagName)) return; const e = nt[t.key]; e && (t.preventDefault(), this._slide(this._directionToOrder(e))); } _getItemIndex(t) { return this._getItems().indexOf(t); } _setActiveIndicatorElement(t) { if (!this._indicatorsElement) return; const e = z.findOne(".active", this._indicatorsElement); e.classList.remove(it), e.removeAttribute("aria-current"); const i = z.findOne(`[data-bs-slide-to="${t}"]`, this._indicatorsElement); i && (i.classList.add(it), i.setAttribute("aria-current", "true")); } _updateInterval() { const t = this._activeElement || this._getActive(); if (!t) return; const e = Number.parseInt(t.getAttribute("data-bs-interval"), 10); this._config.interval = e || this._config.defaultInterval; } _slide(t, e = null) { if (this._isSliding) return; const i = this._getActive(), n = t === U, s = e || b(this._getItems(), i, n, this._config.wrap); if (s === i) return; const o = this._getItemIndex(s), r = (e) => P.trigger(this._element, e, { relatedTarget: s, direction: this._orderToDirection(t), from: this._getItemIndex(i), to: o, }); if (r("slide.bs.carousel").defaultPrevented) return; if (!i || !s) return; const a = Boolean(this._interval); this.pause(), (this._isSliding = !0), this._setActiveIndicatorElement(o), (this._activeElement = s); const l = n ? "carousel-item-start" : "carousel-item-end", c = n ? "carousel-item-next" : "carousel-item-prev"; s.classList.add(c), d(s), i.classList.add(l), s.classList.add(l), this._queueCallback( () => { s.classList.remove(l, c), s.classList.add(it), i.classList.remove(it, c, l), (this._isSliding = !1), r(tt); }, i, this._isAnimated() ), a && this.cycle(); } _isAnimated() { return this._element.classList.contains("slide"); } _getActive() { return z.findOne(".active.carousel-item", this._element); } _getItems() { return z.find(".carousel-item", this._element); } _clearInterval() { this._interval && (clearInterval(this._interval), (this._interval = null)); } _directionToOrder(t) { return p() ? (t === J ? G : U) : t === J ? U : G; } _orderToDirection(t) { return p() ? (t === G ? J : Z) : t === G ? Z : J; } static jQueryInterface(t) { return this.each(function () { const e = rt.getOrCreateInstance(this, t); if ("number" != typeof t) { if ("string" == typeof t) { if (void 0 === e[t] || t.startsWith("_") || "constructor" === t) throw new TypeError(`No method named "${t}"`); e[t](); } } else e.to(t); }); } } P.on( document, "click.bs.carousel.data-api", "[data-bs-slide], [data-bs-slide-to]", function (t) { const e = z.getElementFromSelector(this); if (!e || !e.classList.contains(et)) return; t.preventDefault(); const i = rt.getOrCreateInstance(e), n = this.getAttribute("data-bs-slide-to"); return n ? (i.to(n), void i._maybeEnableCycle()) : "next" === H.getDataAttribute(this, "slide") ? (i.next(), void i._maybeEnableCycle()) : (i.prev(), void i._maybeEnableCycle()); } ), P.on(window, "load.bs.carousel.data-api", () => { const t = z.find('[data-bs-ride="carousel"]'); for (const e of t) rt.getOrCreateInstance(e); }), m(rt); const at = "show", lt = "collapse", ct = "collapsing", ht = '[data-bs-toggle="collapse"]', dt = { parent: null, toggle: !0 }, ut = { parent: "(null|element)", toggle: "boolean" }; class ft extends W { constructor(t, e) { super(t, e), (this._isTransitioning = !1), (this._triggerArray = []); const i = z.find(ht); for (const t of i) { const e = z.getSelectorFromElement(t), i = z.find(e).filter((t) => t === this._element); null !== e && i.length && this._triggerArray.push(t); } this._initializeChildren(), this._config.parent || this._addAriaAndCollapsedClass(this._triggerArray, this._isShown()), this._config.toggle && this.toggle(); } static get Default() { return dt; } static get DefaultType() { return ut; } static get NAME() { return "collapse"; } toggle() { this._isShown() ? this.hide() : this.show(); } show() { if (this._isTransitioning || this._isShown()) return; let t = []; if ( (this._config.parent && (t = this._getFirstLevelChildren( ".collapse.show, .collapse.collapsing" ) .filter((t) => t !== this._element) .map((t) => ft.getOrCreateInstance(t, { toggle: !1 }))), t.length && t[0]._isTransitioning) ) return; if (P.trigger(this._element, "show.bs.collapse").defaultPrevented) return; for (const e of t) e.hide(); const e = this._getDimension(); this._element.classList.remove(lt), this._element.classList.add(ct), (this._element.style[e] = 0), this._addAriaAndCollapsedClass(this._triggerArray, !0), (this._isTransitioning = !0); const i = `scroll${e[0].toUpperCase() + e.slice(1)}`; this._queueCallback( () => { (this._isTransitioning = !1), this._element.classList.remove(ct), this._element.classList.add(lt, at), (this._element.style[e] = ""), P.trigger(this._element, "shown.bs.collapse"); }, this._element, !0 ), (this._element.style[e] = `${this._element[i]}px`); } hide() { if (this._isTransitioning || !this._isShown()) return; if (P.trigger(this._element, "hide.bs.collapse").defaultPrevented) return; const t = this._getDimension(); (this._element.style[t] = `${ this._element.getBoundingClientRect()[t] }px`), d(this._element), this._element.classList.add(ct), this._element.classList.remove(lt, at); for (const t of this._triggerArray) { const e = z.getElementFromSelector(t); e && !this._isShown(e) && this._addAriaAndCollapsedClass([t], !1); } (this._isTransitioning = !0), (this._element.style[t] = ""), this._queueCallback( () => { (this._isTransitioning = !1), this._element.classList.remove(ct), this._element.classList.add(lt), P.trigger(this._element, "hidden.bs.collapse"); }, this._element, !0 ); } _isShown(t = this._element) { return t.classList.contains(at); } _configAfterMerge(t) { return (t.toggle = Boolean(t.toggle)), (t.parent = r(t.parent)), t; } _getDimension() { return this._element.classList.contains("collapse-horizontal") ? "width" : "height"; } _initializeChildren() { if (!this._config.parent) return; const t = this._getFirstLevelChildren(ht); for (const e of t) { const t = z.getElementFromSelector(e); t && this._addAriaAndCollapsedClass([e], this._isShown(t)); } } _getFirstLevelChildren(t) { const e = z.find(":scope .collapse .collapse", this._config.parent); return z.find(t, this._config.parent).filter((t) => !e.includes(t)); } _addAriaAndCollapsedClass(t, e) { if (t.length) for (const i of t) i.classList.toggle("collapsed", !e), i.setAttribute("aria-expanded", e); } static jQueryInterface(t) { const e = {}; return ( "string" == typeof t && /show|hide/.test(t) && (e.toggle = !1), this.each(function () { const i = ft.getOrCreateInstance(this, e); if ("string" == typeof t) { if (void 0 === i[t]) throw new TypeError(`No method named "${t}"`); i[t](); } }) ); } } P.on(document, "click.bs.collapse.data-api", ht, function (t) { ("A" === t.target.tagName || (t.delegateTarget && "A" === t.delegateTarget.tagName)) && t.preventDefault(); for (const t of z.getMultipleElementsFromSelector(this)) ft.getOrCreateInstance(t, { toggle: !1 }).toggle(); }), m(ft); var pt = "top", mt = "bottom", gt = "right", _t = "left", bt = "auto", vt = [pt, mt, gt, _t], yt = "start", wt = "end", At = "clippingParents", Et = "viewport", Tt = "popper", Ct = "reference", Ot = vt.reduce(function (t, e) { return t.concat([e + "-" + yt, e + "-" + wt]); }, []), xt = [].concat(vt, [bt]).reduce(function (t, e) { return t.concat([e, e + "-" + yt, e + "-" + wt]); }, []), kt = "beforeRead", Lt = "read", St = "afterRead", Dt = "beforeMain", It = "main", Nt = "afterMain", Pt = "beforeWrite", Mt = "write", jt = "afterWrite", Ft = [kt, Lt, St, Dt, It, Nt, Pt, Mt, jt]; function Ht(t) { return t ? (t.nodeName || "").toLowerCase() : null; } function $t(t) { if (null == t) return window; if ("[object Window]" !== t.toString()) { var e = t.ownerDocument; return (e && e.defaultView) || window; } return t; } function Wt(t) { return t instanceof $t(t).Element || t instanceof Element; } function Bt(t) { return t instanceof $t(t).HTMLElement || t instanceof HTMLElement; } function zt(t) { return ( "undefined" != typeof ShadowRoot && (t instanceof $t(t).ShadowRoot || t instanceof ShadowRoot) ); } const Rt = { name: "applyStyles", enabled: !0, phase: "write", fn: function (t) { var e = t.state; Object.keys(e.elements).forEach(function (t) { var i = e.styles[t] || {}, n = e.attributes[t] || {}, s = e.elements[t]; Bt(s) && Ht(s) && (Object.assign(s.style, i), Object.keys(n).forEach(function (t) { var e = n[t]; !1 === e ? s.removeAttribute(t) : s.setAttribute(t, !0 === e ? "" : e); })); }); }, effect: function (t) { var e = t.state, i = { popper: { position: e.options.strategy, left: "0", top: "0", margin: "0", }, arrow: { position: "absolute" }, reference: {}, }; return ( Object.assign(e.elements.popper.style, i.popper), (e.styles = i), e.elements.arrow && Object.assign(e.elements.arrow.style, i.arrow), function () { Object.keys(e.elements).forEach(function (t) { var n = e.elements[t], s = e.attributes[t] || {}, o = Object.keys( e.styles.hasOwnProperty(t) ? e.styles[t] : i[t] ).reduce(function (t, e) { return (t[e] = ""), t; }, {}); Bt(n) && Ht(n) && (Object.assign(n.style, o), Object.keys(s).forEach(function (t) { n.removeAttribute(t); })); }); } ); }, requires: ["computeStyles"], }; function qt(t) { return t.split("-")[0]; } var Vt = Math.max, Kt = Math.min, Qt = Math.round; function Xt() { var t = navigator.userAgentData; return null != t && t.brands && Array.isArray(t.brands) ? t.brands .map(function (t) { return t.brand + "/" + t.version; }) .join(" ") : navigator.userAgent; } function Yt() { return !/^((?!chrome|android).)*safari/i.test(Xt()); } function Ut(t, e, i) { void 0 === e && (e = !1), void 0 === i && (i = !1); var n = t.getBoundingClientRect(), s = 1, o = 1; e && Bt(t) && ((s = (t.offsetWidth > 0 && Qt(n.width) / t.offsetWidth) || 1), (o = (t.offsetHeight > 0 && Qt(n.height) / t.offsetHeight) || 1)); var r = (Wt(t) ? $t(t) : window).visualViewport, a = !Yt() && i, l = (n.left + (a && r ? r.offsetLeft : 0)) / s, c = (n.top + (a && r ? r.offsetTop : 0)) / o, h = n.width / s, d = n.height / o; return { width: h, height: d, top: c, right: l + h, bottom: c + d, left: l, x: l, y: c, }; } function Gt(t) { var e = Ut(t), i = t.offsetWidth, n = t.offsetHeight; return ( Math.abs(e.width - i) <= 1 && (i = e.width), Math.abs(e.height - n) <= 1 && (n = e.height), { x: t.offsetLeft, y: t.offsetTop, width: i, height: n } ); } function Jt(t, e) { var i = e.getRootNode && e.getRootNode(); if (t.contains(e)) return !0; if (i && zt(i)) { var n = e; do { if (n && t.isSameNode(n)) return !0; n = n.parentNode || n.host; } while (n); } return !1; } function Zt(t) { return $t(t).getComputedStyle(t); } function te(t) { return ["table", "td", "th"].indexOf(Ht(t)) >= 0; } function ee(t) { return ( (Wt(t) ? t.ownerDocument : t.document) || window.document ).documentElement; } function ie(t) { return "html" === Ht(t) ? t : t.assignedSlot || t.parentNode || (zt(t) ? t.host : null) || ee(t); } function ne(t) { return Bt(t) && "fixed" !== Zt(t).position ? t.offsetParent : null; } function se(t) { for (var e = $t(t), i = ne(t); i && te(i) && "static" === Zt(i).position; ) i = ne(i); return i && ("html" === Ht(i) || ("body" === Ht(i) && "static" === Zt(i).position)) ? e : i || (function (t) { var e = /firefox/i.test(Xt()); if (/Trident/i.test(Xt()) && Bt(t) && "fixed" === Zt(t).position) return null; var i = ie(t); for ( zt(i) && (i = i.host); Bt(i) && ["html", "body"].indexOf(Ht(i)) < 0; ) { var n = Zt(i); if ( "none" !== n.transform || "none" !== n.perspective || "paint" === n.contain || -1 !== ["transform", "perspective"].indexOf(n.willChange) || (e && "filter" === n.willChange) || (e && n.filter && "none" !== n.filter) ) return i; i = i.parentNode; } return null; })(t) || e; } function oe(t) { return ["top", "bottom"].indexOf(t) >= 0 ? "x" : "y"; } function re(t, e, i) { return Vt(t, Kt(e, i)); } function ae(t) { return Object.assign({}, { top: 0, right: 0, bottom: 0, left: 0 }, t); } function le(t, e) { return e.reduce(function (e, i) { return (e[i] = t), e; }, {}); } const ce = { name: "arrow", enabled: !0, phase: "main", fn: function (t) { var e, i = t.state, n = t.name, s = t.options, o = i.elements.arrow, r = i.modifiersData.popperOffsets, a = qt(i.placement), l = oe(a), c = [_t, gt].indexOf(a) >= 0 ? "height" : "width"; if (o && r) { var h = (function (t, e) { return ae( "number" != typeof (t = "function" == typeof t ? t(Object.assign({}, e.rects, { placement: e.placement })) : t) ? t : le(t, vt) ); })(s.padding, i), d = Gt(o), u = "y" === l ? pt : _t, f = "y" === l ? mt : gt, p = i.rects.reference[c] + i.rects.reference[l] - r[l] - i.rects.popper[c], m = r[l] - i.rects.reference[l], g = se(o), _ = g ? ("y" === l ? g.clientHeight || 0 : g.clientWidth || 0) : 0, b = p / 2 - m / 2, v = h[u], y = _ - d[c] - h[f], w = _ / 2 - d[c] / 2 + b, A = re(v, w, y), E = l; i.modifiersData[n] = (((e = {})[E] = A), (e.centerOffset = A - w), e); } }, effect: function (t) { var e = t.state, i = t.options.element, n = void 0 === i ? "[data-popper-arrow]" : i; null != n && ("string" != typeof n || (n = e.elements.popper.querySelector(n))) && Jt(e.elements.popper, n) && (e.elements.arrow = n); }, requires: ["popperOffsets"], requiresIfExists: ["preventOverflow"], }; function he(t) { return t.split("-")[1]; } var de = { top: "auto", right: "auto", bottom: "auto", left: "auto" }; function ue(t) { var e, i = t.popper, n = t.popperRect, s = t.placement, o = t.variation, r = t.offsets, a = t.position, l = t.gpuAcceleration, c = t.adaptive, h = t.roundOffsets, d = t.isFixed, u = r.x, f = void 0 === u ? 0 : u, p = r.y, m = void 0 === p ? 0 : p, g = "function" == typeof h ? h({ x: f, y: m }) : { x: f, y: m }; (f = g.x), (m = g.y); var _ = r.hasOwnProperty("x"), b = r.hasOwnProperty("y"), v = _t, y = pt, w = window; if (c) { var A = se(i), E = "clientHeight", T = "clientWidth"; A === $t(i) && "static" !== Zt((A = ee(i))).position && "absolute" === a && ((E = "scrollHeight"), (T = "scrollWidth")), (s === pt || ((s === _t || s === gt) && o === wt)) && ((y = mt), (m -= (d && A === w && w.visualViewport ? w.visualViewport.height : A[E]) - n.height), (m *= l ? 1 : -1)), (s !== _t && ((s !== pt && s !== mt) || o !== wt)) || ((v = gt), (f -= (d && A === w && w.visualViewport ? w.visualViewport.width : A[T]) - n.width), (f *= l ? 1 : -1)); } var C, O = Object.assign({ position: a }, c && de), x = !0 === h ? (function (t, e) { var i = t.x, n = t.y, s = e.devicePixelRatio || 1; return { x: Qt(i * s) / s || 0, y: Qt(n * s) / s || 0 }; })({ x: f, y: m }, $t(i)) : { x: f, y: m }; return ( (f = x.x), (m = x.y), l ? Object.assign( {}, O, (((C = {})[y] = b ? "0" : ""), (C[v] = _ ? "0" : ""), (C.transform = (w.devicePixelRatio || 1) <= 1 ? "translate(" + f + "px, " + m + "px)" : "translate3d(" + f + "px, " + m + "px, 0)"), C) ) : Object.assign( {}, O, (((e = {})[y] = b ? m + "px" : ""), (e[v] = _ ? f + "px" : ""), (e.transform = ""), e) ) ); } const fe = { name: "computeStyles", enabled: !0, phase: "beforeWrite", fn: function (t) { var e = t.state, i = t.options, n = i.gpuAcceleration, s = void 0 === n || n, o = i.adaptive, r = void 0 === o || o, a = i.roundOffsets, l = void 0 === a || a, c = { placement: qt(e.placement), variation: he(e.placement), popper: e.elements.popper, popperRect: e.rects.popper, gpuAcceleration: s, isFixed: "fixed" === e.options.strategy, }; null != e.modifiersData.popperOffsets && (e.styles.popper = Object.assign( {}, e.styles.popper, ue( Object.assign({}, c, { offsets: e.modifiersData.popperOffsets, position: e.options.strategy, adaptive: r, roundOffsets: l, }) ) )), null != e.modifiersData.arrow && (e.styles.arrow = Object.assign( {}, e.styles.arrow, ue( Object.assign({}, c, { offsets: e.modifiersData.arrow, position: "absolute", adaptive: !1, roundOffsets: l, }) ) )), (e.attributes.popper = Object.assign({}, e.attributes.popper, { "data-popper-placement": e.placement, })); }, data: {}, }; var pe = { passive: !0 }; const me = { name: "eventListeners", enabled: !0, phase: "write", fn: function () {}, effect: function (t) { var e = t.state, i = t.instance, n = t.options, s = n.scroll, o = void 0 === s || s, r = n.resize, a = void 0 === r || r, l = $t(e.elements.popper), c = [].concat(e.scrollParents.reference, e.scrollParents.popper); return ( o && c.forEach(function (t) { t.addEventListener("scroll", i.update, pe); }), a && l.addEventListener("resize", i.update, pe), function () { o && c.forEach(function (t) { t.removeEventListener("scroll", i.update, pe); }), a && l.removeEventListener("resize", i.update, pe); } ); }, data: {}, }; var ge = { left: "right", right: "left", bottom: "top", top: "bottom" }; function _e(t) { return t.replace(/left|right|bottom|top/g, function (t) { return ge[t]; }); } var be = { start: "end", end: "start" }; function ve(t) { return t.replace(/start|end/g, function (t) { return be[t]; }); } function ye(t) { var e = $t(t); return { scrollLeft: e.pageXOffset, scrollTop: e.pageYOffset }; } function we(t) { return Ut(ee(t)).left + ye(t).scrollLeft; } function Ae(t) { var e = Zt(t), i = e.overflow, n = e.overflowX, s = e.overflowY; return /auto|scroll|overlay|hidden/.test(i + s + n); } function Ee(t) { return ["html", "body", "#document"].indexOf(Ht(t)) >= 0 ? t.ownerDocument.body : Bt(t) && Ae(t) ? t : Ee(ie(t)); } function Te(t, e) { var i; void 0 === e && (e = []); var n = Ee(t), s = n === (null == (i = t.ownerDocument) ? void 0 : i.body), o = $t(n), r = s ? [o].concat(o.visualViewport || [], Ae(n) ? n : []) : n, a = e.concat(r); return s ? a : a.concat(Te(ie(r))); } function Ce(t) { return Object.assign({}, t, { left: t.x, top: t.y, right: t.x + t.width, bottom: t.y + t.height, }); } function Oe(t, e, i) { return e === Et ? Ce( (function (t, e) { var i = $t(t), n = ee(t), s = i.visualViewport, o = n.clientWidth, r = n.clientHeight, a = 0, l = 0; if (s) { (o = s.width), (r = s.height); var c = Yt(); (c || (!c && "fixed" === e)) && ((a = s.offsetLeft), (l = s.offsetTop)); } return { width: o, height: r, x: a + we(t), y: l }; })(t, i) ) : Wt(e) ? (function (t, e) { var i = Ut(t, !1, "fixed" === e); return ( (i.top = i.top + t.clientTop), (i.left = i.left + t.clientLeft), (i.bottom = i.top + t.clientHeight), (i.right = i.left + t.clientWidth), (i.width = t.clientWidth), (i.height = t.clientHeight), (i.x = i.left), (i.y = i.top), i ); })(e, i) : Ce( (function (t) { var e, i = ee(t), n = ye(t), s = null == (e = t.ownerDocument) ? void 0 : e.body, o = Vt( i.scrollWidth, i.clientWidth, s ? s.scrollWidth : 0, s ? s.clientWidth : 0 ), r = Vt( i.scrollHeight, i.clientHeight, s ? s.scrollHeight : 0, s ? s.clientHeight : 0 ), a = -n.scrollLeft + we(t), l = -n.scrollTop; return ( "rtl" === Zt(s || i).direction && (a += Vt(i.clientWidth, s ? s.clientWidth : 0) - o), { width: o, height: r, x: a, y: l } ); })(ee(t)) ); } function xe(t) { var e, i = t.reference, n = t.element, s = t.placement, o = s ? qt(s) : null, r = s ? he(s) : null, a = i.x + i.width / 2 - n.width / 2, l = i.y + i.height / 2 - n.height / 2; switch (o) { case pt: e = { x: a, y: i.y - n.height }; break; case mt: e = { x: a, y: i.y + i.height }; break; case gt: e = { x: i.x + i.width, y: l }; break; case _t: e = { x: i.x - n.width, y: l }; break; default: e = { x: i.x, y: i.y }; } var c = o ? oe(o) : null; if (null != c) { var h = "y" === c ? "height" : "width"; switch (r) { case yt: e[c] = e[c] - (i[h] / 2 - n[h] / 2); break; case wt: e[c] = e[c] + (i[h] / 2 - n[h] / 2); } } return e; } function ke(t, e) { void 0 === e && (e = {}); var i = e, n = i.placement, s = void 0 === n ? t.placement : n, o = i.strategy, r = void 0 === o ? t.strategy : o, a = i.boundary, l = void 0 === a ? At : a, c = i.rootBoundary, h = void 0 === c ? Et : c, d = i.elementContext, u = void 0 === d ? Tt : d, f = i.altBoundary, p = void 0 !== f && f, m = i.padding, g = void 0 === m ? 0 : m, _ = ae("number" != typeof g ? g : le(g, vt)), b = u === Tt ? Ct : Tt, v = t.rects.popper, y = t.elements[p ? b : u], w = (function (t, e, i, n) { var s = "clippingParents" === e ? (function (t) { var e = Te(ie(t)), i = ["absolute", "fixed"].indexOf(Zt(t).position) >= 0 && Bt(t) ? se(t) : t; return Wt(i) ? e.filter(function (t) { return Wt(t) && Jt(t, i) && "body" !== Ht(t); }) : []; })(t) : [].concat(e), o = [].concat(s, [i]), r = o[0], a = o.reduce(function (e, i) { var s = Oe(t, i, n); return ( (e.top = Vt(s.top, e.top)), (e.right = Kt(s.right, e.right)), (e.bottom = Kt(s.bottom, e.bottom)), (e.left = Vt(s.left, e.left)), e ); }, Oe(t, r, n)); return ( (a.width = a.right - a.left), (a.height = a.bottom - a.top), (a.x = a.left), (a.y = a.top), a ); })(Wt(y) ? y : y.contextElement || ee(t.elements.popper), l, h, r), A = Ut(t.elements.reference), E = xe({ reference: A, element: v, strategy: "absolute", placement: s }), T = Ce(Object.assign({}, v, E)), C = u === Tt ? T : A, O = { top: w.top - C.top + _.top, bottom: C.bottom - w.bottom + _.bottom, left: w.left - C.left + _.left, right: C.right - w.right + _.right, }, x = t.modifiersData.offset; if (u === Tt && x) { var k = x[s]; Object.keys(O).forEach(function (t) { var e = [gt, mt].indexOf(t) >= 0 ? 1 : -1, i = [pt, mt].indexOf(t) >= 0 ? "y" : "x"; O[t] += k[i] * e; }); } return O; } function Le(t, e) { void 0 === e && (e = {}); var i = e, n = i.placement, s = i.boundary, o = i.rootBoundary, r = i.padding, a = i.flipVariations, l = i.allowedAutoPlacements, c = void 0 === l ? xt : l, h = he(n), d = h ? a ? Ot : Ot.filter(function (t) { return he(t) === h; }) : vt, u = d.filter(function (t) { return c.indexOf(t) >= 0; }); 0 === u.length && (u = d); var f = u.reduce(function (e, i) { return ( (e[i] = ke(t, { placement: i, boundary: s, rootBoundary: o, padding: r, })[qt(i)]), e ); }, {}); return Object.keys(f).sort(function (t, e) { return f[t] - f[e]; }); } const Se = { name: "flip", enabled: !0, phase: "main", fn: function (t) { var e = t.state, i = t.options, n = t.name; if (!e.modifiersData[n]._skip) { for ( var s = i.mainAxis, o = void 0 === s || s, r = i.altAxis, a = void 0 === r || r, l = i.fallbackPlacements, c = i.padding, h = i.boundary, d = i.rootBoundary, u = i.altBoundary, f = i.flipVariations, p = void 0 === f || f, m = i.allowedAutoPlacements, g = e.options.placement, _ = qt(g), b = l || (_ !== g && p ? (function (t) { if (qt(t) === bt) return []; var e = _e(t); return [ve(t), e, ve(e)]; })(g) : [_e(g)]), v = [g].concat(b).reduce(function (t, i) { return t.concat( qt(i) === bt ? Le(e, { placement: i, boundary: h, rootBoundary: d, padding: c, flipVariations: p, allowedAutoPlacements: m, }) : i ); }, []), y = e.rects.reference, w = e.rects.popper, A = new Map(), E = !0, T = v[0], C = 0; C < v.length; C++ ) { var O = v[C], x = qt(O), k = he(O) === yt, L = [pt, mt].indexOf(x) >= 0, S = L ? "width" : "height", D = ke(e, { placement: O, boundary: h, rootBoundary: d, altBoundary: u, padding: c, }), I = L ? (k ? gt : _t) : k ? mt : pt; y[S] > w[S] && (I = _e(I)); var N = _e(I), P = []; if ( (o && P.push(D[x] <= 0), a && P.push(D[I] <= 0, D[N] <= 0), P.every(function (t) { return t; })) ) { (T = O), (E = !1); break; } A.set(O, P); } if (E) for ( var M = function (t) { var e = v.find(function (e) { var i = A.get(e); if (i) return i.slice(0, t).every(function (t) { return t; }); }); if (e) return (T = e), "break"; }, j = p ? 3 : 1; j > 0 && "break" !== M(j); j-- ); e.placement !== T && ((e.modifiersData[n]._skip = !0), (e.placement = T), (e.reset = !0)); } }, requiresIfExists: ["offset"], data: { _skip: !1 }, }; function De(t, e, i) { return ( void 0 === i && (i = { x: 0, y: 0 }), { top: t.top - e.height - i.y, right: t.right - e.width + i.x, bottom: t.bottom - e.height + i.y, left: t.left - e.width - i.x, } ); } function Ie(t) { return [pt, gt, mt, _t].some(function (e) { return t[e] >= 0; }); } const Ne = { name: "hide", enabled: !0, phase: "main", requiresIfExists: ["preventOverflow"], fn: function (t) { var e = t.state, i = t.name, n = e.rects.reference, s = e.rects.popper, o = e.modifiersData.preventOverflow, r = ke(e, { elementContext: "reference" }), a = ke(e, { altBoundary: !0 }), l = De(r, n), c = De(a, s, o), h = Ie(l), d = Ie(c); (e.modifiersData[i] = { referenceClippingOffsets: l, popperEscapeOffsets: c, isReferenceHidden: h, hasPopperEscaped: d, }), (e.attributes.popper = Object.assign({}, e.attributes.popper, { "data-popper-reference-hidden": h, "data-popper-escaped": d, })); }, }, Pe = { name: "offset", enabled: !0, phase: "main", requires: ["popperOffsets"], fn: function (t) { var e = t.state, i = t.options, n = t.name, s = i.offset, o = void 0 === s ? [0, 0] : s, r = xt.reduce(function (t, i) { return ( (t[i] = (function (t, e, i) { var n = qt(t), s = [_t, pt].indexOf(n) >= 0 ? -1 : 1, o = "function" == typeof i ? i(Object.assign({}, e, { placement: t })) : i, r = o[0], a = o[1]; return ( (r = r || 0), (a = (a || 0) * s), [_t, gt].indexOf(n) >= 0 ? { x: a, y: r } : { x: r, y: a } ); })(i, e.rects, o)), t ); }, {}), a = r[e.placement], l = a.x, c = a.y; null != e.modifiersData.popperOffsets && ((e.modifiersData.popperOffsets.x += l), (e.modifiersData.popperOffsets.y += c)), (e.modifiersData[n] = r); }, }, Me = { name: "popperOffsets", enabled: !0, phase: "read", fn: function (t) { var e = t.state, i = t.name; e.modifiersData[i] = xe({ reference: e.rects.reference, element: e.rects.popper, strategy: "absolute", placement: e.placement, }); }, data: {}, }, je = { name: "preventOverflow", enabled: !0, phase: "main", fn: function (t) { var e = t.state, i = t.options, n = t.name, s = i.mainAxis, o = void 0 === s || s, r = i.altAxis, a = void 0 !== r && r, l = i.boundary, c = i.rootBoundary, h = i.altBoundary, d = i.padding, u = i.tether, f = void 0 === u || u, p = i.tetherOffset, m = void 0 === p ? 0 : p, g = ke(e, { boundary: l, rootBoundary: c, padding: d, altBoundary: h, }), _ = qt(e.placement), b = he(e.placement), v = !b, y = oe(_), w = "x" === y ? "y" : "x", A = e.modifiersData.popperOffsets, E = e.rects.reference, T = e.rects.popper, C = "function" == typeof m ? m(Object.assign({}, e.rects, { placement: e.placement })) : m, O = "number" == typeof C ? { mainAxis: C, altAxis: C } : Object.assign({ mainAxis: 0, altAxis: 0 }, C), x = e.modifiersData.offset ? e.modifiersData.offset[e.placement] : null, k = { x: 0, y: 0 }; if (A) { if (o) { var L, S = "y" === y ? pt : _t, D = "y" === y ? mt : gt, I = "y" === y ? "height" : "width", N = A[y], P = N + g[S], M = N - g[D], j = f ? -T[I] / 2 : 0, F = b === yt ? E[I] : T[I], H = b === yt ? -T[I] : -E[I], $ = e.elements.arrow, W = f && $ ? Gt($) : { width: 0, height: 0 }, B = e.modifiersData["arrow#persistent"] ? e.modifiersData["arrow#persistent"].padding : { top: 0, right: 0, bottom: 0, left: 0 }, z = B[S], R = B[D], q = re(0, E[I], W[I]), V = v ? E[I] / 2 - j - q - z - O.mainAxis : F - q - z - O.mainAxis, K = v ? -E[I] / 2 + j + q + R + O.mainAxis : H + q + R + O.mainAxis, Q = e.elements.arrow && se(e.elements.arrow), X = Q ? ("y" === y ? Q.clientTop || 0 : Q.clientLeft || 0) : 0, Y = null != (L = null == x ? void 0 : x[y]) ? L : 0, U = N + K - Y, G = re(f ? Kt(P, N + V - Y - X) : P, N, f ? Vt(M, U) : M); (A[y] = G), (k[y] = G - N); } if (a) { var J, Z = "x" === y ? pt : _t, tt = "x" === y ? mt : gt, et = A[w], it = "y" === w ? "height" : "width", nt = et + g[Z], st = et - g[tt], ot = -1 !== [pt, _t].indexOf(_), rt = null != (J = null == x ? void 0 : x[w]) ? J : 0, at = ot ? nt : et - E[it] - T[it] - rt + O.altAxis, lt = ot ? et + E[it] + T[it] - rt - O.altAxis : st, ct = f && ot ? (function (t, e, i) { var n = re(t, e, i); return n > i ? i : n; })(at, et, lt) : re(f ? at : nt, et, f ? lt : st); (A[w] = ct), (k[w] = ct - et); } e.modifiersData[n] = k; } }, requiresIfExists: ["offset"], }; function Fe(t, e, i) { void 0 === i && (i = !1); var n, s, o = Bt(e), r = Bt(e) && (function (t) { var e = t.getBoundingClientRect(), i = Qt(e.width) / t.offsetWidth || 1, n = Qt(e.height) / t.offsetHeight || 1; return 1 !== i || 1 !== n; })(e), a = ee(e), l = Ut(t, r, i), c = { scrollLeft: 0, scrollTop: 0 }, h = { x: 0, y: 0 }; return ( (o || (!o && !i)) && (("body" !== Ht(e) || Ae(a)) && (c = (n = e) !== $t(n) && Bt(n) ? { scrollLeft: (s = n).scrollLeft, scrollTop: s.scrollTop } : ye(n)), Bt(e) ? (((h = Ut(e, !0)).x += e.clientLeft), (h.y += e.clientTop)) : a && (h.x = we(a))), { x: l.left + c.scrollLeft - h.x, y: l.top + c.scrollTop - h.y, width: l.width, height: l.height, } ); } function He(t) { var e = new Map(), i = new Set(), n = []; function s(t) { i.add(t.name), [] .concat(t.requires || [], t.requiresIfExists || []) .forEach(function (t) { if (!i.has(t)) { var n = e.get(t); n && s(n); } }), n.push(t); } return ( t.forEach(function (t) { e.set(t.name, t); }), t.forEach(function (t) { i.has(t.name) || s(t); }), n ); } var $e = { placement: "bottom", modifiers: [], strategy: "absolute" }; function We() { for (var t = arguments.length, e = new Array(t), i = 0; i < t; i++) e[i] = arguments[i]; return !e.some(function (t) { return !(t && "function" == typeof t.getBoundingClientRect); }); } function Be(t) { void 0 === t && (t = {}); var e = t, i = e.defaultModifiers, n = void 0 === i ? [] : i, s = e.defaultOptions, o = void 0 === s ? $e : s; return function (t, e, i) { void 0 === i && (i = o); var s, r, a = { placement: "bottom", orderedModifiers: [], options: Object.assign({}, $e, o), modifiersData: {}, elements: { reference: t, popper: e }, attributes: {}, styles: {}, }, l = [], c = !1, h = { state: a, setOptions: function (i) { var s = "function" == typeof i ? i(a.options) : i; d(), (a.options = Object.assign({}, o, a.options, s)), (a.scrollParents = { reference: Wt(t) ? Te(t) : t.contextElement ? Te(t.contextElement) : [], popper: Te(e), }); var r, c, u = (function (t) { var e = He(t); return Ft.reduce(function (t, i) { return t.concat( e.filter(function (t) { return t.phase === i; }) ); }, []); })( ((r = [].concat(n, a.options.modifiers)), (c = r.reduce(function (t, e) { var i = t[e.name]; return ( (t[e.name] = i ? Object.assign({}, i, e, { options: Object.assign({}, i.options, e.options), data: Object.assign({}, i.data, e.data), }) : e), t ); }, {})), Object.keys(c).map(function (t) { return c[t]; })) ); return ( (a.orderedModifiers = u.filter(function (t) { return t.enabled; })), a.orderedModifiers.forEach(function (t) { var e = t.name, i = t.options, n = void 0 === i ? {} : i, s = t.effect; if ("function" == typeof s) { var o = s({ state: a, name: e, instance: h, options: n }); l.push(o || function () {}); } }), h.update() ); }, forceUpdate: function () { if (!c) { var t = a.elements, e = t.reference, i = t.popper; if (We(e, i)) { (a.rects = { reference: Fe(e, se(i), "fixed" === a.options.strategy), popper: Gt(i), }), (a.reset = !1), (a.placement = a.options.placement), a.orderedModifiers.forEach(function (t) { return (a.modifiersData[t.name] = Object.assign( {}, t.data )); }); for (var n = 0; n < a.orderedModifiers.length; n++) if (!0 !== a.reset) { var s = a.orderedModifiers[n], o = s.fn, r = s.options, l = void 0 === r ? {} : r, d = s.name; "function" == typeof o && (a = o({ state: a, options: l, name: d, instance: h }) || a); } else (a.reset = !1), (n = -1); } } }, update: ((s = function () { return new Promise(function (t) { h.forceUpdate(), t(a); }); }), function () { return ( r || (r = new Promise(function (t) { Promise.resolve().then(function () { (r = void 0), t(s()); }); })), r ); }), destroy: function () { d(), (c = !0); }, }; if (!We(t, e)) return h; function d() { l.forEach(function (t) { return t(); }), (l = []); } return ( h.setOptions(i).then(function (t) { !c && i.onFirstUpdate && i.onFirstUpdate(t); }), h ); }; } var ze = Be(), Re = Be({ defaultModifiers: [me, Me, fe, Rt] }), qe = Be({ defaultModifiers: [me, Me, fe, Rt, Pe, Se, je, ce, Ne] }); const Ve = Object.freeze( Object.defineProperty( { __proto__: null, afterMain: Nt, afterRead: St, afterWrite: jt, applyStyles: Rt, arrow: ce, auto: bt, basePlacements: vt, beforeMain: Dt, beforeRead: kt, beforeWrite: Pt, bottom: mt, clippingParents: At, computeStyles: fe, createPopper: qe, createPopperBase: ze, createPopperLite: Re, detectOverflow: ke, end: wt, eventListeners: me, flip: Se, hide: Ne, left: _t, main: It, modifierPhases: Ft, offset: Pe, placements: xt, popper: Tt, popperGenerator: Be, popperOffsets: Me, preventOverflow: je, read: Lt, reference: Ct, right: gt, start: yt, top: pt, variationPlacements: Ot, viewport: Et, write: Mt, }, Symbol.toStringTag, { value: "Module" } ) ), Ke = "dropdown", Qe = "ArrowUp", Xe = "ArrowDown", Ye = "click.bs.dropdown.data-api", Ue = "keydown.bs.dropdown.data-api", Ge = "show", Je = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)', Ze = `${Je}.show`, ti = ".dropdown-menu", ei = p() ? "top-end" : "top-start", ii = p() ? "top-start" : "top-end", ni = p() ? "bottom-end" : "bottom-start", si = p() ? "bottom-start" : "bottom-end", oi = p() ? "left-start" : "right-start", ri = p() ? "right-start" : "left-start", ai = { autoClose: !0, boundary: "clippingParents", display: "dynamic", offset: [0, 2], popperConfig: null, reference: "toggle", }, li = { autoClose: "(boolean|string)", boundary: "(string|element)", display: "string", offset: "(array|string|function)", popperConfig: "(null|object|function)", reference: "(string|element|object)", }; class ci extends W { constructor(t, e) { super(t, e), (this._popper = null), (this._parent = this._element.parentNode), (this._menu = z.next(this._element, ti)[0] || z.prev(this._element, ti)[0] || z.findOne(ti, this._parent)), (this._inNavbar = this._detectNavbar()); } static get Default() { return ai; } static get DefaultType() { return li; } static get NAME() { return Ke; } toggle() { return this._isShown() ? this.hide() : this.show(); } show() { if (l(this._element) || this._isShown()) return; const t = { relatedTarget: this._element }; if (!P.trigger(this._element, "show.bs.dropdown", t).defaultPrevented) { if ( (this._createPopper(), "ontouchstart" in document.documentElement && !this._parent.closest(".navbar-nav")) ) for (const t of [].concat(...document.body.children)) P.on(t, "mouseover", h); this._element.focus(), this._element.setAttribute("aria-expanded", !0), this._menu.classList.add(Ge), this._element.classList.add(Ge), P.trigger(this._element, "shown.bs.dropdown", t); } } hide() { if (l(this._element) || !this._isShown()) return; const t = { relatedTarget: this._element }; this._completeHide(t); } dispose() { this._popper && this._popper.destroy(), super.dispose(); } update() { (this._inNavbar = this._detectNavbar()), this._popper && this._popper.update(); } _completeHide(t) { if (!P.trigger(this._element, "hide.bs.dropdown", t).defaultPrevented) { if ("ontouchstart" in document.documentElement) for (const t of [].concat(...document.body.children)) P.off(t, "mouseover", h); this._popper && this._popper.destroy(), this._menu.classList.remove(Ge), this._element.classList.remove(Ge), this._element.setAttribute("aria-expanded", "false"), H.removeDataAttribute(this._menu, "popper"), P.trigger(this._element, "hidden.bs.dropdown", t); } } _getConfig(t) { if ( "object" == typeof (t = super._getConfig(t)).reference && !o(t.reference) && "function" != typeof t.reference.getBoundingClientRect ) throw new TypeError( `${Ke.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.` ); return t; } _createPopper() { if (void 0 === Ve) throw new TypeError( "Bootstrap's dropdowns require Popper (https://popper.js.org)" ); let t = this._element; "parent" === this._config.reference ? (t = this._parent) : o(this._config.reference) ? (t = r(this._config.reference)) : "object" == typeof this._config.reference && (t = this._config.reference); const e = this._getPopperConfig(); this._popper = qe(t, this._menu, e); } _isShown() { return this._menu.classList.contains(Ge); } _getPlacement() { const t = this._parent; if (t.classList.contains("dropend")) return oi; if (t.classList.contains("dropstart")) return ri; if (t.classList.contains("dropup-center")) return "top"; if (t.classList.contains("dropdown-center")) return "bottom"; const e = "end" === getComputedStyle(this._menu).getPropertyValue("--bs-position").trim(); return t.classList.contains("dropup") ? (e ? ii : ei) : e ? si : ni; } _detectNavbar() { return null !== this._element.closest(".navbar"); } _getOffset() { const { offset: t } = this._config; return "string" == typeof t ? t.split(",").map((t) => Number.parseInt(t, 10)) : "function" == typeof t ? (e) => t(e, this._element) : t; } _getPopperConfig() { const t = { placement: this._getPlacement(), modifiers: [ { name: "preventOverflow", options: { boundary: this._config.boundary }, }, { name: "offset", options: { offset: this._getOffset() } }, ], }; return ( (this._inNavbar || "static" === this._config.display) && (H.setDataAttribute(this._menu, "popper", "static"), (t.modifiers = [{ name: "applyStyles", enabled: !1 }])), { ...t, ...g(this._config.popperConfig, [t]) } ); } _selectMenuItem({ key: t, target: e }) { const i = z .find( ".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)", this._menu ) .filter((t) => a(t)); i.length && b(i, e, t === Xe, !i.includes(e)).focus(); } static jQueryInterface(t) { return this.each(function () { const e = ci.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t]) throw new TypeError(`No method named "${t}"`); e[t](); } }); } static clearMenus(t) { if (2 === t.button || ("keyup" === t.type && "Tab" !== t.key)) return; const e = z.find(Ze); for (const i of e) { const e = ci.getInstance(i); if (!e || !1 === e._config.autoClose) continue; const n = t.composedPath(), s = n.includes(e._menu); if ( n.includes(e._element) || ("inside" === e._config.autoClose && !s) || ("outside" === e._config.autoClose && s) ) continue; if ( e._menu.contains(t.target) && (("keyup" === t.type && "Tab" === t.key) || /input|select|option|textarea|form/i.test(t.target.tagName)) ) continue; const o = { relatedTarget: e._element }; "click" === t.type && (o.clickEvent = t), e._completeHide(o); } } static dataApiKeydownHandler(t) { const e = /input|textarea/i.test(t.target.tagName), i = "Escape" === t.key, n = [Qe, Xe].includes(t.key); if (!n && !i) return; if (e && !i) return; t.preventDefault(); const s = this.matches(Je) ? this : z.prev(this, Je)[0] || z.next(this, Je)[0] || z.findOne(Je, t.delegateTarget.parentNode), o = ci.getOrCreateInstance(s); if (n) return t.stopPropagation(), o.show(), void o._selectMenuItem(t); o._isShown() && (t.stopPropagation(), o.hide(), s.focus()); } } P.on(document, Ue, Je, ci.dataApiKeydownHandler), P.on(document, Ue, ti, ci.dataApiKeydownHandler), P.on(document, Ye, ci.clearMenus), P.on(document, "keyup.bs.dropdown.data-api", ci.clearMenus), P.on(document, Ye, Je, function (t) { t.preventDefault(), ci.getOrCreateInstance(this).toggle(); }), m(ci); const hi = "show", di = "mousedown.bs.backdrop", ui = { className: "modal-backdrop", clickCallback: null, isAnimated: !1, isVisible: !0, rootElement: "body", }, fi = { className: "string", clickCallback: "(function|null)", isAnimated: "boolean", isVisible: "boolean", rootElement: "(element|string)", }; class pi extends $ { constructor(t) { super(), (this._config = this._getConfig(t)), (this._isAppended = !1), (this._element = null); } static get Default() { return ui; } static get DefaultType() { return fi; } static get NAME() { return "backdrop"; } show(t) { if (!this._config.isVisible) return void g(t); this._append(); const e = this._getElement(); this._config.isAnimated && d(e), e.classList.add(hi), this._emulateAnimation(() => { g(t); }); } hide(t) { this._config.isVisible ? (this._getElement().classList.remove(hi), this._emulateAnimation(() => { this.dispose(), g(t); })) : g(t); } dispose() { this._isAppended && (P.off(this._element, di), this._element.remove(), (this._isAppended = !1)); } _getElement() { if (!this._element) { const t = document.createElement("div"); (t.className = this._config.className), this._config.isAnimated && t.classList.add("fade"), (this._element = t); } return this._element; } _configAfterMerge(t) { return (t.rootElement = r(t.rootElement)), t; } _append() { if (this._isAppended) return; const t = this._getElement(); this._config.rootElement.append(t), P.on(t, di, () => { g(this._config.clickCallback); }), (this._isAppended = !0); } _emulateAnimation(t) { _(t, this._getElement(), this._config.isAnimated); } } const mi = ".bs.focustrap", gi = "backward", _i = { autofocus: !0, trapElement: null }, bi = { autofocus: "boolean", trapElement: "element" }; class vi extends $ { constructor(t) { super(), (this._config = this._getConfig(t)), (this._isActive = !1), (this._lastTabNavDirection = null); } static get Default() { return _i; } static get DefaultType() { return bi; } static get NAME() { return "focustrap"; } activate() { this._isActive || (this._config.autofocus && this._config.trapElement.focus(), P.off(document, mi), P.on(document, "focusin.bs.focustrap", (t) => this._handleFocusin(t)), P.on(document, "keydown.tab.bs.focustrap", (t) => this._handleKeydown(t) ), (this._isActive = !0)); } deactivate() { this._isActive && ((this._isActive = !1), P.off(document, mi)); } _handleFocusin(t) { const { trapElement: e } = this._config; if (t.target === document || t.target === e || e.contains(t.target)) return; const i = z.focusableChildren(e); 0 === i.length ? e.focus() : this._lastTabNavDirection === gi ? i[i.length - 1].focus() : i[0].focus(); } _handleKeydown(t) { "Tab" === t.key && (this._lastTabNavDirection = t.shiftKey ? gi : "forward"); } } const yi = ".fixed-top, .fixed-bottom, .is-fixed, .sticky-top", wi = ".sticky-top", Ai = "padding-right", Ei = "margin-right"; class Ti { constructor() { this._element = document.body; } getWidth() { const t = document.documentElement.clientWidth; return Math.abs(window.innerWidth - t); } hide() { const t = this.getWidth(); this._disableOverFlow(), this._setElementAttributes(this._element, Ai, (e) => e + t), this._setElementAttributes(yi, Ai, (e) => e + t), this._setElementAttributes(wi, Ei, (e) => e - t); } reset() { this._resetElementAttributes(this._element, "overflow"), this._resetElementAttributes(this._element, Ai), this._resetElementAttributes(yi, Ai), this._resetElementAttributes(wi, Ei); } isOverflowing() { return this.getWidth() > 0; } _disableOverFlow() { this._saveInitialAttribute(this._element, "overflow"), (this._element.style.overflow = "hidden"); } _setElementAttributes(t, e, i) { const n = this.getWidth(); this._applyManipulationCallback(t, (t) => { if (t !== this._element && window.innerWidth > t.clientWidth + n) return; this._saveInitialAttribute(t, e); const s = window.getComputedStyle(t).getPropertyValue(e); t.style.setProperty(e, `${i(Number.parseFloat(s))}px`); }); } _saveInitialAttribute(t, e) { const i = t.style.getPropertyValue(e); i && H.setDataAttribute(t, e, i); } _resetElementAttributes(t, e) { this._applyManipulationCallback(t, (t) => { const i = H.getDataAttribute(t, e); null !== i ? (H.removeDataAttribute(t, e), t.style.setProperty(e, i)) : t.style.removeProperty(e); }); } _applyManipulationCallback(t, e) { if (o(t)) e(t); else for (const i of z.find(t, this._element)) e(i); } } const Ci = ".bs.modal", Oi = "hidden.bs.modal", xi = "show.bs.modal", ki = "modal-open", Li = "show", Si = "modal-static", Di = { backdrop: !0, focus: !0, keyboard: !0 }, Ii = { backdrop: "(boolean|string)", focus: "boolean", keyboard: "boolean", }; class Ni extends W { constructor(t, e) { super(t, e), (this._dialog = z.findOne(".modal-dialog", this._element)), (this._backdrop = this._initializeBackDrop()), (this._focustrap = this._initializeFocusTrap()), (this._isShown = !1), (this._isTransitioning = !1), (this._scrollBar = new Ti()), this._addEventListeners(); } static get Default() { return Di; } static get DefaultType() { return Ii; } static get NAME() { return "modal"; } toggle(t) { return this._isShown ? this.hide() : this.show(t); } show(t) { this._isShown || this._isTransitioning || P.trigger(this._element, xi, { relatedTarget: t }).defaultPrevented || ((this._isShown = !0), (this._isTransitioning = !0), this._scrollBar.hide(), document.body.classList.add(ki), this._adjustDialog(), this._backdrop.show(() => this._showElement(t))); } hide() { this._isShown && !this._isTransitioning && (P.trigger(this._element, "hide.bs.modal").defaultPrevented || ((this._isShown = !1), (this._isTransitioning = !0), this._focustrap.deactivate(), this._element.classList.remove(Li), this._queueCallback( () => this._hideModal(), this._element, this._isAnimated() ))); } dispose() { P.off(window, Ci), P.off(this._dialog, Ci), this._backdrop.dispose(), this._focustrap.deactivate(), super.dispose(); } handleUpdate() { this._adjustDialog(); } _initializeBackDrop() { return new pi({ isVisible: Boolean(this._config.backdrop), isAnimated: this._isAnimated(), }); } _initializeFocusTrap() { return new vi({ trapElement: this._element }); } _showElement(t) { document.body.contains(this._element) || document.body.append(this._element), (this._element.style.display = "block"), this._element.removeAttribute("aria-hidden"), this._element.setAttribute("aria-modal", !0), this._element.setAttribute("role", "dialog"), (this._element.scrollTop = 0); const e = z.findOne(".modal-body", this._dialog); e && (e.scrollTop = 0), d(this._element), this._element.classList.add(Li), this._queueCallback( () => { this._config.focus && this._focustrap.activate(), (this._isTransitioning = !1), P.trigger(this._element, "shown.bs.modal", { relatedTarget: t }); }, this._dialog, this._isAnimated() ); } _addEventListeners() { P.on(this._element, "keydown.dismiss.bs.modal", (t) => { "Escape" === t.key && (this._config.keyboard ? this.hide() : this._triggerBackdropTransition()); }), P.on(window, "resize.bs.modal", () => { this._isShown && !this._isTransitioning && this._adjustDialog(); }), P.on(this._element, "mousedown.dismiss.bs.modal", (t) => { P.one(this._element, "click.dismiss.bs.modal", (e) => { this._element === t.target && this._element === e.target && ("static" !== this._config.backdrop ? this._config.backdrop && this.hide() : this._triggerBackdropTransition()); }); }); } _hideModal() { (this._element.style.display = "none"), this._element.setAttribute("aria-hidden", !0), this._element.removeAttribute("aria-modal"), this._element.removeAttribute("role"), (this._isTransitioning = !1), this._backdrop.hide(() => { document.body.classList.remove(ki), this._resetAdjustments(), this._scrollBar.reset(), P.trigger(this._element, Oi); }); } _isAnimated() { return this._element.classList.contains("fade"); } _triggerBackdropTransition() { if (P.trigger(this._element, "hidePrevented.bs.modal").defaultPrevented) return; const t = this._element.scrollHeight > document.documentElement.clientHeight, e = this._element.style.overflowY; "hidden" === e || this._element.classList.contains(Si) || (t || (this._element.style.overflowY = "hidden"), this._element.classList.add(Si), this._queueCallback(() => { this._element.classList.remove(Si), this._queueCallback(() => { this._element.style.overflowY = e; }, this._dialog); }, this._dialog), this._element.focus()); } _adjustDialog() { const t = this._element.scrollHeight > document.documentElement.clientHeight, e = this._scrollBar.getWidth(), i = e > 0; if (i && !t) { const t = p() ? "paddingLeft" : "paddingRight"; this._element.style[t] = `${e}px`; } if (!i && t) { const t = p() ? "paddingRight" : "paddingLeft"; this._element.style[t] = `${e}px`; } } _resetAdjustments() { (this._element.style.paddingLeft = ""), (this._element.style.paddingRight = ""); } static jQueryInterface(t, e) { return this.each(function () { const i = Ni.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === i[t]) throw new TypeError(`No method named "${t}"`); i[t](e); } }); } } P.on( document, "click.bs.modal.data-api", '[data-bs-toggle="modal"]', function (t) { const e = z.getElementFromSelector(this); ["A", "AREA"].includes(this.tagName) && t.preventDefault(), P.one(e, xi, (t) => { t.defaultPrevented || P.one(e, Oi, () => { a(this) && this.focus(); }); }); const i = z.findOne(".modal.show"); i && Ni.getInstance(i).hide(), Ni.getOrCreateInstance(e).toggle(this); } ), R(Ni), m(Ni); const Pi = "show", Mi = "showing", ji = "hiding", Fi = ".offcanvas.show", Hi = "hidePrevented.bs.offcanvas", $i = "hidden.bs.offcanvas", Wi = { backdrop: !0, keyboard: !0, scroll: !1 }, Bi = { backdrop: "(boolean|string)", keyboard: "boolean", scroll: "boolean", }; class zi extends W { constructor(t, e) { super(t, e), (this._isShown = !1), (this._backdrop = this._initializeBackDrop()), (this._focustrap = this._initializeFocusTrap()), this._addEventListeners(); } static get Default() { return Wi; } static get DefaultType() { return Bi; } static get NAME() { return "offcanvas"; } toggle(t) { return this._isShown ? this.hide() : this.show(t); } show(t) { this._isShown || P.trigger(this._element, "show.bs.offcanvas", { relatedTarget: t }) .defaultPrevented || ((this._isShown = !0), this._backdrop.show(), this._config.scroll || new Ti().hide(), this._element.setAttribute("aria-modal", !0), this._element.setAttribute("role", "dialog"), this._element.classList.add(Mi), this._queueCallback( () => { (this._config.scroll && !this._config.backdrop) || this._focustrap.activate(), this._element.classList.add(Pi), this._element.classList.remove(Mi), P.trigger(this._element, "shown.bs.offcanvas", { relatedTarget: t, }); }, this._element, !0 )); } hide() { this._isShown && (P.trigger(this._element, "hide.bs.offcanvas").defaultPrevented || (this._focustrap.deactivate(), this._element.blur(), (this._isShown = !1), this._element.classList.add(ji), this._backdrop.hide(), this._queueCallback( () => { this._element.classList.remove(Pi, ji), this._element.removeAttribute("aria-modal"), this._element.removeAttribute("role"), this._config.scroll || new Ti().reset(), P.trigger(this._element, $i); }, this._element, !0 ))); } dispose() { this._backdrop.dispose(), this._focustrap.deactivate(), super.dispose(); } _initializeBackDrop() { const t = Boolean(this._config.backdrop); return new pi({ className: "offcanvas-backdrop", isVisible: t, isAnimated: !0, rootElement: this._element.parentNode, clickCallback: t ? () => { "static" !== this._config.backdrop ? this.hide() : P.trigger(this._element, Hi); } : null, }); } _initializeFocusTrap() { return new vi({ trapElement: this._element }); } _addEventListeners() { P.on(this._element, "keydown.dismiss.bs.offcanvas", (t) => { "Escape" === t.key && (this._config.keyboard ? this.hide() : P.trigger(this._element, Hi)); }); } static jQueryInterface(t) { return this.each(function () { const e = zi.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t] || t.startsWith("_") || "constructor" === t) throw new TypeError(`No method named "${t}"`); e[t](this); } }); } } P.on( document, "click.bs.offcanvas.data-api", '[data-bs-toggle="offcanvas"]', function (t) { const e = z.getElementFromSelector(this); if ((["A", "AREA"].includes(this.tagName) && t.preventDefault(), l(this))) return; P.one(e, $i, () => { a(this) && this.focus(); }); const i = z.findOne(Fi); i && i !== e && zi.getInstance(i).hide(), zi.getOrCreateInstance(e).toggle(this); } ), P.on(window, "load.bs.offcanvas.data-api", () => { for (const t of z.find(Fi)) zi.getOrCreateInstance(t).show(); }), P.on(window, "resize.bs.offcanvas", () => { for (const t of z.find("[aria-modal][class*=show][class*=offcanvas-]")) "fixed" !== getComputedStyle(t).position && zi.getOrCreateInstance(t).hide(); }), R(zi), m(zi); const Ri = { "*": ["class", "dir", "id", "lang", "role", /^aria-[\w-]*$/i], a: ["target", "href", "title", "rel"], area: [], b: [], br: [], col: [], code: [], div: [], em: [], hr: [], h1: [], h2: [], h3: [], h4: [], h5: [], h6: [], i: [], img: ["src", "srcset", "alt", "title", "width", "height"], li: [], ol: [], p: [], pre: [], s: [], small: [], span: [], sub: [], sup: [], strong: [], u: [], ul: [], }, qi = new Set([ "background", "cite", "href", "itemtype", "longdesc", "poster", "src", "xlink:href", ]), Vi = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i, Ki = (t, e) => { const i = t.nodeName.toLowerCase(); return e.includes(i) ? !qi.has(i) || Boolean(Vi.test(t.nodeValue)) : e.filter((t) => t instanceof RegExp).some((t) => t.test(i)); }, Qi = { allowList: Ri, content: {}, extraClass: "", html: !1, sanitize: !0, sanitizeFn: null, template: "
", }, Xi = { allowList: "object", content: "object", extraClass: "(string|function)", html: "boolean", sanitize: "boolean", sanitizeFn: "(null|function)", template: "string", }, Yi = { entry: "(string|element|function|null)", selector: "(string|element)", }; class Ui extends $ { constructor(t) { super(), (this._config = this._getConfig(t)); } static get Default() { return Qi; } static get DefaultType() { return Xi; } static get NAME() { return "TemplateFactory"; } getContent() { return Object.values(this._config.content) .map((t) => this._resolvePossibleFunction(t)) .filter(Boolean); } hasContent() { return this.getContent().length > 0; } changeContent(t) { return ( this._checkContent(t), (this._config.content = { ...this._config.content, ...t }), this ); } toHtml() { const t = document.createElement("div"); t.innerHTML = this._maybeSanitize(this._config.template); for (const [e, i] of Object.entries(this._config.content)) this._setContent(t, i, e); const e = t.children[0], i = this._resolvePossibleFunction(this._config.extraClass); return i && e.classList.add(...i.split(" ")), e; } _typeCheckConfig(t) { super._typeCheckConfig(t), this._checkContent(t.content); } _checkContent(t) { for (const [e, i] of Object.entries(t)) super._typeCheckConfig({ selector: e, entry: i }, Yi); } _setContent(t, e, i) { const n = z.findOne(i, t); n && ((e = this._resolvePossibleFunction(e)) ? o(e) ? this._putElementInTemplate(r(e), n) : this._config.html ? (n.innerHTML = this._maybeSanitize(e)) : (n.textContent = e) : n.remove()); } _maybeSanitize(t) { return this._config.sanitize ? (function (t, e, i) { if (!t.length) return t; if (i && "function" == typeof i) return i(t); const n = new window.DOMParser().parseFromString(t, "text/html"), s = [].concat(...n.body.querySelectorAll("*")); for (const t of s) { const i = t.nodeName.toLowerCase(); if (!Object.keys(e).includes(i)) { t.remove(); continue; } const n = [].concat(...t.attributes), s = [].concat(e["*"] || [], e[i] || []); for (const e of n) Ki(e, s) || t.removeAttribute(e.nodeName); } return n.body.innerHTML; })(t, this._config.allowList, this._config.sanitizeFn) : t; } _resolvePossibleFunction(t) { return g(t, [this]); } _putElementInTemplate(t, e) { if (this._config.html) return (e.innerHTML = ""), void e.append(t); e.textContent = t.textContent; } } const Gi = new Set(["sanitize", "allowList", "sanitizeFn"]), Ji = "fade", Zi = "show", tn = ".modal", en = "hide.bs.modal", nn = "hover", sn = "focus", on = { AUTO: "auto", TOP: "top", RIGHT: p() ? "left" : "right", BOTTOM: "bottom", LEFT: p() ? "right" : "left", }, rn = { allowList: Ri, animation: !0, boundary: "clippingParents", container: !1, customClass: "", delay: 0, fallbackPlacements: ["top", "right", "bottom", "left"], html: !1, offset: [0, 6], placement: "top", popperConfig: null, sanitize: !0, sanitizeFn: null, selector: !1, template: '', title: "", trigger: "hover focus", }, an = { allowList: "object", animation: "boolean", boundary: "(string|element)", container: "(string|element|boolean)", customClass: "(string|function)", delay: "(number|object)", fallbackPlacements: "array", html: "boolean", offset: "(array|string|function)", placement: "(string|function)", popperConfig: "(null|object|function)", sanitize: "boolean", sanitizeFn: "(null|function)", selector: "(string|boolean)", template: "string", title: "(string|element|function)", trigger: "string", }; class ln extends W { constructor(t, e) { if (void 0 === Ve) throw new TypeError( "Bootstrap's tooltips require Popper (https://popper.js.org)" ); super(t, e), (this._isEnabled = !0), (this._timeout = 0), (this._isHovered = null), (this._activeTrigger = {}), (this._popper = null), (this._templateFactory = null), (this._newContent = null), (this.tip = null), this._setListeners(), this._config.selector || this._fixTitle(); } static get Default() { return rn; } static get DefaultType() { return an; } static get NAME() { return "tooltip"; } enable() { this._isEnabled = !0; } disable() { this._isEnabled = !1; } toggleEnabled() { this._isEnabled = !this._isEnabled; } toggle() { this._isEnabled && ((this._activeTrigger.click = !this._activeTrigger.click), this._isShown() ? this._leave() : this._enter()); } dispose() { clearTimeout(this._timeout), P.off(this._element.closest(tn), en, this._hideModalHandler), this._element.getAttribute("data-bs-original-title") && this._element.setAttribute( "title", this._element.getAttribute("data-bs-original-title") ), this._disposePopper(), super.dispose(); } show() { if ("none" === this._element.style.display) throw new Error("Please use show on visible elements"); if (!this._isWithContent() || !this._isEnabled) return; const t = P.trigger(this._element, this.constructor.eventName("show")), e = ( c(this._element) || this._element.ownerDocument.documentElement ).contains(this._element); if (t.defaultPrevented || !e) return; this._disposePopper(); const i = this._getTipElement(); this._element.setAttribute("aria-describedby", i.getAttribute("id")); const { container: n } = this._config; if ( (this._element.ownerDocument.documentElement.contains(this.tip) || (n.append(i), P.trigger(this._element, this.constructor.eventName("inserted"))), (this._popper = this._createPopper(i)), i.classList.add(Zi), "ontouchstart" in document.documentElement) ) for (const t of [].concat(...document.body.children)) P.on(t, "mouseover", h); this._queueCallback( () => { P.trigger(this._element, this.constructor.eventName("shown")), !1 === this._isHovered && this._leave(), (this._isHovered = !1); }, this.tip, this._isAnimated() ); } hide() { if ( this._isShown() && !P.trigger(this._element, this.constructor.eventName("hide")) .defaultPrevented ) { if ( (this._getTipElement().classList.remove(Zi), "ontouchstart" in document.documentElement) ) for (const t of [].concat(...document.body.children)) P.off(t, "mouseover", h); (this._activeTrigger.click = !1), (this._activeTrigger.focus = !1), (this._activeTrigger.hover = !1), (this._isHovered = null), this._queueCallback( () => { this._isWithActiveTrigger() || (this._isHovered || this._disposePopper(), this._element.removeAttribute("aria-describedby"), P.trigger(this._element, this.constructor.eventName("hidden"))); }, this.tip, this._isAnimated() ); } } update() { this._popper && this._popper.update(); } _isWithContent() { return Boolean(this._getTitle()); } _getTipElement() { return ( this.tip || (this.tip = this._createTipElement( this._newContent || this._getContentForTemplate() )), this.tip ); } _createTipElement(t) { const e = this._getTemplateFactory(t).toHtml(); if (!e) return null; e.classList.remove(Ji, Zi), e.classList.add(`bs-${this.constructor.NAME}-auto`); const i = ((t) => { do { t += Math.floor(1e6 * Math.random()); } while (document.getElementById(t)); return t; })(this.constructor.NAME).toString(); return ( e.setAttribute("id", i), this._isAnimated() && e.classList.add(Ji), e ); } setContent(t) { (this._newContent = t), this._isShown() && (this._disposePopper(), this.show()); } _getTemplateFactory(t) { return ( this._templateFactory ? this._templateFactory.changeContent(t) : (this._templateFactory = new Ui({ ...this._config, content: t, extraClass: this._resolvePossibleFunction( this._config.customClass ), })), this._templateFactory ); } _getContentForTemplate() { return { ".tooltip-inner": this._getTitle() }; } _getTitle() { return ( this._resolvePossibleFunction(this._config.title) || this._element.getAttribute("data-bs-original-title") ); } _initializeOnDelegatedTarget(t) { return this.constructor.getOrCreateInstance( t.delegateTarget, this._getDelegateConfig() ); } _isAnimated() { return ( this._config.animation || (this.tip && this.tip.classList.contains(Ji)) ); } _isShown() { return this.tip && this.tip.classList.contains(Zi); } _createPopper(t) { const e = g(this._config.placement, [this, t, this._element]), i = on[e.toUpperCase()]; return qe(this._element, t, this._getPopperConfig(i)); } _getOffset() { const { offset: t } = this._config; return "string" == typeof t ? t.split(",").map((t) => Number.parseInt(t, 10)) : "function" == typeof t ? (e) => t(e, this._element) : t; } _resolvePossibleFunction(t) { return g(t, [this._element]); } _getPopperConfig(t) { const e = { placement: t, modifiers: [ { name: "flip", options: { fallbackPlacements: this._config.fallbackPlacements }, }, { name: "offset", options: { offset: this._getOffset() } }, { name: "preventOverflow", options: { boundary: this._config.boundary }, }, { name: "arrow", options: { element: `.${this.constructor.NAME}-arrow` }, }, { name: "preSetPlacement", enabled: !0, phase: "beforeMain", fn: (t) => { this._getTipElement().setAttribute( "data-popper-placement", t.state.placement ); }, }, ], }; return { ...e, ...g(this._config.popperConfig, [e]) }; } _setListeners() { const t = this._config.trigger.split(" "); for (const e of t) if ("click" === e) P.on( this._element, this.constructor.eventName("click"), this._config.selector, (t) => { this._initializeOnDelegatedTarget(t).toggle(); } ); else if ("manual" !== e) { const t = e === nn ? this.constructor.eventName("mouseenter") : this.constructor.eventName("focusin"), i = e === nn ? this.constructor.eventName("mouseleave") : this.constructor.eventName("focusout"); P.on(this._element, t, this._config.selector, (t) => { const e = this._initializeOnDelegatedTarget(t); (e._activeTrigger["focusin" === t.type ? sn : nn] = !0), e._enter(); }), P.on(this._element, i, this._config.selector, (t) => { const e = this._initializeOnDelegatedTarget(t); (e._activeTrigger["focusout" === t.type ? sn : nn] = e._element.contains(t.relatedTarget)), e._leave(); }); } (this._hideModalHandler = () => { this._element && this.hide(); }), P.on(this._element.closest(tn), en, this._hideModalHandler); } _fixTitle() { const t = this._element.getAttribute("title"); t && (this._element.getAttribute("aria-label") || this._element.textContent.trim() || this._element.setAttribute("aria-label", t), this._element.setAttribute("data-bs-original-title", t), this._element.removeAttribute("title")); } _enter() { this._isShown() || this._isHovered ? (this._isHovered = !0) : ((this._isHovered = !0), this._setTimeout(() => { this._isHovered && this.show(); }, this._config.delay.show)); } _leave() { this._isWithActiveTrigger() || ((this._isHovered = !1), this._setTimeout(() => { this._isHovered || this.hide(); }, this._config.delay.hide)); } _setTimeout(t, e) { clearTimeout(this._timeout), (this._timeout = setTimeout(t, e)); } _isWithActiveTrigger() { return Object.values(this._activeTrigger).includes(!0); } _getConfig(t) { const e = H.getDataAttributes(this._element); for (const t of Object.keys(e)) Gi.has(t) && delete e[t]; return ( (t = { ...e, ...("object" == typeof t && t ? t : {}) }), (t = this._mergeConfigObj(t)), (t = this._configAfterMerge(t)), this._typeCheckConfig(t), t ); } _configAfterMerge(t) { return ( (t.container = !1 === t.container ? document.body : r(t.container)), "number" == typeof t.delay && (t.delay = { show: t.delay, hide: t.delay }), "number" == typeof t.title && (t.title = t.title.toString()), "number" == typeof t.content && (t.content = t.content.toString()), t ); } _getDelegateConfig() { const t = {}; for (const [e, i] of Object.entries(this._config)) this.constructor.Default[e] !== i && (t[e] = i); return (t.selector = !1), (t.trigger = "manual"), t; } _disposePopper() { this._popper && (this._popper.destroy(), (this._popper = null)), this.tip && (this.tip.remove(), (this.tip = null)); } static jQueryInterface(t) { return this.each(function () { const e = ln.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t]) throw new TypeError(`No method named "${t}"`); e[t](); } }); } } m(ln); const cn = { ...ln.Default, content: "", offset: [0, 8], placement: "right", template: '', trigger: "click", }, hn = { ...ln.DefaultType, content: "(null|string|element|function)" }; class dn extends ln { static get Default() { return cn; } static get DefaultType() { return hn; } static get NAME() { return "popover"; } _isWithContent() { return this._getTitle() || this._getContent(); } _getContentForTemplate() { return { ".popover-header": this._getTitle(), ".popover-body": this._getContent(), }; } _getContent() { return this._resolvePossibleFunction(this._config.content); } static jQueryInterface(t) { return this.each(function () { const e = dn.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t]) throw new TypeError(`No method named "${t}"`); e[t](); } }); } } m(dn); const un = "click.bs.scrollspy", fn = "active", pn = "[href]", mn = { offset: null, rootMargin: "0px 0px -25%", smoothScroll: !1, target: null, threshold: [0.1, 0.5, 1], }, gn = { offset: "(number|null)", rootMargin: "string", smoothScroll: "boolean", target: "element", threshold: "array", }; class _n extends W { constructor(t, e) { super(t, e), (this._targetLinks = new Map()), (this._observableSections = new Map()), (this._rootElement = "visible" === getComputedStyle(this._element).overflowY ? null : this._element), (this._activeTarget = null), (this._observer = null), (this._previousScrollData = { visibleEntryTop: 0, parentScrollTop: 0 }), this.refresh(); } static get Default() { return mn; } static get DefaultType() { return gn; } static get NAME() { return "scrollspy"; } refresh() { this._initializeTargetsAndObservables(), this._maybeEnableSmoothScroll(), this._observer ? this._observer.disconnect() : (this._observer = this._getNewObserver()); for (const t of this._observableSections.values()) this._observer.observe(t); } dispose() { this._observer.disconnect(), super.dispose(); } _configAfterMerge(t) { return ( (t.target = r(t.target) || document.body), (t.rootMargin = t.offset ? `${t.offset}px 0px -30%` : t.rootMargin), "string" == typeof t.threshold && (t.threshold = t.threshold .split(",") .map((t) => Number.parseFloat(t))), t ); } _maybeEnableSmoothScroll() { this._config.smoothScroll && (P.off(this._config.target, un), P.on(this._config.target, un, pn, (t) => { const e = this._observableSections.get(t.target.hash); if (e) { t.preventDefault(); const i = this._rootElement || window, n = e.offsetTop - this._element.offsetTop; if (i.scrollTo) return void i.scrollTo({ top: n, behavior: "smooth" }); i.scrollTop = n; } })); } _getNewObserver() { const t = { root: this._rootElement, threshold: this._config.threshold, rootMargin: this._config.rootMargin, }; return new IntersectionObserver((t) => this._observerCallback(t), t); } _observerCallback(t) { const e = (t) => this._targetLinks.get(`#${t.target.id}`), i = (t) => { (this._previousScrollData.visibleEntryTop = t.target.offsetTop), this._process(e(t)); }, n = (this._rootElement || document.documentElement).scrollTop, s = n >= this._previousScrollData.parentScrollTop; this._previousScrollData.parentScrollTop = n; for (const o of t) { if (!o.isIntersecting) { (this._activeTarget = null), this._clearActiveClass(e(o)); continue; } const t = o.target.offsetTop >= this._previousScrollData.visibleEntryTop; if (s && t) { if ((i(o), !n)) return; } else s || t || i(o); } } _initializeTargetsAndObservables() { (this._targetLinks = new Map()), (this._observableSections = new Map()); const t = z.find(pn, this._config.target); for (const e of t) { if (!e.hash || l(e)) continue; const t = z.findOne(decodeURI(e.hash), this._element); a(t) && (this._targetLinks.set(decodeURI(e.hash), e), this._observableSections.set(e.hash, t)); } } _process(t) { this._activeTarget !== t && (this._clearActiveClass(this._config.target), (this._activeTarget = t), t.classList.add(fn), this._activateParents(t), P.trigger(this._element, "activate.bs.scrollspy", { relatedTarget: t, })); } _activateParents(t) { if (t.classList.contains("dropdown-item")) z.findOne(".dropdown-toggle", t.closest(".dropdown")).classList.add(fn); else for (const e of z.parents(t, ".nav, .list-group")) for (const t of z.prev( e, ".nav-link, .nav-item > .nav-link, .list-group-item" )) t.classList.add(fn); } _clearActiveClass(t) { t.classList.remove(fn); const e = z.find("[href].active", t); for (const t of e) t.classList.remove(fn); } static jQueryInterface(t) { return this.each(function () { const e = _n.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t] || t.startsWith("_") || "constructor" === t) throw new TypeError(`No method named "${t}"`); e[t](); } }); } } P.on(window, "load.bs.scrollspy.data-api", () => { for (const t of z.find('[data-bs-spy="scroll"]')) _n.getOrCreateInstance(t); }), m(_n); const bn = "ArrowLeft", vn = "ArrowRight", yn = "ArrowUp", wn = "ArrowDown", An = "active", En = "fade", Tn = "show", Cn = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]', On = `.nav-link:not(.dropdown-toggle), .list-group-item:not(.dropdown-toggle), [role="tab"]:not(.dropdown-toggle), ${Cn}`; class xn extends W { constructor(t) { super(t), (this._parent = this._element.closest( '.list-group, .nav, [role="tablist"]' )), this._parent && (this._setInitialAttributes(this._parent, this._getChildren()), P.on(this._element, "keydown.bs.tab", (t) => this._keydown(t))); } static get NAME() { return "tab"; } show() { const t = this._element; if (this._elemIsActive(t)) return; const e = this._getActiveElem(), i = e ? P.trigger(e, "hide.bs.tab", { relatedTarget: t }) : null; P.trigger(t, "show.bs.tab", { relatedTarget: e }).defaultPrevented || (i && i.defaultPrevented) || (this._deactivate(e, t), this._activate(t, e)); } _activate(t, e) { t && (t.classList.add(An), this._activate(z.getElementFromSelector(t)), this._queueCallback( () => { "tab" === t.getAttribute("role") ? (t.removeAttribute("tabindex"), t.setAttribute("aria-selected", !0), this._toggleDropDown(t, !0), P.trigger(t, "shown.bs.tab", { relatedTarget: e })) : t.classList.add(Tn); }, t, t.classList.contains(En) )); } _deactivate(t, e) { t && (t.classList.remove(An), t.blur(), this._deactivate(z.getElementFromSelector(t)), this._queueCallback( () => { "tab" === t.getAttribute("role") ? (t.setAttribute("aria-selected", !1), t.setAttribute("tabindex", "-1"), this._toggleDropDown(t, !1), P.trigger(t, "hidden.bs.tab", { relatedTarget: e })) : t.classList.remove(Tn); }, t, t.classList.contains(En) )); } _keydown(t) { if (![bn, vn, yn, wn].includes(t.key)) return; t.stopPropagation(), t.preventDefault(); const e = [vn, wn].includes(t.key), i = b( this._getChildren().filter((t) => !l(t)), t.target, e, !0 ); i && (i.focus({ preventScroll: !0 }), xn.getOrCreateInstance(i).show()); } _getChildren() { return z.find(On, this._parent); } _getActiveElem() { return this._getChildren().find((t) => this._elemIsActive(t)) || null; } _setInitialAttributes(t, e) { this._setAttributeIfNotExists(t, "role", "tablist"); for (const t of e) this._setInitialAttributesOnChild(t); } _setInitialAttributesOnChild(t) { t = this._getInnerElement(t); const e = this._elemIsActive(t), i = this._getOuterElement(t); t.setAttribute("aria-selected", e), i !== t && this._setAttributeIfNotExists(i, "role", "presentation"), e || t.setAttribute("tabindex", "-1"), this._setAttributeIfNotExists(t, "role", "tab"), this._setInitialAttributesOnTargetPanel(t); } _setInitialAttributesOnTargetPanel(t) { const e = z.getElementFromSelector(t); e && (this._setAttributeIfNotExists(e, "role", "tabpanel"), t.id && this._setAttributeIfNotExists(e, "aria-labelledby", `${t.id}`)); } _toggleDropDown(t, e) { const i = this._getOuterElement(t); if (!i.classList.contains("dropdown")) return; const n = (t, n) => { const s = z.findOne(t, i); s && s.classList.toggle(n, e); }; n(".dropdown-toggle", An), n(".dropdown-menu", Tn), i.setAttribute("aria-expanded", e); } _setAttributeIfNotExists(t, e, i) { t.hasAttribute(e) || t.setAttribute(e, i); } _elemIsActive(t) { return t.classList.contains(An); } _getInnerElement(t) { return t.matches(On) ? t : z.findOne(On, t); } _getOuterElement(t) { return t.closest(".nav-item, .list-group-item") || t; } static jQueryInterface(t) { return this.each(function () { const e = xn.getOrCreateInstance(this); if ("string" == typeof t) { if (void 0 === e[t] || t.startsWith("_") || "constructor" === t) throw new TypeError(`No method named "${t}"`); e[t](); } }); } } P.on(document, "click.bs.tab", Cn, function (t) { ["A", "AREA"].includes(this.tagName) && t.preventDefault(), l(this) || xn.getOrCreateInstance(this).show(); }), P.on(window, "load.bs.tab", () => { for (const t of z.find( '.active[data-bs-toggle="tab"], .active[data-bs-toggle="pill"], .active[data-bs-toggle="list"]' )) xn.getOrCreateInstance(t); }), m(xn); const kn = "hide", Ln = "show", Sn = "showing", Dn = { animation: "boolean", autohide: "boolean", delay: "number" }, In = { animation: !0, autohide: !0, delay: 5e3 }; class Nn extends W { constructor(t, e) { super(t, e), (this._timeout = null), (this._hasMouseInteraction = !1), (this._hasKeyboardInteraction = !1), this._setListeners(); } static get Default() { return In; } static get DefaultType() { return Dn; } static get NAME() { return "toast"; } show() { P.trigger(this._element, "show.bs.toast").defaultPrevented || (this._clearTimeout(), this._config.animation && this._element.classList.add("fade"), this._element.classList.remove(kn), d(this._element), this._element.classList.add(Ln, Sn), this._queueCallback( () => { this._element.classList.remove(Sn), P.trigger(this._element, "shown.bs.toast"), this._maybeScheduleHide(); }, this._element, this._config.animation )); } hide() { this.isShown() && (P.trigger(this._element, "hide.bs.toast").defaultPrevented || (this._element.classList.add(Sn), this._queueCallback( () => { this._element.classList.add(kn), this._element.classList.remove(Sn, Ln), P.trigger(this._element, "hidden.bs.toast"); }, this._element, this._config.animation ))); } dispose() { this._clearTimeout(), this.isShown() && this._element.classList.remove(Ln), super.dispose(); } isShown() { return this._element.classList.contains(Ln); } _maybeScheduleHide() { this._config.autohide && (this._hasMouseInteraction || this._hasKeyboardInteraction || (this._timeout = setTimeout(() => { this.hide(); }, this._config.delay))); } _onInteraction(t, e) { switch (t.type) { case "mouseover": case "mouseout": this._hasMouseInteraction = e; break; case "focusin": case "focusout": this._hasKeyboardInteraction = e; } if (e) return void this._clearTimeout(); const i = t.relatedTarget; this._element === i || this._element.contains(i) || this._maybeScheduleHide(); } _setListeners() { P.on(this._element, "mouseover.bs.toast", (t) => this._onInteraction(t, !0) ), P.on(this._element, "mouseout.bs.toast", (t) => this._onInteraction(t, !1) ), P.on(this._element, "focusin.bs.toast", (t) => this._onInteraction(t, !0) ), P.on(this._element, "focusout.bs.toast", (t) => this._onInteraction(t, !1) ); } _clearTimeout() { clearTimeout(this._timeout), (this._timeout = null); } static jQueryInterface(t) { return this.each(function () { const e = Nn.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t]) throw new TypeError(`No method named "${t}"`); e[t](this); } }); } } return ( R(Nn), m(Nn), { Alert: q, Button: K, Carousel: rt, Collapse: ft, Dropdown: ci, Modal: Ni, Offcanvas: zi, Popover: dn, ScrollSpy: _n, Tab: xn, Toast: Nn, Tooltip: ln, } ); }); a2d-2.0.5/a2d/static/js/000077500000000000000000000000001464731662700145555ustar00rootroot00000000000000a2d-2.0.5/a2d/static/js/clean_pin.js000066400000000000000000000002431464731662700170420ustar00rootroot00000000000000// Clean PIN input function cancelAction() { window.location.href = "/login"; } function clearPinFields() { document.getElementById("pinForm").reset(); } a2d-2.0.5/a2d/static/js/dns.js000066400000000000000000000343301464731662700157020ustar00rootroot00000000000000//HTML alert display box document.addEventListener('DOMContentLoaded', function() { function updateServerData() { fetch('/for-html') .then(response => response.json()) .then(data => { const listenPortElement = document.getElementById('listen-port'); const serverNameElement = document.getElementById('server-name'); const currentSSLStatusElement = document.getElementById('current-ssl-status'); const commonNameElement = document.getElementById('common-name'); const organizationNameElement = document.getElementById('organization-name'); const expiryDateElement = document.getElementById('expiry-date'); const nginxStatusElement = document.getElementById('nginx-status'); const gunicornStatusElement = document.getElementById('gunicorn-status'); listenPortElement.innerHTML = 'Listen Port: ' + data.listen_port; serverNameElement.innerHTML = 'Server Name: ' + data.server_name; currentSSLStatusElement.innerHTML = 'SSL Status: ' + data.current_ssl_status; commonNameElement.innerHTML = 'Common Name (CN): ' + data.common_name; organizationNameElement.innerHTML = 'Organization Name (O): ' + data.organization_name; nginxStatusElement.innerHTML = 'Nginx: Active since ' + data.nginx_status; gunicornStatusElement.innerHTML = 'Gunicorn WSGI: Active since ' + data.gunicorn_status; const expiryDate = data.expiry_date; if (expiryDate === 'Expired') { expiryDateElement.innerHTML = 'Expiry Date: ' + expiryDate + ''; } else { expiryDateElement.innerHTML = 'Expiry Date: ' + expiryDate; } }) .catch(error => { console.error('Error fetching server data:', error); }); } updateServerData(); setInterval(updateServerData, 5000); }); //Network health document.addEventListener('DOMContentLoaded', function() { function updateNetworkData() { fetch('/get-rtt') .then(response => response.json()) .then(data => { const aprsRttElement = document.getElementById('aprs-rtt'); const dapnetRttElement = document.getElementById('dapnet-rtt'); const aprsRtt = data.aprs_rtt; const dapnetRtt = data.dapnet_rtt; if (aprsRtt === 'Unreachable') { aprsRttElement.innerHTML = 'APRS RTT: ' + aprsRtt + ''; } else { aprsRttElement.innerHTML = 'APRS RTT: ' + aprsRtt; } if (dapnetRtt === 'Unreachable') { dapnetRttElement.innerHTML = 'DAPNET RTT: ' + dapnetRtt + ''; } else { dapnetRttElement.innerHTML = 'DAPNET RTT: ' + dapnetRtt; } }) .catch(error => { console.error('Error fetching server data:', error); }); } updateNetworkData(); //first minute 10 sec, after that 120 sec var firstMinuteInterval = setInterval(updateNetworkData, 10000); setTimeout(function () { clearInterval(firstMinuteInterval); setInterval(updateNetworkData, 120000); }, 60000); }); //Server Config document.addEventListener("DOMContentLoaded", function () { const form = document.getElementById("serverForm"); const enableSSLCheckbox = document.getElementById("enable_ssl"); const enableCASSLCheckbox = document.getElementById("enable_cassl"); const setDefaultDnsCheckbox = document.getElementById("set_default_dns"); const serverMessage = document.getElementById("server-message"); form.addEventListener("submit", async function (event) { event.preventDefault(); const listenPort = form.elements["listen_port"].value; const serverName = form.elements["server_name"].value; const caSSLCerts = form.elements["set_cassl_certs"].value; const enableSSL = enableSSLCheckbox.checked; const enableCASSL = enableCASSLCheckbox.checked; const setDefaultDns = setDefaultDnsCheckbox.checked; try { const formData = new FormData(); formData.append("listen_port", listenPort); formData.append("server_name", serverName); formData.append("set_cassl_certs", caSSLCerts); formData.append("enable_ssl", enableSSL); formData.append("enable_cassl", enableCASSL); formData.append("set_default_dns", setDefaultDns); const response = await fetch(form.action, { method: "POST", body: formData, }); const result = await response.text(); if (response.ok) { if (result === "Create SSL") { serverMessage.innerText = "Create self SSL certificate before enabling"; } else if (result === "Create caSSL") { serverMessage.innerText = "Create/Select CA SSL certificate before enabling"; } else if (result === "SN_CN no match") { serverMessage.innerText = "Server and Common Name mismatch"; } else if (result === "caSSL enabled") { serverMessage.innerText = "CA SSL enabled"; } else if (result === "Set default") { serverMessage.innerText = "Default to the original settings"; } else if (result === "SSL enabled") { serverMessage.innerText = "Self-Signed SSL enabled"; } else if (result === "Server updated") { serverMessage.innerText = "Server settings updated"; } else { serverMessage.innerText = "Unknown response from the server"; } } else { serverMessage.innerText = "Failed to update server"; } } catch (error) { console.error("Error updating server", error); serverMessage.innerText = "Error updating server"; } }); }); // JavaScript code to ensure only one checkbox is checked at a time document.addEventListener("DOMContentLoaded", function() { const enableSSLCheckboxControl = document.getElementById('enable_ssl'); const enableCASSLCheckboxControl = document.getElementById('enable_cassl'); const setDefaultDNSCheckbox = document.getElementById('set_default_dns'); const checkboxClickedInput = document.getElementById('checkbox_clicked'); enableSSLCheckboxControl.addEventListener('click', function() { setDefaultDNSCheckbox.checked = false; enableCASSLCheckboxControl.checked = false; checkboxClickedInput.value = 'enable_ssl'; }); enableCASSLCheckboxControl.addEventListener('click', function() { setDefaultDNSCheckbox.checked = false; enableSSLCheckboxControl.checked = false; checkboxClickedInput.value = 'enable_cassl'; }); setDefaultDNSCheckbox.addEventListener('click', function() { enableSSLCheckboxControl.checked = false; enableCASSLCheckboxControl.checked = false; checkboxClickedInput.value = 'set_default_dns'; }); }); //CA signed SSL document.addEventListener("DOMContentLoaded", function () { const form = document.getElementById("caSSLForm"); const caSSLMessage = document.getElementById("cassl-message"); form.addEventListener("submit", async function (event) { event.preventDefault(); const cacommonName = form.elements["ca_common_name"].value; const emailidName = form.elements["email_id"].value; try { // Show "Generating..." message caSSLMessage.innerText = "Generating..."; const formData = new FormData(); formData.append("ca_common_name", cacommonName); formData.append("email_id", emailidName); const response = await fetch(form.action, { method: "POST", body: formData, }); const result = await response.text(); if (response.ok) { if (result === "caSSL exist") { caSSLMessage.innerText = "CA SSL certificate already exists"; } else if (result === "caSSL generated") { caSSLMessage.innerText = "CA SSL certificate generated"; } else if (result === "invalid email") { caSSLMessage.innerText = "Enter valid email for certificate notifications"; } else { caSSLMessage.innerText = "Unknown response from the server"; } } else { caSSLMessage.innerText = "Failed to generate SSL certificate"; } } catch (error) { console.error("Error generating SSL", error); caSSLMessage.innerText = "Error generating SSL certificate"; } }); }); //Self signed SSL document.addEventListener("DOMContentLoaded", function () { const form = document.getElementById("selfSSLForm"); const selfSSLMessage = document.getElementById("selfssl-message"); form.addEventListener("submit", async function (event) { event.preventDefault(); const commonName = form.elements["common_name"].value; const validityDays = form.elements["validity_days"].value; const organizationName = form.elements["organization_name"].value; try { // Show "Generating..." message selfSSLMessage.innerText = "Generating..."; const formData = new FormData(); formData.append("common_name", commonName); formData.append("validity_days", validityDays); formData.append("organization_name", organizationName); const response = await fetch(form.action, { method: "POST", body: formData, }); const result = await response.text(); if (response.ok) { if (result === "sSSL generated") { selfSSLMessage.innerText = "SSL certificate generated"; } else { selfSSLMessage.innerText = "Unknown response from the server"; } } else { selfSSLMessage.innerText = "Failed to generate SSL certificate"; } } catch (error) { console.error("Error generating SSL", error); selfSSLMessage.innerText = "Error generating SSL certificate"; } }); }); //Remove CA SSL document.addEventListener("DOMContentLoaded", function () { const form = document.getElementById("rmcaSSLForm"); const rmCaSSLMessage = document.getElementById("rmcassl-message"); form.addEventListener("submit", async function (event) { event.preventDefault(); const rmcaSSLCerts = form.elements["rm_cassl_certs"].value; try { const formData = new FormData(); formData.append("rm_cassl_certs", rmcaSSLCerts); const response = await fetch(form.action, { method: "POST", body: formData, }); const result = await response.text(); if (response.ok) { if (result === "caSSL removed") { rmCaSSLMessage.innerText = "CA SSL Deleted"; } else if (result === "Error removing SSL") { rmCaSSLMessage.innerText = "Error deleting CA SSL"; } else if (result === "caSSL in use") { rmCaSSLMessage.innerText = "Selected CA SSL or Server Name in USE"; } else if (result === "No caSSL") { rmCaSSLMessage.innerText = "CA SSL Do not Exist"; } else { rmCaSSLMessage.innerText = "Unknown response from the server"; } } else { rmCaSSLMessage.innerText = "Failed to delete CA SSL"; } } catch (error) { console.error("Error deleting CA SSL", error); rmCaSSLMessage.innerText = "Error deleting CA SSL"; } }); }); //Reset portal document.addEventListener("DOMContentLoaded", function () { const form = document.getElementById("resetForm"); const passphraseInput = document.getElementById("passphrase"); const selfSSLDELCheckbox = document.getElementById("self_ssl_delete"); const caSSLDELCheckbox = document.getElementById("ca_ssl_delete"); const resetMessage = document.getElementById("reset-message"); form.addEventListener("submit", async function (event) { event.preventDefault(); const passphrase = passphraseInput.value; const resetConfirm = form.elements["reset_confirm"].value; const selfSSLDEL = selfSSLDELCheckbox.checked; const caSSLDEL = caSSLDELCheckbox.checked; try { // Show "Resetting..." message resetMessage.innerText = "Factory resetting..."; const formData = new FormData(); formData.append("passphrase", passphrase); formData.append("reset_confirm", resetConfirm); formData.append("self_ssl_delete", selfSSLDEL); formData.append("ca_ssl_delete", caSSLDEL); const response = await fetch(form.action, { method: "POST", body: formData, }); const result = await response.text(); if (response.ok) { if (result === "Type delete") { resetMessage.innerText = "Type DELETE to confirm Factory Reset"; } else if (result === "logout") { resetMessage.innerText = "Factory reset complete, you can safely logout"; } else { resetMessage.innerText = "Unknown response from the server"; } } else if (response.status === 400) { resetMessage.innerText = "Invalid Passphrase!"; } } catch (error) { console.error("Error updating server", error); resetMessage.innerText = "Error Factory Resetting"; } }); }); a2d-2.0.5/a2d/static/js/export_import.js000066400000000000000000000074311464731662700200330ustar00rootroot00000000000000//Export document.addEventListener("DOMContentLoaded", function () { const backupButton = document.getElementById("backupButton"); const passphraseInput = document.getElementById("passphrase1"); const errorMessage = document.getElementById("error-message"); backupButton.addEventListener("click", async function () { // Get the passphrase from the input const passphrase = passphraseInput.value; // Create a new FormData object to send the passphrase const formData = new FormData(); formData.append("passphrase", passphrase); formData.append("action", "export"); try { // Use the Fetch API to make the POST request const response = await fetch("/export", { method: "POST", body: formData, }); if (response.ok) { // Create a download link for the binary file const blob = await response.blob(); const link = document.createElement("a"); link.href = window.URL.createObjectURL(blob); link.download = "a2d_config_backup.yaml"; link.click(); //Set timeout to redirect, works if autodownload enabled in browsers setTimeout(() => { window.location.href = "/"; }, 2000); } else if (response.status === 400) { errorMessage.innerText = "Invalid Passphrase!"; } else if (response.status === 401) { errorMessage.innerText = "Config files missing! Setup Config"; } else { console.error("Backup failed"); errorMessage.innerText = "Backup failed"; } } catch (error) { console.error("Error during backup:", error); errorMessage.innerText = "Error during backup"; } }); }); //Import document.addEventListener("DOMContentLoaded", function () { const restoreButton = document.getElementById("restoreButton"); const passphraseInput = document.getElementById("passphrase2"); const importFileInput = document.getElementById("import_file"); const errorMessage2 = document.getElementById("error-message2"); restoreButton.addEventListener("click", async function () { // Get the passphrase and file from the inputs const passphrase = passphraseInput.value; const importFile = importFileInput.files[0]; // Create a new FormData object to send the passphrase and file const formData = new FormData(); formData.append("passphrase", passphrase); formData.append("import_file", importFile); formData.append("action", "import"); try { // Use the Fetch API to make the POST request const response = await fetch("/import", { method: "POST", body: formData, }); if (response.ok) { // Import successful, redirect to the main page, server side redirect wont work window.location.href = "/"; } else if (response.status === 400) { errorMessage2.innerText = "Invalid Passphrase!"; } else if (response.status === 401) { errorMessage2.innerText = "Config file tampered! Don't use this file"; } else if (response.status === 402) { errorMessage2.innerText = "Invalid a2d backup file!"; } else { // Other error, log it console.error("Restore failed"); errorMessage2.innerText = "Backup failed"; } } catch (error) { console.error("Error during restore:", error); errorMessage2.innerText = "Error during backup"; } }); }); a2d-2.0.5/a2d/static/js/heartbeat.js000066400000000000000000000016511464731662700170550ustar00rootroot00000000000000// Function to perform the logout and redirect to the logout page. function logoutUser() { localStorage.clear(); // You can use sessionStorage instead for a session-specific logout. window.location.href = '/logout'; // Replace '/logout' with the URL for your logout page or endpoint. } // Interval in milliseconds for the inactivity period (e.g., 30 minutes). const INACTIVITY_PERIOD = 20 * 60 * 1000; // Function to reset the inactivity timer. function resetInactivityTimer() { clearTimeout(window.inactivityTimer); window.inactivityTimer = setTimeout(logoutUser, INACTIVITY_PERIOD); } // Start the inactivity timer when the page loads. document.addEventListener('DOMContentLoaded', function () { resetInactivityTimer(); // Reset the timer when any user interaction occurs (e.g., click, keypress). window.addEventListener('click', resetInactivityTimer); window.addEventListener('keydown', resetInactivityTimer); }); a2d-2.0.5/a2d/static/js/input_cursor.js000066400000000000000000000012301464731662700176430ustar00rootroot00000000000000// PIN input move cursor function moveToNext(currentInput, nextInputName) { if (currentInput.value.length >= currentInput.maxLength) { const nextInput = document.getElementsByName(nextInputName)[0]; nextInput.focus(); } } //Advanced config checkbox requirement function validateForm() { var checkboxes = document.getElementsByName('ssids[]'); var checked = false; for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checked = true; break; } } if (!checked) { alert('Select at least one SSID or -0 for no SSID.'); return false; } return true; } a2d-2.0.5/a2d/static/js/log_updates.js000066400000000000000000000041651464731662700174270ustar00rootroot00000000000000// Variable to hold the interval ID var logsInterval; // Log updates function updateLogs() { var logsContainer = document.getElementById("logs-container"); var counter = 0; // Make a fetch request to fetch new log entries fetch("/fetch-logs") .then(response => { if (!response.ok) { throw new Error("Request failed. Status: " + response.status); } return response.json(); }) .then(logs => { // Clear existing log entries logsContainer.innerHTML = ""; if (logs.length > 0) { // Add new log entries for (var i = 0; i < logs.length; i++) { var log = logs[i]; var logEntry = document.createElement("p"); counter++; // Split the log into parts: message_id, restOfLog var parts = log.split(" > "); var message_id = parts[0]; var restOfLog = parts.slice(1).join(" > "); // Create a element for the log entry var logSpan = document.createElement("span"); logSpan.innerHTML = `${logs.length - i}. ${message_id}> ${restOfLog}`; // Apply italics to message_id logEntry.appendChild(logSpan); // Append the to the log entry logsContainer.appendChild(logEntry); } } else { // Add 'No APRS messages' text var noMessagesElement = document.createElement("p"); noMessagesElement.textContent = "No Transmits"; logsContainer.appendChild(noMessagesElement); } }) .catch(error => { // Request failed, handle error console.error("Failed to fetch log entries. Error:", error); }); } // Periodically update logs every 3 seconds logsInterval = setInterval(updateLogs, 3000); // Stop fetching logs after 10 minutes (600,000 milliseconds) setTimeout(function () { clearInterval(logsInterval); }, 600000); a2d-2.0.5/a2d/static/js/login_buttons.js000066400000000000000000000057501464731662700200100ustar00rootroot00000000000000// Button functions document.addEventListener("DOMContentLoaded", function() { const loginButton = document.getElementById("loginButton"); const registerButton = document.getElementById("registerButton"); const digit1Input = document.getElementById("digit1Input"); const digit2Input = document.getElementById("digit2Input"); const digit3Input = document.getElementById("digit3Input"); const digit4Input = document.getElementById("digit4Input"); const digit5Input = document.getElementById("digit5Input"); const digit6Input = document.getElementById("digit6Input"); fetch("/check_pin_file") .then(response => response.text()) .then(data => { if (data === "7965732062696e") { loginButton.disabled = false; registerButton.innerHTML = "Forgot PIN?"; registerButton.value = "forgotpin"; registerButton.addEventListener("click", function() { window.location.href = "/pin-change"; }); loginButton.addEventListener("click", function() { document.getElementById("pinForm").action = "/login"; document.getElementById("pinForm").submit(); }); digit1Input.addEventListener("keydown", function(event) { if (event.key === "Enter") { loginButton.click(); } }); digit2Input.addEventListener("keydown", function(event) { if (event.key === "Enter") { loginButton.click(); } }); digit3Input.addEventListener("keydown", function(event) { if (event.key === "Enter") { loginButton.click(); } }); digit4Input.addEventListener("keydown", function(event) { if (event.key === "Enter") { loginButton.click(); } }); digit5Input.addEventListener("keydown", function(event) { if (event.key === "Enter") { loginButton.click(); } }); digit6Input.addEventListener("keydown", function(event) { if (event.key === "Enter") { loginButton.click(); } }); } else { loginButton.disabled = true; registerButton.innerHTML = "Register"; registerButton.value = "register"; registerButton.addEventListener("click", function() { window.location.href = "/create-passphrase"; }); // Disable input fields digit1Input.disabled = true; digit2Input.disabled = true; digit3Input.disabled = true; digit4Input.disabled = true; digit5Input.disabled = true; digit6Input.disabled = true; } }) .catch(error => { console.error("Error checking pin file:", error); }); }); a2d-2.0.5/a2d/static/js/status.js000066400000000000000000000060011464731662700164330ustar00rootroot00000000000000document.addEventListener('DOMContentLoaded', function() { async function updateServiceStatus() { try { const [serviceStatusResponse, dashResponse] = await Promise.all([ fetch('/service-status'), fetch('/for-dash') ]); const serviceStatusData = await serviceStatusResponse.json(); const dashData = await dashResponse.json(); const serviceStatusDiv = document.getElementById('service-status'); const lastRunDiv = document.getElementById('last-run'); const runIntervalDiv = document.getElementById('run-interval'); const ssidDisplayDiv = document.getElementById('ssids-display'); const labelElement = document.getElementById('get-callsign2'); const callsignDiv = document.getElementById('get-callsign'); const statusMessage = serviceStatusData.service_status.status_message; const colorStatus = serviceStatusData.service_status.color_status; if (colorStatus) { serviceStatusDiv.innerHTML = 'Status: ' + statusMessage; } else { serviceStatusDiv.innerHTML = 'Status: ' + statusMessage + ''; } lastRunDiv.innerHTML = 'Last Run: ' + dashData.last_run_human; runIntervalDiv.innerHTML = 'Run Interval: ' + dashData.run_interval; ssidDisplayDiv.innerHTML = 'Destination SSIDs: ' + dashData.ssids_display; labelElement.textContent = dashData.callsign2; const callsign = dashData.callsign; if (callsign === 'Setup Configuration') { callsignDiv.innerHTML = 'Call Sign: ' + callsign + ''; } else { callsignDiv.innerHTML = 'Call Sign: ' + callsign; } } catch (error) { console.error('Error fetching and updating data:', error); } fetch('/system-info') .then(response => response.json()) .then(data => { const cpuTemperatureElement = document.getElementById('cpu-temperature'); const systemMemoryUsageElement = document.getElementById('system-memory-usage'); const cpuLoadElement = document.getElementById('cpu-load'); cpuTemperatureElement.innerHTML = 'CPU Temp: ' + data.cpu_temperature; systemMemoryUsageElement.innerHTML = 'Memory Usage: ' + data.system_memory_usage; cpuLoadElement.innerHTML = 'CPU Load: ' + data.cpu_load; }) .catch(error => { console.error('Error fetching server data:', error); }); } updateServiceStatus(); setInterval(updateServiceStatus, 5000); }); a2d-2.0.5/a2d/static/js/version.js000066400000000000000000000006531464731662700166040ustar00rootroot00000000000000// Fetch the version number from /version endpoint document.addEventListener('DOMContentLoaded', () => { fetch('/version') .then(response => response.json()) .then(data => { const versionElement = document.getElementById('version-placeholder'); versionElement.textContent = `v${data.version}`; }) .catch(error => console.error('Error fetching version:', error)); }); a2d-2.0.5/a2d/templates/000077500000000000000000000000001464731662700146505ustar00rootroot00000000000000a2d-2.0.5/a2d/templates/dns.html000066400000000000000000000050621464731662700163250ustar00rootroot00000000000000 a2d Dashboard {% include 'navbar_dns.html' %}
Server Status

Network Health
{% include 'footer.html' %}
a2d-2.0.5/a2d/templates/footer.html000066400000000000000000000002531464731662700170340ustar00rootroot00000000000000
a2d ... © 2023 - 2024 NGC2023. MIT, CC-BY-3.0.
a2d-2.0.5/a2d/templates/index.html000066400000000000000000000073471464731662700166600ustar00rootroot00000000000000 a2d Dashboard {% include 'navbar_dash.html' %}

System Health

APRS to DAPNET Transmits

#. MessageID> SourceCall (DestinationSSID): Message

{% include 'footer.html' %}
a2d-2.0.5/a2d/templates/login.html000066400000000000000000000073011464731662700166470ustar00rootroot00000000000000 a2d PIN {% include 'navbar_login.html' %}
PIN


{% if error %} {% endif %}
{% include 'footer.html' %}
a2d-2.0.5/a2d/templates/navbar_dash.html000066400000000000000000000341511464731662700200120ustar00rootroot00000000000000
a2d-2.0.5/a2d/templates/navbar_dns.html000066400000000000000000000410251464731662700176550ustar00rootroot00000000000000 a2d-2.0.5/a2d/templates/navbar_login.html000066400000000000000000000030361464731662700202010ustar00rootroot00000000000000 a2d-2.0.5/a2d/templates/no_docs.html000066400000000000000000000037241464731662700171700ustar00rootroot00000000000000 a2d Instructions {% include 'navbar_login.html' %}
a2d Documentation
{% include 'footer.html' %}
a2d-2.0.5/a2d/templates/updatepin.html000066400000000000000000000076101464731662700175330ustar00rootroot00000000000000 a2d PIN {% include 'navbar_login.html' %}
New PIN
New Passphrase

{% include 'footer.html' %}
a2d-2.0.5/a2d/templates/verifypassphrase.html000066400000000000000000000046531464731662700211440ustar00rootroot00000000000000 a2d Forgot PIN {% include 'navbar_login.html' %}
Enter Passphrase
{% include 'footer.html' %}
a2d-2.0.5/a2dfiles/000077500000000000000000000000001464731662700136755ustar00rootroot00000000000000a2d-2.0.5/a2dfiles/00-a2d.conf000066400000000000000000000005351464731662700154320ustar00rootroot00000000000000server { listen 9331; server_name _; location / { proxy_pass http://127.0.0.1:9333; # Gunicorn address and port proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } a2d-2.0.5/a2dfiles/a2d.desktop000066400000000000000000000004671464731662700157450ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=a2d portal Exec=/usr/share/scripts/a2d_desktop_url.sh Icon=/usr/share/images/logo_a2d.png Categories=Utility;HamRadio; Comment=APRS to DAPNET portal Version=2.0 Keywords=hamradio;radio;aprs;dapnet;pager;pocsag;message;portal;a2d;webapp; Terminal=false DBusActivatable=true a2d-2.0.5/a2dfiles/a2d_adv_conf.ini000066400000000000000000000002571464731662700167070ustar00rootroot00000000000000[Service] Description=APRS to DAPNET Service [Timer] IntervalMinutes=15 IntervalHours=0 [SSID] PreferredSSIDs=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 [LOGS] NumberOfLogs=150 a2d-2.0.5/a2dfiles/dapnet_data.ini000066400000000000000000000000351464731662700166400ustar00rootroot00000000000000[DAPNETVerify] HTTPStatus=201a2d-2.0.5/a2dfiles/io.github.ngc2023.a2d.metainfo.xml000066400000000000000000000050651464731662700216400ustar00rootroot00000000000000 io.github.ngc2023.a2d a2d APRS to DAPNET portal MIT MIT and CC-BY-3.0

This portal serves as a bridge between the APRS Automatic Packet Reporting System and the DAPNET Decentralized Amateur Paging Network platform. It allows APRS users to relay their APRS messages and alerts to their DAPNET pager. This integration enables seamless communication between two distinct radio communication systems, enhancing the reach and accessibility of critical information for amateur radio enthusiasts.

https://github.com/ngc2023/a2d none none none none none none none none none none none none none none none none none none none none none none NGC2023 kd8mbd@gmail.com a2d.desktop
a2d-2.0.5/a2dfiles/logo_a2d.png000066400000000000000000002435141464731662700161020ustar00rootroot00000000000000PNG  IHDR88lsRGB!iTXtXML:com.adobe.xmp 919039361464473 Adobe Express undefined o¬ IDATx^}Wꪢ]VH!ɐ7&3Lc!pC4{j1w!mow)$JUv{enP.A(hy064UoKyswUs~'@@@@O@@@@!$@@@@HpB   $8     C@@@@s@@@ |   `    @$8!@@@@@@@^G!d    @9    > @@@Hp0@@@@ x       /@#2@@@ @@@@HpB ࣀR.ӷE&";md^ਈ,u}=OE@ `2 yv<"rC'qV;n;]'7,;MNN/@QLjp܆ @'cUBC'0J ^c" t:Mxh6؈G@b|1+@R\IfҐ ",Ht0I@Rǐ`\ ĆhItԥm@hxcmh )$igHD͛7^ .G@ Ʉ" +o߾7-//)"*"V tku<*"+Njf@JGTd0 :6fzli vFЉY}Br @Y9 @`$NrndHdO`eۊNrl޼>؃e@Hp3z `I%":%g5"kqe% "@#H2@BƗD*T1nFʖr)  ?F@ 4oS$M]FJGPᢳ HnHr#@ _L #y .y $8C)E#"uϡm $=Fc?81 ^ *t@@(OGR9*p #@ ]H m֔ErijzM:^³y$  (#%z\DqeE:h4>V<@ބ PJ/"Wx@$c@ Q=OFp,ٚ5dkc|W[UP  (#(Ԕyy,˲y?{>킯,{SUADw~?b+i P C@@\z`RRT*KK'NdY3(_СCcǎeFzrٳ"`GӀ 8  +hjJrV={$3ʍOI'NlY\\|r`>! a@|pz=j fIt|DD,"%@ïx@ VoqVlX`M߿ӟ-y'pYa f@ O,  7>P Hz5dz>bU%,@#3v@ v à4'Iry>m&gDD)Nc @$8b*cB8/zZD.y[D!kr,,,£G333'm6@([䛽@@SajfRٹs~Z6.M!p^Ld9SVhZ IS x#@ÛP@6NOɲ쎉vڵh@@ݞU:L3@hHpDZ zPD!"5ScccWZg81Ŕ('`iu8r  6 @BZv"B*u8 P Ry( m7؞b;jZ@) >xRY@MGle< +J"lOarM%˲[SSSg\! `SM]F(MBQʹ҉HՐĽF ڣ@ E i Fݳg˦J{h3yDaHB i@/~Ń 0]`4˲NLL|a fPH^uffB@ @" Jc"ux|])dMB`C}X^^k.8SVhZ@CGq C8ACK :tzرI sALBCq ,BQLH$c  ?F@`H 'R' #`DT0 @$8"0CR9I%2:@3$88  GQ0NR1H#  < HQt'Gc$AW@ mh (2.I*G! @Ç(@cb<02T< 0A(,@0! $ *>E$  ʘ@8A%G>tNR< @ „4 ' bRTLj @$8b*cB0]TL T< ANw!C  pʨr'% P 2y6 QNP1Ic ^I*.!,@cd:nDMT|1-I*Ei@ &1E  pJ sJAf # @|"BL piQCIGLd, @pJsAK &t@'P8I%HO@2Hp3@ pqRTJ%NR4t @`hCq cT NRJ 1!@@8AI'iƉ 0 aŸ@KNP2,tʂ'X@I@(HpDF `.'0|$_#C@ Qvx> @aNP)LH pJ@ NHp8a 68Aņ*m,`z'm 0 A@[NP64t̒'XY@Hp>: 8Ay'qƋ 0 A@kNP:80p0'*W`Rm۶ܘt 1!P@k],.":Q#X}巳,;"]ByH_׋H ݯyenn8Î&nP6hvGEv) C"S]f,eYv0L4 g\U=/Ev(߳dG7ᡓ|Dxy邢fǓM/r8˲ZR(TlIgƝ=FMv$Nz~Rh"~y&"*uE^7;+JR=8X̜h%XdGz>(*3+۷Sp|!@ϋQ]9JÐ}I2BwZgHn=OS}'_EdgQ#7I샢ը}Fc?xØo2\Ǝ"_DA,l6s d\(>:C)q>lMIg^2 ~1*%^NӀ?(ZxTDnYɓG+@#22:4̈DdS}1l U'Rw#F? xTc1^,6GYE8_,Zà}Ðq- ܊+H eJefB_3|_掳r#9(F,~ѫt[Y 1΄K7II? Lh !LjlK[Hnn[ $Go^!"$;⛫% z8{X[3C%7.xe@Ge5NjZFYz/xcu[la~ DGi.B`09q}~QP4c]wtVje-yGN $c icIʼn$(j܃$8 q-+璫؂屮#z( ^8)[X%$9FQV `J P@Ex -0lA~D"r8 Lt?[>ŔY(11tcDzzX\\8 klCF#Y=ZTVS$6l1. m+|x"X-BԏU*&{}zMpͣ ~ ^GJlY9Vs=ECIzoPV W*7Kwy[Q LnE@udyyvNVQ[YQPֳ+M$;ID> /Is N%%7>P ?yT@Cȏxj\۰1Dr$ 1|*}Z=5A{O'9Hr0%=Ŝ!P‹^ByܢFw؞ѳORSq! $ 1󨐨~misG9زF"Ol\PS|r- $8_#˲@G'=|e{ZUk|hr$ay<K=эhI/kM( ꏚykjo. ^ԠFz2Ivq -x_oeYvl>jLtƫ^\lRJ颣z I[q$8tJxwrcHv^`Z]Ir5GgupJ,ˎvO?!ο^5N(MIvg+ oIhHG 7<%xe%:ɍ]> _R9UTHjDo^檆&(Һ6*}jk%K .Gɕ P‹u+7VPtb>*:w>m@nюr?%9r31L IDAT7w';.ӱyhJxnrv!QFc5A s@ /%7zjDX9~RFu<X#١0;si!Q@c5A 3ɍGRs:G07Gh ~)i,Ql])Һ6*$:(IaŸ3$88J%`{rc0RtSbǙ#]G܉ $=÷VA&:/<8٫B Êq=Hp7@Hn 8Q4ǺZcD:K/Ξ=[u K0kL'|TnqeK/DuBÊq=Hp@Hn 8 nlAb-(q̹ DDG ֛HXk $9; ny /mK79 E2o CA7 t\t 4|οA_;Hl  z$:| 06I Hp0E w(=_7/z'_!D>"@4u:Μ9DdlX-$BڒVP3" aѳ!OB#D-gϞ-..YDv[Htlr nH$9F6sNHFx³>$6D>9M} CD@ E u LE #p+8A 7|lYY3v$6 ѱnNi1i*Ʉ:? +vb#y@ ,i) i9Q܈+Ӯ3AOtPH'a U8 ɍ8`RqNeF $蠐N4 ^G!Lk$7wHIl?!$.L :$9 Hp4;Mr#GeF:S"+liii˙3g|LDzVbIrϣ D$ɍ@D~F0ˈ@ "JtjÃMà oHpx: \x5.G.8kg$g;&td- -j2m@ $:/yT=&cՆ!i`V$8JHn0?V xS@`($"D-%:x]*\Lw ctHn$ Bn1.@XOJqyyI1R^$9 ݵ*@*/"@rc}CxN~͋cJ@V{nݧ,Ӌ#F0)]I4"ɑF$866 $7bGo|BCA^T*T**&CA<uͣ`Z a 1ׯo;^F޳Mq^Y!"g,T*G꩟g5{ .x]5Mw5$9<](UG<+@r`C@1|饗t]Zv+Ϟ={Z^[n=sw.԰!DF`~M;sY)^x]F9kIrCF0 Ӑ C43@1}ܜ^A@W^|~po GɍdC@@, 䰈K 64wF1f   (Ϟ'#@Jr#)  y7$8 E:!N)  @$9ʏ=p#@Í3O`*   ^${s^{dHr#3p@@Hrx`UU^ `.   PIc@ gKL@@ O,Yf=im    /& ↴    76l4q$7"  /@Hp nŕ @@@$A FHpa    /fb c$7Q  Hr8'灆HpM9Fq# $@#h7In'A@@J Q=.(@ `귓H}0~@@HrD#[! k4  @$9JRǐ`\ @@ c! h,$7A@@< A@$8b⢮    H/!GQ+$7J  x @à Ѕ$8  @@Z$G ~p$8ܰo@@E$G(J$8ҋP#&1#  I$ Ip2w&Κ'!  I"IpFHrc$6nB@@ )I>D;Hrý9OD@@ TF.~/FDr7#  I$ݠIpx:Dr<{  @$9B`'~ FA@@ iI(=wF1  "@#H7hErq1+ʟ;wJe1@@B$aH.~M5w_Dd~p#@@A$#z>> ܛ'*("Np#@@Iq㌈h4OOO/@_pSRȻEd)$7t'Y!X ۶moFBXqC'6#34 ƽST"2Ar# @dDeo71qtȰɍ|k-"KeY1/~tdDEo2bubB'7z@Jtɲh7!B2uy.@#`$8{'&tbYeMT&, 2CVC?]Buxx(:QViZSNq$8â$7W  DF;F :kp!M^ɐ Y|s5 Zqc_4EGӘ J#k"2i/ɍDDs$20 ؇iхt£g(r- #0ɱ*t DbhɍDDHd]k%>VnwaŇk P$8Jpg]ړ QZy0*)߬5`f[|AGQUL#!@RDdܰK пk-K ^qiBp`Ghc-[:7Mr,"&6+Sfѩ)} _ @H`&<Ȗko܈|0<@OBL [I[σ[l.x% *"n3$8̛zբRi)$74A}0M{ LU\faG333'-M `s{JewLLL;t`h4>9==`QCq0 Gj{gffN06Dkeesw&ꎣY<'@ :q$F fHp}RJ>=R\wާHnM U4~^mu"CJ ]TVHZVwC t?ɡn,ni65G[$8^Xڞ/J0 "IjPKà1M%лdGP n,lUaJSGUJ=("'72ʱ+[30I# `XaPUdGe\D*`igDD)t#" Q0{bxZv싑1,PFA> )GGq<,F8Q 4ptwG!RJ-[gffNjf< I LvFQm!(D[(@r \ȲI7F '`aʙjzE:n״\ 2-?n$Op=rhXc.& tCQק̓pϓ@B1ir$S~ÛDa7pR'tF@>z]<L&;eQkrr(,z#:@JD"R54wוR:W$$8" d0,$8ݳgr1$DZc_z[P< @`Nt/` S <[&GÄgm, &c!Aa H`-(ּ>1_C-,aĉ^"qHp?+.o # o~@ -Sg#`Mw :10i XGSGAO#y(C`Ubeg"`_D}c@r~!,"aP-|p0$\ Q4Ć+|@l_)ן#RD"R740 }jO0 #z֙H3 @O}]"2AX_U($zPDѩU$8L(z b;۱ם(L4yZ^jh6@ nk_F@ATRO"bgر+[3Y婚8n##FtСcUD& c_6i "FDd(MtݼycG *.//?,"[ aV4;;ht7RDi]pĤ)b S4 tcIDN|e<<R_qTDnVJphtR!yT u{Z^6C4@$6""C@/z_N7{yًA c&ѐv=ySL"lS1%I;,@b#u F%NG{h"5E]jhG]m*fffNFNF!"oP+'A -+""dthZv싉3G=|QME7Hé* L$@'qw'Q3\< #Q$:,GFOR~yl) L"' FYšpeƠGXS&(vΐ@E':>O 6ntF}V흙9灉ÄbmXűct19Ʌ#ØGs0EKv 5ell]+zCÖg:XšGѱŝ+@b#3rXWL<\wS<.఩Y Uųӝ4zlL19@9 x"`kʼ277w)r7HpXyGp9:ַӟdXL(`ۊGZA`$[SX1Rd¾GptБFASDc_GwNHRm+IA `{kPXԇ@; >쳳yOHҠ8:,/c@ i۶m{djjJ'>B\lMɲ쎉vڵ8`,Bu!9ѱ`FSO|SDޯ ֔Ĉ #7Gdž?M#Vm81 h=,>l6e5hܕG¦kFJÇ(x$t#iVm$~P#XHIph Gǖ_6CL$88تѱ;zXt@`5GaKv `~\$ѱL t>ٸSDTD&D@j6xi3$[S86 ᰯvʣ8:6Hϐ:/3"rHnMЫ9Nq̊T\lMHTg&QW8ѱI.͖ ,>yHF)Ʉˁ2,~tA=#‘DA@ zP)yԚ$8}8: Vmq@ 0 0;[SFNs IDAT$8YFْ*aP]@ yF-+υ(:ɍ/U" ?0>>ݻwβ, \Gt}[U08:E0yS΋Eu"BNy [V|1&@ c4TPGATnT"8M >0]Ap#ݲfyxjjJ/Fዮ$8 qtWZS-)qƕQ!8e &M'@ݍy$8 8ѱG }PJ/" !x#eEDnWJWt!ؚ2: 98ѱLFsޒR'T, 0~s8˲5Gٲ2*#!֔2yF$86/X& % P@d qk9lM)ǝn,@cc#X%h G2 D$ GɆ%( $Yosts ql3#VaFM Pwä&m aZ48:6`3 8  `Qqiu7q}}GFZG-)L@ݺo6)>@Gl(@ݍ !.8ѱ!ONr#`2@ SY}vMOO+|!P[SJCKpPc| (=tHU⣩Fޣq5ţ`Е$8 F8:#x*_D)""¿Ɖn! 8)G F`DV5-T礔׋HƠr\ `AV,de&@#y_[U9:9S8)%h2@ NX&a aĉ^RG失Q'wVo|MD&-X{Wz&˲xRKQ X a6R%"4y<,7›G, lUUDvHP[gffNjfFA e_UJ=Wy?J);DfWZ G3 ,@c`*.4-ngQWRύxr iF ,@c`*.,"`qpzJq $!=ap+I*} &?߽gϞ 8X4 @`>UA2b>3I ŵ# zR\wާ1h4`A@@tIUIǫVг`*4 \.`q|VivvE(*j@A U6X={U!~6*"@`U Z )*j@([z~ɲ;?V z@^Xst" H0lhX_q,΋  `[h4OOOm+N8P?qDV՗VaI KaH|>  ꍄg`^  z:q-:e<,۩OB;A:SS$8LIE??X̜hXܸ[D&"V- #Xa8s;Dc$4F[>CGHpgSzNN))KQт܎ @.t+˲FPz+( :kTQN (-|Jp|||={^F;,"V- ;ԩܮW.--ª *R( c|W~87LiO)uOD^<@<0VtT'7Ν;;J^=#]NUyq$8X1U WܮcD@ #?~J_DK`Gѷ4v`SmF~qSw = Yl6OMMUC}߿0-"ˆشiT*ɲLܹs+-#I/W9a#NIGᄙ  #ztZ=yND#`UVorCݍry*  ,dYv[<<55OՐso"'EƪjU$~SI28\OȞG#7\j,nz @fffN:vrrRn_2m 8D:FIpg+TJ=-"ޒ@ +ѲӨ$* x.Wnh4c/yo+????$'F{{*">{Ip$>F~g[§D䕣}0i-u@8_?鮢FoY7&]QVq0cG 17) {#lZdY[S =C@ 7g?y\z饲sN4?>|X|R3rH:U$8pdwwٳeҜa9@X-yY7tr|xȑ# ۋ轝m*1SjGJ64V[?V*k7 R3OϱcE@E"rUo߾]nFٲEdj7֢aaM9)D-p;_n}`wiʂRqyl">y'4@@`8 zVo<:˲1y^**BIf|\!"g{~[;@@(0?Npd_?ex8l}D. jcH?gmn8OȄ飁@@_P`L0T`Th4 ?IsZAcnG58vFQ{`,5In=("ܰL  `\,0}Riv{.iVq48 RL)MVo0ؚ@" \ @pѻR'߿_/㰱3"RVJxtO?Z[ 3߻gϞ Ksؚbf@@k^ٹs\{NLݴiӾݱ[ZűemfԙP{̓{O+ʵ{}SlM3. @6eǎl678+ WD>zhqǽF !(@c@T/Y\V4;;bVbq?@_chR_X4[85Vx3 aK6vmKqQ'Ėw  7Vާ*91z@Gi}$8NϠVba=@@K/]quE0Cx ѿ,u7YJg>ME':?;[gffNl" Q0MVqQVoXa֓@@F{?rхF ~_ZZ{ZXZqZ^6v,}ꍢ|Rqyl(G@ P`?!}bƧ)6j['IpDF)>7dca?%"4.m! (0gEdOبYs$8 k z#Ŝ# FG^XO EJokUEXÊ%rRAy ={lM2$`)ew4 l,@F. U:Hpjq{{'&&k׮elQy   "099rW_ >z߾};ZD& eZ^CQ4E#@O).j2H۲sý9@@o-[@>tPرc_H``(6j3HpIPJ=-"AqQ27  &@]Iݞ%" U1ci>VcccWZg,MK[8!-  @SF,z+7|BȲLݘ{d,`(vaYp#@@k^ٹs\{Nq9rB/--=slv,`ʨ>t⢣D=Y;@@uzٱc4M(ho(6)@æn my ).o@RFh]`wG#ouJI+SD9Jۤ(  7ėMb})F IDATw ^='lO*+kFJ@@ %K/tu]Ұ/F{;6DžC ,-}'X{8R ) LNN$8ꔆ=X}*04T)@cH.WJ=("zÔvXaΒ@@/۷ˍ7([lcP ={EJL m*&ic 焭zƮlZdY'YU8  o*`*tVq,TkZVpMv p,;_n}ѫ&[I: x"@xOXe]J 5fK88MdkG`3]-K,L@@  WO=T9M6[-4A -Tw-}OqQS2Ԏ8  (0ѣcӧA TW HtNTO&ԦǕ׾+yYKD  f(0PQ7z{6F#`c{ZJx??UC_WU^zʹs~RRDeYv[N;@@G]`GR߰VPMM ҶXKVel_Y<55u4Hpd{poHhʯ"[P6zFߛw/@^zJ뮻5y9|*"_Y}WD) Zڦro }HpÁG`i{.wez&˲|$pNl<3l?I j~ꫯ-[$':A1R=DtZ'8|],ˑ#GD-ii/cvlH)K,mOj7ξSqJlr|kQQGNnA6Hg-? @o.7xJ +"UJ=™m*.~ {؞b7$6:EBHntǨO\я~$˖~R` }R_p5ئJ:7L)\." }1 t 77+fKFzˊ^.FZ=$@~Vo<E. Q764̜\t%HV#Nz{ 3([[򖷼E^WAdiMih:SO MLZj.HRp\C؞b.$6Y^ׯ$7~W~O- #@8ӧOMe_4O!S3(nH 577oorcI3g!P vG6߿ cwlO)Hbw#P@HE!1!@]?!R!yT gG@؞2:Nn?[7mڴ^GwN\ 袢7pq.mY$9\dhe]J)]Wݞ%" gAL_"kd zk"2)ƴj#2oWuyW^޵s%ftQѷ+EEc"cT *WܹSAoI:SFE+6mww @KT5-#IÝu)OzCD>=%UIn^W˕W^)/K.9~ȩS._kI<7 RI 7r$l_$9b.cC~_رC&Pk,0z|V)\Y衰:z. Ge=Fq,x`׮]esCY:j&1d mv~|C'It£7a?T쮶}ﮬވmkZs$G߉ P`gEJ/W-mSaGp/ 4j<]=66vez&˲/yK:/'"Vc֭rUW]*g};y?\sU~|V*7z Ir؜Q> P`t,0}Re΁}X^^k0Vq9>E`_:7Mވm{6tRCп7m&;~w~gem7n;9rFc.,QHrl$#@,^>,O>dgYnL=R7:tz޻YUE& AC%uvTj;};\H^ۜ1ٷRݷޞ\(fGC#b" <Ҁviz&k^kc>jUco^77zk JZ1|1c-[Ϋ4 [@G<|7;5)q($5f͚eef$!C#hdgw/)+h>cdi/#e);v]v[f M>={tHLC.^fШd:88t{{{᭭947+>M2u7łsҫ(%B)ޠ4!*05i8_Fj 'NPgg'{u:Xo FMWUriD" xK,QIlcۻO>$Ě3tiw46$G{mm͛7J  8bu7~USğYGD?!C;h&6*++) ^P*볈?xAъ`Lgˤqyʊ+hٲeiܞ=1100`i޼ 9|@owڽ{7ݻ= b!0Z~MY<HhP1bv'7Zuj}8+W@hW\FoL1%=h0Lp:7 xe<-뮀HCc@0psYG XnBrtC5S~29}!ݨ,%>"ڐN' /Mr GB`ߴ }-5)& K&|g.`\.0$;F@X|4nk裍F=lI"zFq&N(ao Ka<8ESDyњqI}D "6m!a8(Qf?8&MDWh.0/}֮]K,4V F{Kz_~eKLN`p?!dqq63ٱAvHFVõ?TLUQMDh9`?o78)ĆpcdYx%I Zgr#vQoxzޱBO FJ`0inB8 _Hv 扏7m4́iӦnG|"GDp 283?ܘ?G槤eofϞMV©('&98I Π]`S>M> 9Sd:{ *ǎ +**ZZZ5>,-ać.ك5&kR2 ?WSS-[X` WjR:vQ=Ha"QghϞ=/Xon*űXlSoP䄄PHhQ]4>͔3ϟ;v>ΡC$?h3FcQxFg* 8|bB"$I*M QPQ}tFƌɡ~x1E$_hy &BQϼB",Ï=QetQgǦ]`wo|n֔Dx>@`/9˽C/~QG4M.W[,( >$3Rq .K #szrh&8 d 8e #0uTghXY,4*'0Dr|MiKtp&<$"x~Gh\%)ߍ;{?0OLK On SAj(*++9 9 eO!ɑƂKAr@Xx" ӿ10nn\ :ȝ;w8rHPqa4Ms)C1̺dsEtOڼy:bEp\XOH,(S\tQlts86~phi"0ʻFq56$B B&.LΘ1jk9zw7kyt+w>b}'!dǕ+WIvWA`U`رcXQQҒںւlD(VdCx`#N!,lC׈ fr@ȍ^x绦i>,$,ZV\c`~| 7c%Q{ 9dB2I[{!a h0i ȍHC!`G߹T/+1^◔%QYcƌ!~@r3;3&pNW(0z6 !sqPͥK|d;lN=8#j#2BܘMDrXff4ﺇ.1iҤˏ<`PJyMq~q'.'O%#e#3gke]C #?FfI`艧rVAr7fK&Ƽy̌$dhE&;?kyw A`4hdWHv 34M ,wYOGNrDBpp ""chhrMM4Hi:bEbFdzb, ,u1HH_QʷGݻw|Xcʔ)n:B+F ^c̘1.]2/_wƝ >\1" 2Id$t21gz/ڡ0[ dbCucE^g͚e :΢h!j␏!@rE ӊ555L />9:d§iϧիWS}}aP`I"z&KA3B_׉hgxDunw0qq*go&:8cK- Yx$G4cx PHl(fBFɍw ΞR(0"(U&9kkkn޼jM*!%5Ki) 2. ޝQX+ci5\ojArxw[QO&viid!#σw 6co󎧒~ٱb 뮻D d91T$GhGkkMiJޢKwwYvRu%T~Y-D8Xo?^TXHPcB KV~mKK*5>e7gRGG։L3 mr`d?S}}}?YZ[(G^w`CSj 2Yʌ. &7܁34QgmZH_m>&d7~IV#y>Ê!mfiB$oho>b>\3===V& JOJpxu̘1i˖-UT(dCt7@d莴Qun0's0qLr$GSSERyG,ƁB *H@)zLoN# Qgo$U,MC;.; ">+GD}{OHJ#gr^12IQQuHq-BK.ӧ{= C 0! 6ϥtԩ$b("" ,'EC;!9>GD\ﳯkhhrMM>̜4iGyd0E֟ IDATf&%*\{Gh%Z_,DކoTYn(_ C5]8q*ԙ>`O .3q­;w8rNF!vYCO<MDT~NDBѯk^G>{gDsO]re`ر 2tMp:u77 7J' +G쭷޲jr15#սArFt"YSNT/:&*UQg!07&`Zb2"z8H{V# ب9c#+#Nݍ9sXDEAn.:fQ~ƙ9hp3K,*H@ǔ@9/u.Њ(B5Lx|5dǯhyFӚš){ĆSiWUU%Tnw%-spGp/;N 91$Nta[tȠ|,>zwٲe4nܸFS<)vn #X5C!W"zhklrЄBOƏNLaR$YfYb<}}}VyJww7`r@$G#Z1{1 7Y-FYY38 mX-C vKD4U[lK-_4[@ՊY E+F e*"{h6B@rD?fJR v }J`0inBHj֭[k?&4ES@uuͧU[ !ڝkchm}t@TԙOi)^I,@+FEHx C5dmA+t,y3qf9}uʱcdWbEEE[KKYCnd壏>Zh͛7sɊ8ID34doPp (MXp(Xh@`4XTQ 9T! ;^j[iŊ8!+p! }wuuў={_t'!ę܋)@i53fܲey0)bȗp$ %jjjGYbT5Q(1Q}:Z1gϞ7fnlrl"ښP4b91}D&Bʗ͟rCw4RnLj11a:URjp j$GP0 7-1e;wnj{zzXhTm|WgMYOSQMp4 eHՃҔp}6QBUV jBfZGP_k 9" 71~xK+"*₀,08eU{TMp8U $6!ĠB0 ]¢(M)4k0(\)[Guwwkg$G>-i"3f 7ʸɌ4m$"_B / JP!UɎ 8;.;?n8q"+<Ӏ 9?ɳ1vM" 0|llooFBp &1JVGED !كũ) dDE w Q nh 9a2H/(O!LnX/^,Fhܙh(SUT *9AJ|]¢(M)>N~;= gFчɡc@rDf$oa~__7y2"j#;7t:(MQKFgg²Fccc"ׯ{.\b> M 9t!.Ho8e 5L?*ʕ+ֿWTTx>YB@~B@@Te@`T&BCX)块=F/4ˆt< HPiC 0 rC΍CCC?ϝ;G|ɍ}n'Xd?JV1DD !WJXFw !4V%Ma-Fa*YUHUUSa(rE#{)#+)!0|wW8M? 9c4Hh@|rZd 47|8xw̙3Z?4&>/~ę :X~ O40Ig~c]')BKB-eZ"&HfaG=7pjPb#$ի cgnAr!Ar7Iqo1188H*HRg._AGq1Yc/VTTUIqC0@iJye4`7VZF'nF2a38rß ={:+{[nA6Wuuu_7dk ri#{fwY4 0|B`T8HWvɡX=Ln ,RߌW{~m (^H`]%! 8:ZaKSXsa׮]t&h&d8%+t$+DZ; o,6ʭ;拉K~/O&Bv0X ri#{¢\{ɓ'4NL@+F $G9;KzmYe7xcS'n6u]wѲeˠ 04 r0OhuvSC`9& 00Hߐ)C)4F}}={4{X1Kh,8#sڵ4|b_yGޱBp!79VrnʒF/[jI!`ԩSiݺu4gΜX3-kc47|3\n&>>BP!79VanbF/[jI! Rvpuu5X/^L|#wr^j>m4#9Ap|47CZ`քEm cY$»f!hk<._,YrkBnz9Ap|47CZ ܹ33¢6u1w  $Gԍɡ(-wDnػA_gϞ:zA%zq6rW a0(@\8Ohdo(3;v&LuvFc@rD81$+23fmGzBmv0\C\0Ohdo(XV7 0c  $,rArhhpbJ'$ܰtRn!0܊7xO3B34@pȹ ~F#{CQWWGMMM`WVfOwww=e 9?,*~zH),5k !1 7!! rPOhdo(]veN{5kEp444xf9~zĉ<ArD!*V37 warT;w8r,Dhb!r) %0z22&tŮHh]#Z gC,]3g_iY:wqt8ʸu7^2 2a 8~JF#{C F8{>!ŋF#$G-N3Q__O{/t74?% _7n\\utuuў={_v]EK"C.0@p'=ZG ~kKgoL8*OillLk… Ɓ|Z@rD٧NJ֭9ss˴{nz7c8 &{"Z1ETr!hj*u@ֳ7؟&Mh:k$G-g?Xw!78c暈 +cX# 8~ң%͔5t 0hKɁl@rD?o wrm~t7N<ؙ7o^<1fP%0jA47 !^ CΉ 8)D$'R,!)v„ R7t'Ono^7Qg 05$G~-<;JS m;ߝ=UDbEEE[KK Zݥn 8\ C?*SP~PJ| 1"uJ`: $Gt4%\ff:DD". r1CU"{ V ,oe#Z8cFQC`F!!_1 29FF`| Uׅ 9u(HpGBy4OLٻwojDE w QkMa"(Սٲ9O/hU 3JE#Z8K*?~<<˔)S,ѹsQR۴;Q}XV9uލ3$GnjKS:DtLC!0|p<%F%poS^ 8ˋm---,4BA 8+9Z(Oqv{ŋ5\0e@rD4 {#hVuChC`TAߐCoHg<b1 7<Hhtvvihx;_ދ FtE3"zi&g 8|CѬ/Oi8#Rb|@9 FeJ^PR'M {96 A`l%L|F!݋ rVURw@#Ha@+FRݻw[In(=do\fʔ)n:;wnC]!0 Z r@ïh4SZ7J)Y"#H8z%8? "7C*@pǬp9FFyB0]LA d8 HOtFo;fHE o8cP`I"zzVg!yr&871UwQFzA$\KYMzEZz5xၖ*$dǥ w)"X`!!3P9'O7͛PODF37q*9o4 IDATxX(8;oKQA֏›LB$G |Jj E˻ױA8Ξ=KDL3 mrX AP>~F@yC&4F ƜczG$~XqQ@u|/_N˖-q鿘8COO1#ٶ>+iÁo@plrߒ1YWWGMMM`371M-!qɡe,2y1P4Ri+I 09a< 8~F?7_rE7eLΚ5"8d̤z,Ęsȣ)շDn$^%M)ױN}CFޢar!Ck r&G2g&4> ƜeMoIMZ)1αr9Kȑ#7"R@pWHpH x_#:ʚܯ-zpOPXr'B:|gMTMRg@pE9ѪF'Nh466*XU:MM|_ &BY$M qQg+,9PdX3 (1@p C?}PܤI,fQěbl@9_u(k+alXP4᝝Ŀ3Pr\d0pF@pȁ C?'J Ûb 07ϟ;vPww{-7TgT%8U59ydZn͛7/܉2l&i^&9&87F7s?~vI'NPpЋH5&yg_+|`UT[AT q4MBW"v90 8C?euwėĘs Bx!$}lhV$s< -^^hkii9;V6@py~Lp𱰂%c eM|1F ƜY?zde$ #ؤ'htuuў={_o"~O|&8)c _hуh1Fu+~]G+W3gRUUUĝ@ys0/ubZib@rn!gp|;/0ʙ ܡCA1F8y9nP~]G|q'8Pkbhb,Yr1!-02w'Ԡ4F8y9vP~mG<g(z?\1g \.j@WaP9ApH'oD =/2cY?zD >x-wUo\"SDyJ1> ĜcFa{%/s R@pH''80C(3pGo1Bs BΜ9g (7pOx n(F]؅,$9ApH''JŒ%zj+4-?W Fq7; 7yqk(7p·qs}@`6E%Mu=BB 8$Sӣ (5(?qgwwwz(OqUcʔ)n:;wnBV24A`4\a6@pȅ Z[[i9 2oرzXE2Y?Z _ K48Bj|.[Y9ٳAGڠaBU"CMS%0ZWWGMMM`+I0h)12cY?7$q 7{-GĢ<(O)1cؑl!CKB ~+ keALBKؽqyb%$vs 7q}? gB@,,b>߮(ir’'!Da@!CΡ 8;Gc)~]G8@,=>O(irv’&&zApyG@T2wb7u#<@n5SZL VU/-2{(ir^Ց†a4MsȜ@ 8G@˥*xZy Y 1+1;J\'P.*gF1p01bEEE[KK !SAp@G=n3~@nǸp٠Q(4[La@C {R@pO|̀&hĉhcccP#bGqm!✽Rxݻw޽{*4"hG"oC!#CpkmmiA(=<c4i$ G Һ_Wz-e=^w UZ G!0N%0mBl@@ 8?!DE؟~m"i縁(u['qxP>YBz 'R  2Lr?! "V׆A=/q = rCnV㞽1eZn͝;m+Z ήTHA`45WMz6CΗ 8''8 06~$Xu4U) 7X7x#Ϸ }+Ki) 259S!)T]9 9?➼ï xwpH 03(irC= 7c{GtIb5<:Fi)W&wbu="@ 8#bp FQ@룝;wRwww0 Rٳ-UwC`#5FA`E!: 8&8"v$NmqdxK  7 |C> b"18 fZ %ʚGGqyL3 mrX r[C@pxCHP7v]< 71 jEE|M3I^V>YBzQ===V&cmv0(E 8|غ:jjj \AvC0r!0)ȍ<gj1'D3$ wi)O(krw˜; 8@?UGd&8| ;!9 Ŝc{C;I#7)wヲb1  8BOTRp#1)3uCrɌN"7W>SrzW Ei!0"s+!r>B!"AD:;;<NĸH1瘁5;ّI$7x8 'KH59;J(-FrAdt 8'~B{|ѝSV^-c"c{=8qDA߰ &uC`4ne 7B$I%7x38 =n 0ZʚFa@`E!<8"b@?~ E FEFP%A=EO(krw˜; 8?:JQA9k !r^*{'{4@`#:Ǎ˜%"C|>0 8Q@Bą%m`=z^{5]HF1sܨ5 iG*z!9 Dw(s !V$.4 -BFAKtIkxPuF1sܨ%+**ZZZG*z!9 !s(QZx;F p+ cUQ(>YBz@Q c &LrVafݡ =<^ s@`^pHgT#EXd w')oC#C 8|⧂/|D,D+˧dM4Ɋ d(B 7TF u]pUgɧcG7ܮ>Kۭ+\CFfrݰaVoSԩS-cΜ9؏M(,ij@p ~ 8|k 0UV)@3&oɁ r J@Qtܧ eMNRU\ X@?>R5u"cm4`tQj-pDHP7 K 0E!>,]NpxX ,C&@nD,)SRsF xg#`U$=#8>+z!=Y&8P@u="r#H (XXS>Yz I 1&z"C.@pOO,2aE]fzY&8P#u=@nDY"7kh)G*=~REq4MBWXe!}>kiiYSQQ#>ueH>Ea3aI 0pCn 8œB=oƼH1g>}:::رc|"!1Y%8S,Xhz}bg 7TF F b1#bGbq =b9G`)Uc1sL(FWU `vJn@,[(6a{!fܝ(^5zr?>RuΝ~J7OC`NYr#Zg`!-pDups(=LD۽E)zh!?YX7 F2{ฎNq9˽=RD9AptqQALv{܈Y'7}wAo'K@o8;I{X"11 8|+#z* =AnDeWx8TF} 0zdp@pKAWB'NP`-&?YR6z܈ 7>cu/u=҉ ~ mkk[mf4522V^-c"c3gpϴ7dox0NPU{܈ 7Frx<{,uttѣG;ԩSiݺu4gΜ43A`4 0*BFp&;Z_#mBAERmF1}Kf 8wE+8{cѢEj*0aBdo8AE 7r#Z(Æ ֽ<~G ?! 3 ڒ@kkL)S.! EPe!^#DTuڌ*7hdaĤ̙3wAn=o1cefxd2~#@a@`4[/DD9dzBB%ZE UD+j3 ZĉS!pYB)frcʕiܞ=kb܈U 7J=oqP4ۄ1B17x'!Y!ĿD4Fъ~'MSMppy97777Jn@R%'@X4X[F܈֋ 7[lช9^o CBE(zx 8ID3T\.ovw$ 4 L2}㐒ݐ4aQqPؓ ҥ`0*܈m 7q':Njv 8h&!+"@9<<1;Ij&xQnmm|=:3GrS_wgIਤ{$ȍh rqWqsF s1oU׋m---,4WDu)XgkMU!S_֙hM >OMG$@nxʔ)֑s6 *$A:O!D_)[|>{D}DA*U a!DGŲi*wTH|j \ 7E `%qȍhr;zu_6mAK/[o_2FݯcQw#h"}hmmbf“TxqGCCEtHK q(lȍh r 8>αP`0mBlE@Sm]]] >%U;TNpDZo+jiښ3M*o„ Ѡ,6 4@TԛzpC(r#Z?o ?hь9۽ |0 S _ս+^&ڭ(U)~!"RLp/84o$SR-ZD+W$&Ђ#DK 7)ȍ`"o8 F]K.Z[[oO *}555?_T"r֕"جLS 9ʇ@kkH՗w%L'[/Z߿߭+?a܈a 7㏷ް: 8Q(0$= w#ZW!"gǎ~OTR" !6T $---k***8C*7hHb NL[x@K 7% 9q7Np8a8Q(0yDfr3QWWTo0ZZ^nZ;002 9:kpikd.FਫS-"xMZ }DKɷ r#Zܐ7 NpL::NG+F,! "x\.wZ6 k,S)$9vO;~a|knnnDZgyG LlTVVZe ݿ^{&]>F^p:8qgRGG=z[@e"ڜ^2@@ybDt#q5phY0!aRv,nQv&9c׮]tq2Mebgw?yZj(Xw{WƁ܈ 7WoXf`+V]wEXF%{%`8P@ zkՖh#8\*Clt3GiL6m_V:pTk=!Tlj@IJ w,b ȍhrC- 8e1H';mmmw4<Lrhtfoh'8B(tMts>03<<|9+b!4merłܗAG"#(rя@nGz47cDA`%^ Ef9VC IDATZF;Gkv'ALr ?HaoPuT,F*Lriϟ3$1z) C!2ࣦ 6ТEOլ(OqsYGq0mBl5@%D .?abCkىGĆt. .XDL\7@nxIW/i7Hp<=6 BH;QƁ?vcC#8x; l"利;wQT{ 7ȍpG7Yc?| o<{%0zQo,1")ϱRE.;F憽  Ղ-{/=@E˞Xbdo r#=@nDm0?|u裏 Hp/doxw^OOORlO3ߐD1 2| )@O, fa#Eh s#ڨ- 8!' {{,=UD&!\!?Wkz O!D(b(=,7)]|c=i)S_TtPWHJh,uI%.~VPWQR)SЊ+K_UUB0&s4A%KPm-%yA ̨388:sug֞)9ٸq>QU^"=Pdm>V0ds 0M(t 4ɍk .Lnu]/~Ǝs2>#ȍxҥKn!^ j=jbKҔnzYu|g}vL___uи&3 Xg.erʯ?@t\EC!g_~ڈ= ,XADl%FJ>z/^`{17/&&8@n 7m[ozA1m4"ھ}; F}}={ PE\rRe`"#Qn' :6lĚذ{GDp˗7"bAh xKT 1zS 8w3EKc`wJg/i+S?Hx=)n?s訬??._fZh!PD!8 ]¥+ㆆFLt AR%8PeR,9ByOapi k Fȍp;ۤI+_ r-~fT&|SpNfHШ >rI#4F<G)£ר쎚K.fz;AV 0pY ͞=;Kʅ+GFpdFܐA/ܱ 8e*|ZةS'6rwn/;E%KE% r(t?6144t4,2cҤIvY(ipJr/7QR{0aUp$o=kѰ݃F̸~z;LM{zBTI*zUUU?SsrɍDFaFGZrr#iA|;r gqzDKmg ۾6_pbȐ;ܐ0tMl2B;&-䆾HK&KhB,g#@p8H+-A]e &BQ[do苹RAnpTl0$Rr#JJxI?U%OSvZ A GA|\ oṖ` ZUFamɒ%t}r#5Mb 7ȸǁ$>_ tI pș"omQI-G# "ES=˴(_>YsNi/}֮]l!ܐMChTp&9>gEѐ oON n$8bP"#A#N*E|r"! &">NZhG;vJF"?~4Pi!4*}h޽tPpĺJ?>}(Gwe` q DBp|$"*My*++[ee>b8vj$N0)E1&=ƌS;<<\3444GDv}!7.rW5a&%OرcVɓ'Jȯ~x.m܈ChT {n+chhH{6SN^oQF`%0a@x6 !HDgopE555graDg 8 _+a£oܥK~@D֗ !8T&u#@ho8co|4Z@n3. 4|o?OZ9쌍jsRKcpv}Zj[a\.K)#! ){ʧ8cGFH0  m2nx ;ͧ(X"+R}k P%#t8~?> bP\ ð`gC,PHU{-&0.Gc,R6DD !7\.wFhn6mІo96G]}eFb_(ˋFA7}Q *^ 7\!JEo֬YC_R8nt}:u XKLZ?G?q_XwFhۖFǁqQ)"8!5zSbzH-4.$毰zò o5y!G7@pxɭ 7ׯ;@@ YK_}awa4Msc<F a>f 䰧ȓFDx܌#b%rC磕-[F7pCW%l#K/[o% ‹m---d <nEdGDV18,ҳ@r0NJC.yC z @f@L%~Ǯ{嗭 mvB$WUUS':Mښ3M59"=Bdž™Dn (MqǨx4ܰa-Z( [@f8wuttБ#Gd#_e4Jg4 ^xG/CG|lERwh2 ,4bJ aLl(?pc`=.Z7Cih"ڸq#? "CeرcB5s}D'\ApDecʕG`G YvbTPzȨ@n,#PFbOiG@ *?r_/)ވCX[Sz…mr3vYvD}ʍ w뾂 pJ/dqd^>;VQPq  CB܋ܚS| G 超8eqpOI9puJJN`o<%#rMR|iΜ9I]~*܉[oիWӴi^,RLnX_L(C6 !K/J)#gZbQt.29"VRJROVam fQ&<(g$And2lMnpfŋ_r&'F 8h&!wƶ !~MD뉨F"FDۄ mT3XNT=p) z kpTVVf&@n} [fc)r#U-(2fСCK[Ϝ9C艷 XQQr ;Η?(A*SpR0s[J!e DlmDhAnǺL]' Hn?dqd{裏>tq BcAo2'<%ޱՁ/e*[~?k֬L4Ϟ=;) 7F~| r[ b X~=qɦLo$B`)IxG<|Uq-S)E'QB(W\!~Paɓc#nm}~pM[ ?t(bR :#>>c%/_ݻwӛoI#\t)-[n09<"<0 .Kٜ^8uP:䩛G\Z~Cq-SbdVJtd(;`bOa*I'YUUe|Pr#S7 8RҒbQѮ.z7 +q>۳g.ptommi?Uy8NOxOhYUT!7N5M 'Ll]]Y'JEuP@nu- 6ТEHBv~㌛{$|n# JD{sSwS 8!KJ-sns$Aɍ?ϟ'NoRČ^ߘ1c>Yn 7}{º馛R$Rr--dϪS((OW%u5 8김NJJLvL8TJlR ; DÊvQ>dܹs-ӧ1 7Xl pE!7xrār62{# BD2M Z."_ѧ&L1v%L6+q-a| I  ?:|Vq[dDqYmmvOG񈃳gRGG=zTłbaU6@pd,ZDtp ( … V ghP &:FMvfؾ,5 b%Y 7D 8!銏 xAOOܹ؏S~FD !H2)Z`ͤQt2a"N~I~``g&3&4Ы?2̎B0h."droW+Rt#ۉ7>g>LD} ;SB:H7Ln%8.|d7q"<pVƤ g`Xn˅h;~c عzĂr#qAp4$%7dqDΜOHƯ*OOpT T2PQQhnH7GBB?wq[!gqr-t Hn|&ixMFUa[DG4q۶mX@SxBD4T=# \aX@6b&bP|ƅ* 8if0暦Y0 "$ǢEh4~xF"ܔE6yTInAtEYoS #m-i6Q!^ X"7 ^`'(Zw@tw[ii 7nW^M3f̈)eASJUAt] Go閔 Hl@pd4tTWW|Z0V6 ihbF<` Y@ Mz 7wieCV\%V`d^|ݻi޽400 =q7xSjfss)HQ H-XWJmm?n޼yo iD {ye]:e5A\ Ȟr{m@@nbTK&$bL Kֲ+62ئ"rؔ E)Uͫr IDATUuyK@Э|Cvυv *ox}r7¬[LMy[" ;O}x}}]yk#6FVqد|+_}{& KS]/8ªܦl/ϲwDζenL"S[[[?_j;U Ta'СwwooC@Gߐ72xz}ᙯ ?.^(i=yYӵ|' (8m= ,..tI m*;dۊA偦z+6tvUviCn`CD_&EC:G׾f"cQ5ϟ2 a}̎6[E)lO9P>#;s.\*&%Tv6cʛ8ct^n'ctg@?_egs|np_"7~Tp?r0?,sMDW^^+";!6Eb O\z?{RvnEɲyw8Ӿ Yp(C}++ݭ+>\NW\{,yD }ϗyd17 ~xj[qheLNN~ĉ:W }pȖG칊cV}z!n\vR~E08J/79twwݽ/"F0 :L8phAްt(Szѿ#?6KD!"5zkvo5ǿeY?y֕j ~PԎUExM SSSGܶªf\\cOfB6+0Ѣ`RQ_g1[ϭXapQa{xS3w)27KMe^l]"訦cQƫ4gq(+С??;6W)HK$( [Ro%eooQᆮ߉L!e{Rz@/S.ڦrʎec_1pJ."Fz59#XaRbpt nEgl%I8;&JaU7#Odf =qZ^Wo-nA9<ྯ^[,i~%*@hw7OOLL{nn8̻Ȳ繞̎~h-(Y:h֨]}x#:-+*=z{8(7^/^(ݿ뀃36)71$ɽ[E8t(/ˆ$oF.ַg1}ܥBbnK|elb=*UJψȻLޞۓ 8|HIQJ/"-IFV{ESfmyƒ6+>; +4*Re̴htЁ9v7u+]uW^K.ɏ~_zņ/bL;G \:mj%[SD="eԺNApQ&S4rxLLLtUeۡGFzJNoY\'β<Ϸq =ȣX]u'C_[[[}-Fpecr⪍FJUqT[n|~kk?~RJY9\)8bcSqݷv^+5tP}Y2Vnᢣ~ p o;,}6g8<.]Cv+Cu*c+K?|~ʁwC_+;ʪ`C^x{vs=W*A.jw%@U76ly#"nr  il_z4-)0շ*ݲ\ec"GsyK88lMF~pk>F@@ "Vmbk,_h0AclFcj1Y =W! q Dj_*[7,grr.o S"0%q;* z^   Dj_78\4^N 8qϕ#{  1Wmz<18LeGb"  06 _"RYYDL~հ#Ohr⥧؈-~*[UR"ް?x̎X1WT/>w\myyy>iF58 '{}BDxͻ4S'O|p4G5,W>*"J)uViw|x-  nP߿hPu5lRSN}7˲miyZV~[G @bȲ\:DD{!ΰcu`+]?[@2C34Pވw\\c Fc'pVVx7 *p^[^)C{a{כz}gCBm6Y1+@a7-UAW t@ ,U,,,<Ƕb%XKc,l[Vq4,])U@WC%t~VoI p ūwXU6R( 6^f߅ҽc5JVиot:Ȥ&Ͱ0z!c"rl>@@ :%nWoHZ-J{JcUӹsjyψHpYafsvyte۴  ,U*lmm}O)5xI{ Z!"KzG`TQx߶R~ia,0 j9aͮ6 $%X7 #]%@[aoB<w< @61E=7ئq68gq?alXӠ  W65XX.G7=lԲt,g1sG@lGpl0Ikv8&٪ojY،bJ` `k[ﱟꍱ i#*\|CJ@I)>U`_XQ.@Qz 8縣QJ|ZDn-ޏ Q|+aé%7©=!`S{0Z-qu}c @@ȟ(~P*7ApU0:*0䪗J  nx];x 86*aٛȝ"R {4@8+"Vu1˲|XQ">Ic$6tUYojj鍃G@)O  XPQg-Yڵ)@aS7-xl~tfff5a n1 ҃N# @eLNN~ĉk5z#14\(-~3GiBRJ;C9cd:ވ @6^fkJXcFX p0 XfQQJ@V, +}Q@ZCE& d8xJE%"|A/gAB[<9]C@ ̬xt]8V,UVgqoȧED>/@C`R|`kk{JNCJkHޱ#z88QÂҹsjyψHx x,@XQx-@uyUܨ4<7 )AN[\|n}P*x?mjoZXX8Qȝ"RK# YiZܘf!mިT*?uԩ27œ!#jW?_l6p'RJM1Qs@PJosFј=y01862I@7"yݾ/E-_0&M!pǁDUlϴ^w}DNOD@p:(>#" SYS3ǰbވm*OOLL{nnN҈vy?&"^tN  FZwEEnhK"rwպ᳆DiGa*^hZ6ZƅJ{ 3sz“@HW[{2W+!#*EGKT8h4 9-ܾCB@'AV[c5NUܣئeSSSOLOOox4Tb@U   pfC)u|ID4?p4IS p ū (.Ȕf#"_VJl< t@ nLnO|><+y8"/rX@I@ H`VX8΋;R+ 1D8<,JJ]RJ%q0x(+8@ !")@Qgp8QN80,\KEt#H @knA9lgƩ'Ol=A`(xiӢi 9; J " Fb­RJ]Nz"@I!R{EM& ")B p@L n = b5!qv(򿟄׈"x-@uyunqqNLzFV{EC C p ŋM X8Hw{M)9*]EI`Zgaa,r:F_ ;wv…MeSSSOLOOojv(*@QTPJ}BD+"mC6Cj7 @IKJ[[[SJuJ-Y@)uAD vfљUm8 1"m v_  p3b[Wkڛ8IVo,; XɲϞ8qb-`n@"M *@keGWݞ|AD#6ئbP pbE&,ސjz dTmr]?z `Ep k؍ZIEfY'9l4Z 8BXzCD8`4azEi@ `g6nRFmw `b8h&ߙFqɓ/;^` OD0xc 7 V3[Ti6٪t%`LW}9 |^Dns/˲ONN~7Ckkk; 9,< OgC?*XW_RHKD~ 6 Xښ$"wZYTRV} !@ "3R oOn~wx]n5-J{{ p09Xܚ)N*C9%#AWp 2m*lUxqxo![St=Pxр!@ r lcxWqp5L pADYxX@M^{NMcDWpx38bbb}sss p[0:n܍.@eLNN~EØ }b8!oš? n{,6!"q ΦM۶yFEc&OaB6@< w"}x jlu IDATsYW:K鷃/ (@cUKNqK"+F1{ɗ F H Ep#Jz2sՖqult3pSlM܍fLq=Lz `SRlv!,oBE p0?p5s7ƪo6) +&5i @,~Ǿ Vm-c-,GJ67;;{ ԝ 0(HTlZ}VDgBV=fy*AL;I12]8oTJ}[D~ g*S9 Q)4ÌGձOOLL{nnRGZfD(Í\nZaRRFgGVJ&"_=)@~xcc߉H3] F%"EF|u,7R@CHF馭)lMp:©=E QD ϰUQQJi1ZHlq{ [S"3  L`ól Xܪ6+m `񖶧5Vh:<.]C 4(FV,zTtմ;"SؚdaX (@)IA `K[UM%9_ 8"-R""R74% i  t\<@aԉ^"D*pR=!poJgEfWw*V ޗ"Fh8鉉w]r4H}lOqYA@Xcss<Ϗ"R kJ8+"JzY%"JE%" uqVqaaᢡhO a6V,,,<LJ-8w\mee[EAG\e4^qʕjð0!m*4 Gumys^5??B\  t9`J\d48PJ])||Y)nM*Yx%\aP@766."}%3<v `f XJ/; ~ "~Ϻ+ ǙFqɓ/1ތ 襥u$;xf5V,IaR6JQ[8FJhؾ^qd4v\ "4$C>5CS5 $ 5AG]D>ġָiK"LR9m(.y X#iDaQ-gG!!:.\MLL4پCEC tWkT*+:׼@R3".(^FqP#e ࡤy~U>V>! $@<pY;m!xzbbsss"bH guxXgk0 @4Գ"rUރMM{=ot:o Z  G3 @!VubE`M]FB:"r Z޲\ey@8)0i4xb̪6i (,x! \j\r%oZ|0.+GQRȗL"rT)!"5xR^k~~oMF 7)tjV{T i  0H# j[{F@0)_N7Fqɓ/AG Ƌ <DilS1IS `Vì'E+@miq XX!Yrr 'NXS-QpDZ{ 7h)VqD:_ vVQ3ƘzCT)pX'.On~Az0(<F .@z?"$"wZ$##jۭeJrVHf!5\+ 7EʲrF3#κvGeqJ7Ȳ}SSSONOOoD@ !x^[[_M u{FRyR7Ǐ:E($`3`{J"wp-mS?,>Hqpx'vjaFeK҈ nz#9Gy-nS!|0<Z`,̎b@DD6*8r4 x-`; Swc|C[mSyLD^:6vΒjڐͫVg:ts47C}ܞ,$Hv{.÷#H`.1D_U̐=Wgn .(`{J"#BVqb'H g[[\f 0M pCDSsss+)uH1*Da0&[豱eYv,sk&ܨ V 4 8 7{#܈h1k=B7r$4*8`[zD:f\% Ebп^BnA9!0`k%w g?*=XVr_rz Cf0unh<@FUnbg7 Vd̻@. `Ħ Gbw#p@@?O-ٱE u"z"j_1`װC/Jjv ᆈh4>7;;{?؇pP} p @WU!:3m8a( %v{`Pz(ڼp+PB)nK8)ێBDNGձmy   nHj]dFw\ TV9ʒ  !@FR%GJrCr@@ HЁ #.!kq  ~'p\c'(@@\p#>< y  ~ nYz!Ga*^  @TQ3pD[Z;#J  *@keNC@@ lv#ҏ7pBx7   n^!S91!t@@7.C1!X|@@7+ *(@Q-@@@@ 8(8Rqr9@@ n8q8 !Gg  a n]?z3!QNC@@ubHtJ!Hڌ@@ dGw p0'rXaQ@@ n!O8<)D 䈱 @@ v p0' rXq@@ 7"@HB&!Gգ  1 nTMƲS9D 3A@@`O &GWأrxT   FRNvɖrS@@ H#{0^B@@@ H '@T(Ev  FBf]&Bi`@@7"/0UQ!G<@@ Bʐ pbE69lNS-˲cywСCKNgڣG.MOOo; (0u߿׷m|]zDaϖ FI#2;ЗmC"@'Y$"Y}#˲7tyB%a oZײt}׵DfF"f{ p09 vEvmG?]zﯥ,LMM=E4 @0o}-{uT#ptۨ>)@V`'W/ ;G(*G1JP_|]+k7(]t"`'! r[[#}!݁hW Ϊ-@ M`Cm;[:_zn[phqĨv`DaiHT~<_:('!a rY7.00ZH\~נ#˲?YMd EKJ[[[SJuCxOL-Bt0m@8"2qlGl1 7OՄ H{J(HDȇ@+G 6yQ l:V^(?$ylANr7VmAD& ^EbEGzӗ#jkړz Wd-Fq+^GurԄAmN{~&!x9 @d`)_8,gn/3OScL@Vívƨ<>@ cyߖ/WWJlXᆛpÍ3O _#&7B8K^!3] }-띱ۈ69L}[)3+7ԕG];jZ!G%0ځ5?^ʲ?q N@ $q?j f=i-~k 9/mD(F,>Na6@Ws)Ga H#ږE;,P*E?@ Y=KϖC+JĶCr89 :lA,eYv B@E|]HǘcC9)!Gu, v0[Wb"c@`{=&jKʍ[ 6lHB!dB*\뢃_gDDzzo<6E*uc]߼eS7ti 0@Ǝm>}]csKlK^G8Cr e~H<+"Y/:JRYrJ>11Q[[[YXjyk"r[~aAXzﺈ|I$ 0pƇ;8W*3[[[z}c}}=o4_ezwxs-aN8r0._{V _JH^^;r|7yK/777kkko}+Ex $/g<ϟn4 ]p!hzu"Rg&Vn$_8r-GnE>vt:]DOA<\aX_AaA_< :=pu '@1P~<:Ht)˲?j677rOQJ "o tEG뮻a_ @R1;?t|RD׸~%w)FYS @Bcwyy,ԓ1Xfj_?:ʎ!m]Ce!ЙbՒ |.U}eRDUlY)sRJ#r29~mZ&Wm%x1}|e} ZCy~[{ϺM 3`cgz?""*bn5xnJaRMEB3%|sFQKw*EtW}hyx+tj8[_O>|W׎r5G XXțM]RctUtWu调-, H`-(u{h9X!"TJhZYy VE`WPB r OAë\1 `C +^9 6vi`5˳v.@JaOD`ofGK_:g *{2U&&m XL1Q kv\'_6Z\#9K f֫$C>TّeYna11Ch 5~nƻWc\A!%, ^seE~Dt+gRLnA9^}˗W7} ^ @Zi%%|bp% pD\\V\j+->wyeDA6S|^ D?`CD.ZUטۜf@ZLpB-Y Nc{DdēMXlnnn%;3DtvF]oz_sB^afYTVZ {!zGg;R9J Qy" :;LL @ b]BvݭB)yEzK_8ڲ9m)QbP p+H- C$Doylc|ѣK "8(DR#PCW/sƙ@8"`Vì'E"JQfH(z޽KÛy~\DZa,vDPU.;n?F =kT*ggg/ǼZI%|bA@ _d. 'Pb]w#333u+NyDD,A6>UaWژA[pnjjz)ᆩD;(s%, rtk6m/s"rc?M QSS2C3cYS3v/'se臄lT*Gj+W1jjt)ᆩYC; 0Ik rLH 05Yh Z#% (^a8׼eܷ;\˶ţ$8(3WqUzp?]cKtt:zt \ccVxE}^J ]%yR|G',OnX߆{)"NLoHn%j_?(z0.{Fo=ȪS3v_`a:(͒uרVP^j [穡rf"$$@P% O#O7|;Чrj=6Ɵ~{Pб@o'qKB/?P8_g^~FחwWq1ᆝ)I pD^`g^cp@%;{DOl$.˲t8d<*z^ߊ'.mեvY@gF sG!7Aqh2d< 9|6?A'UzS"ASȕW5@y?!"ox+ 5ʛb'{ti>p78/Ucʰ%œȡD>z_uȓ  lG!'uVk.$ -gG/$pc ́!5\`ލ:n/KqcbeϖJ\ Q&|M(a\[@jn`D1D`[ɀۀs.z/fV9!"wDt-+4333{(HD%">Ͳ<ϟn4܄Ҳ7ʒ ,098H4)Hp.Gs&6ڂh4֏?eq_xz+= W<' $ ]$rpLxGM@$5ln<5xC p ŋ_ -)hU!Gsa.x>w&t)F0$@Rk!}k0hAAD3 !Caf>JW, %(:xC p ("0"rg$7ꡳj5<dXz<ȁK+#>wssJNd`$@Q"S">r%|!y)@eYT'_c,xTDRr$<JoO5e;ԨT*/V*urN|J Bxf{',#lG1[kZV`cpu 2VqP VoaG!9C){^?Od pFσ +68܍Y=K/onnޚqGWkڛ.1nM^u ٬,m%PKQ (c;,xo|e"PO8pԗ^'"[97:C`H=CZ<Ŋ! p @G 0.PDC+fFeK8mZP?)"wZ x%cyPJ=kZu6H <d?$JMic{߳ZVj:Ȏy]Uե͍zwO9 JCCz~DD|!"ѣ>==_/۞4nyMti֊nz",;v}_ac9Ͷê4 `\8) "ho\TDRviJψȻDdVDn@0*@a@l y39m*1i<K+"Qy#  (ī@<mUNS=ڨjo\XXhAASؚ⺐<@`,x3 kv2#˲XyRȗL^e==qĚ~ 68l6 q;X㕢A6V,,,<ǭ).+ɳ@ G"8gq\ppШJ@JϋoHPKz G3 X N@L XئAD{NL0a 8 A  ; T8h]xa=ph@f `R65Y!r)`Q, B0&@a@\ (.ȔgrШALr'nA9lEJC  N80@J)}%;Dfm5I3nRwȄ'i4N^x7 n8z4@CܤbfG@ p9@7ܤƙ- *~ׇ! p@ pAL RT,F$@a f@ p}c7]z Ví7OC0(M*1i*HnP lt@%XE/M*yܠw} [< @7ĤA%a@i@7سeAC@ny anR1 JspJ0 8A@;ܤbǕVkD@ pi I0(# *"8 pc@pWZ_T=Dp+@֛!&à47Q&: X18C0+M*f=i- nP N@[o `AT,Ҥܠuy @I%X@sܤbΒ0D/@ pi I*Mz- *^!$@Q`4˲ONN~ĉk! 0P\@  7j7-,,\ ŀ~/`Qy|Y)#@@5f @nR,zbzzz#J4址FZ޲\ewC !@1oEWM*7͏̬;rzR"^04z~ ڣ@ t@F٦bH@o{E&1S)=  b A@AZel~ULχ`afug9`=@8FbM !8CDXB  p0C@t81  JLO@,pX@I@v+i>dfy*Ui+#7"r3Fɓ'_@8b*cBPJ/"7&e9LjVWn8Edpx8Ɇ Ge\ ]ӧO7WWWӻ´ !iф)}%j]̲,O#D,@qq y 9hc X7=e  @!T>"%`y9ʲü9m)7X8sc۱Z޲7Z HR#ɲ3h@ =˫845.{GӛcÎjcnClјp!x) @AN# +`ѝ]ѫ9.+9,;Êa+{C"rU}JSN.7[@@Y @o.Vߪbؽ+:Ddv|СCKN-, ̺,ˎy^ -"rлEAÖ]/$@@p5qb N. t+?sKUx:;e=j&OFp'􋬻a$@8w\myyy>s*ʀ@6aʬFp"@ᄙ "۪)"t~ `J`IDnZ 7L W!ho\AYRGw 8@تYAi{EOR?20! աo JUXhz [S  pVqƋ -yLΊHs7"*A(,@Q" aIDAT(AD>š1V71n$Un @ yB@W# 0H#  r^dOl8 N =BB`" K  B" t7tjH@ pe U^y; O[P*+ʋJe`ß@8«=FP@.\&&&Nyke ?߹rY7Dd~eKJFXe EyFHN#3`@@@ @@@8+9F@@@ >jʈ@@@HN#3`@@@ @@@8+9F@@@ >jʈ@@@HN#3`@@@ @@@8+9F@@@ >jʈ@@@HN#3`@@@ @@@8+9F@@@ >jʈ@@@HN#3`@@@ @@@8+9F@@@ >GzHIENDB`a2d-2.0.5/a2dfiles/runscript_data.ini000066400000000000000000000002161464731662700174170ustar00rootroot00000000000000[InternalTimeStamp] LastRunTime= [APRSKeyVerify] apiKeyStatus=valid [APRSTimeStamp] LastAPRSPull= [APRSCheck] APRSCheck=verified LastCheck=a2d-2.0.5/docs/000077500000000000000000000000001464731662700131345ustar00rootroot00000000000000a2d-2.0.5/docs/ack.md000066400000000000000000000077031464731662700142230ustar00rootroot00000000000000We would like to express our gratitude to the open-source community and the following projects for their contributions, support, and resources that have made a2d possible: - **Python**: The Python programming language has served as the backbone of our project, providing a powerful and versatile platform for development. - Website: [https://www.python.org/](https://www.python.org/) - **Bootstrap**: Bootstrap has been an integral part of our UI design, enabling seamless and responsive HTML and JS components. We use Bootstrap for light and dark mode switching for the UI. - Website: [https://getbootstrap.com/](https://getbootstrap.com/) - **Cryptography**: The Cryptography library has played a crucial role in ensuring the secure handling and encryption of sensitive data within our project. - **Requests**: The Requests library has greatly simplified the process of making HTTP requests and interacting with external APIs, enhancing the functionality of our project. - **Gunicorn**: We are grateful to Gunicorn for providing an efficient and reliable WSGI server that powers our Flask web app. - Website: [https://gunicorn.org/](https://gunicorn.org/) - **Flask**: Flask has been the foundation of our web app, allowing us to create a user-friendly and feature-rich interface for a2d. - Website: [https://flask.palletsprojects.com/](https://flask.palletsprojects.com/) - **Python3-crontab**: Python3-crontab has been instrumental in managing our cron jobs, ensuring timely and efficient execution of scheduled tasks. - Website: [https://pypi.org/project/python-crontab/](https://pypi.org/project/python-crontab/) - **Nginx**: Our project benefits from the powerful and efficient web server capabilities provided by Nginx. We are grateful to the Nginx community for developing and maintaining this essential software. - Website: [https://www.nginx.com/](https://www.nginx.com/) - **Certbot (Let's Encrypt)**: The integration of SSL certificate management into our project has been made possible by Certbot, an integral part of Let's Encrypt. We extend our thanks to the Certbot team and Let's Encrypt for simplifying and securing the SSL certificate process. - Website: [https://certbot.eff.org/](https://certbot.eff.org/) - Website: [https://letsencrypt.org/](https://letsencrypt.org/) - **python3-psutil**: Monitoring and managing system resources is critical for our project, and python3-psutil has been an indispensable tool for this purpose. We would like to thank the python3-psutil developers for their excellent work in creating this Python library. - **Google Icons**: We acknowledge the use of Google Icons (icon sets), which have been utilized to enhance the visual elements of our project. These icons contribute to the overall user experience and user interface design. - Website: [https://fonts.google.com/icons](https://fonts.google.com/icons) We would also like to extend our appreciation to the following organizations for their APIs and continuous support for the ham radio community: - **hampager.de**: We are grateful for the hampager.de API, which has enabled seamless integration with the DAPNET, and for their continued support of the ham radio community. - Website: [https://hampager.de/](https://hampager.de/) - **aprs.fi**: We would like to thank aprs.fi for their API, which has provided valuable APRS data and resources, and for their ongoing commitment to supporting the ham radio community. - Website: [https://aprs.fi/](https://aprs.fi/) We appreciate the support and contributions from the open-source community, as well as hampager.de and aprs.fi. Their work has significantly contributed to the development and success of a2d. If we have inadvertently missed acknowledging someone's contribution, please accept our apologies, and kindly inform us so that we can rectify the oversight. Thank you to all the projects, organizations, and the open-source community for their valuable contributions to a2d.a2d-2.0.5/docs/authors.md000066400000000000000000000025021464731662700151420ustar00rootroot00000000000000The following individuals and entities have contributed to the development of a2d: #### NGC2023 (NY3W): Identified the need for the program, designed and implemented the program architecture, and developed the core functionality. Collaborated with team members to gather requirements and ensure the program met the desired specifications. Conducted thorough testing and debugging to ensure the program's reliability and functionality. - GitHub: [https://github.com/NGC2023/a2d](https://github.com/NGC2023/a2d) #### OpenAI (represented by ChatGPT): OpenAI's ChatGPT played a crucial role in providing intelligent responses, language processing, and assisting with various aspects of the project's development and implementation. - Website: [https://chat.openai.com/](https://chat.openai.com/) #### pbhus: Part of logic design and python coding - GitHub: [https://github.com/pbhus](https://github.com/pbhus) We appreciate the time and effort these individuals and entities have dedicated to the project. Their contributions have been invaluable in shaping the features, fixing bugs, and improving the overall quality of a2d. If we have inadvertently missed mentioning someone's contribution, please accept our apologies, and kindly inform us so that we can rectify the oversight. Thank you to all the authors who have made a2d possible.a2d-2.0.5/docs/changes.md000066400000000000000000000070521464731662700150720ustar00rootroot00000000000000All notable changes to a2d will be documented in this file. ### [Version 2.0.5] - 2024-07-21 #### Change - Created new documentation using MkDocs. - Linked the App Info section to the new documentation. ### [Version 2.0.4] - 2024-07-17 #### Change - Included tests for a2d. - Updated dependencies in pyproject.toml. - Set default port number in a2d.desktop to 9333. #### Removal - Removed nginx and certbot as hard dependencies. ### [Version 2.0.3] - 2023-10-27 #### Change - Transition to a consolidated single Python package approach. ### [Version 2.0.1] - 2023-10-09 #### Feature - Message counts added to APRS to DAPNET Transmit logs. #### Change - Optimized multicore processing for dual-core processors, like the Raspberry Pi Zero 2 W. #### Security fix - Implemented yaml safe_load instead of pickle for a2d configuration backup. ### [Version 2.0.0] - 2023-09-01 #### Feature - Introduced a user-friendly Flask web app to enhance the a2d experience. - The UI now displays a2d status and message logs. - Implemented both light and dark modes for a visually appealing user interface. - Enhanced security with PIN access protected by a Passphrase for the UI. - Users can now back up and restore a2d configurations conveniently. - Added an Instructions section to guide users within the UI. - Implemented an automatic logout feature after 30 min of inactivity. - Introduced automated APRS fetch interval management to prevent APRS account lock. - Users can now access listen port, server name, and manage SSL certificates (self-signed and CA). - Introduced an option to select a2d default settings. - Implemented a factory reset feature for a2d, users can retain SSL certificates. - Enhanced server status UI with all status including SSL and certificate in use. - Added network health monitoring to track round trip time (RTT) to APRS and DAPNET servers. - Included clear notifications and feedback messages in the UI based on user interactions. #### Change - Accelerated data processing by utilizing multiprocessing for efficient multicore utilization. - Consolidated multiple system services into a single, resource-efficient system service. - Streamlined installation by transitioning from the pip repository to the apt repository for dependencies. - Improved session management with the introduction of the auto logout feature. - Enhanced database read/write operations and implemented self-healing mechanisms if data corruption occurs. #### Deprecation - Deprecated the use of terminal and SSH commands for setting up and running a2d. #### Removal - Eliminated terminal access to user data, replacing it with the new web app interface. - Removed a2d_core services, adopting a more efficient cron job-based approach to improved efficiency and resource usage. #### Bug fix - Addressed an issue where the database was being unnecessarily written during each run. - Resolved database corruption in specific scenarios. - Prevented message loss due to frequent APRS fetch by introducing automated APRS fetch interval management. #### Security fix - Implemented safeguards to prevent flooding bulk messages to DAPNET during the initial run. - Optimized data transfer from ARPS for improved efficiency. - Strengthened data transfer to DAPNET with enhanced error handlingfor incorrect credentials. ### [Version 1.0.0] - 2023-06-23 #### Feature - Added a user-friendly input method for gathering user information and configuring a2d. #### Change - Enhanced security by implementing encryption for user information. #### Bug fix - Resolved the issue where callsign 0 was incorrectly representing the SSID. a2d-2.0.5/docs/index.md000066400000000000000000000032521464731662700145670ustar00rootroot00000000000000### a2d - APRS to DAPNET portal a2d is an application for transmitting APRS messages to DAPNET pagers, dedicated to the HAM radio community usage. ### Description a2d utilizes the APRS API to retrieve APRS messages for a callsign and relays them to DAPNET for delivery to your pager device. It supports multiple SSIDs. ### Prerequisites 1. **Ham Radio License:** Ensure you hold a valid HAM radio license with a callsign. Transmitting signals complies with license regulations and local laws. 2. **APRS API Key:** Register on aprs.fi with your callsign to generate a confidential API Key for downloading APRS messages. Keep this key private. 3. **DAPNET User and Password:** Create a secure account on hampager.de for DAPNET. Additional steps may be required if you don't have an approved DAPNET pager or transmitter. 4. **Debian System with Internet Connection**: For optimal performance and convenience in HAM Radio applications, especially if you prefer a compact, standalone setup with internet access, we recommend using a Raspberry Pi. The Raspberry Pi offers a cost-effective solution that's well-suited for these purposes. ### Compatibility **Debian 12**: a2d has been thoroughly tested on Debian 12. **Debian 12 (VMware)**: Tested on Debian 12 within a VMware environment. **Raspberry Pi OS with Debian 12 (bookworm)**: Tested on Raspberry Pi OS with Debian version 12 (bookworm). You can utilize various packages like VNC or SSH to set up your Raspberry Pi even if you intend to run it headlessly (without a physical display). This approach provides flexibility while maintaining a small footprint, making it a versatile choice for HAM Radio enthusiasts.a2d-2.0.5/docs/install.md000066400000000000000000000070551464731662700151330ustar00rootroot00000000000000a2d is designed with a web UI. ### Install a2d You can install a2d from GitHub Packages. Download the Debian package from [a2d GitHub Releases](https://github.com/NGC2023/a2d/releases). Navigate to the directory where you downloaded the Debian package using the command line and run the following command: `sudo apt install -y ./.deb` `sudo apt install -y ./.deb` Replace a2d_package_version.deb with the a2d file name you downloaded before running this command. To enable access to a2d over the network from another system, it is suggested to install additional packages like nginx and certbot. While the a2d interface supports any reverse proxy and HTTP server, it provides options for limited management of nginx. However, please note that nginx is not installed by default. If you choose to use nginx, you will need to install and can configure via a2d. To create and maintain CA SSL certificates, certbot is required. certbot is not installed by default but is essential for generating CA SSL certificates and managing them automatically. The a2d interface works with certbot to handle SSLs related to a2d. Install nginx and certbot: `sudo apt update` `sudo apt install ` ### Uninstall a2d To uninstall a2d, follow these steps. For a thorough removal of user configuration files, it is advisable to uninstall the application after performing a Factory Reset in the a2d portal (Check Resetting a2d portal section). This ensures a clean removal of user-specific settings. `sudo apt purge a2d` However, please note that this command won't remove the core nginx server and other dependencies that were installed alongside a2d. To completely remove all a2d dependencies, you can use the following commands: **Warning:** Removing dependencies may adversely impact other applications using the dependencies. If you using nginx server for other applications or you using it as a webserver, **DO NOT** remove nginx. Removing nginx and its associated files: `sudo apt -y remove --purge nginx nginx-common nginx-full nginx-core` Remove nginx configuration files: `sudo rm -rf /etc/nginx` Remove nginx default configuration: `sudo rm -rf /etc/default/nginx` Remove nginx init.d script: `sudo rm -rf /etc/init.d/nginx` Remove nginx log files: `sudo rm -rf /var/log/nginx` Finally, perform an autoremove to clean up any remaining dependencies: `sudo apt autoremove` ### Python 3 and Other Dependencies: Ensure that Python 3 is available in your system to run a2d. All the required dependencies will be automatically installed during the a2d installation process. However, if you encounter any errors related to dependencies during the a2d installation, please make sure the following packages are available on your system in addition to Python 3: 1. python3-cryptography 2. python3-requests 3. python3-flask 4. python3-gunicorn 5. python3-psutil 6. python3-yaml 7. nginx (not default installation, suggested for remote access) 8. certbot (not default installation, suggested for remote access) Gunicorn serves as the WSGI server that powers the a2d user interface, while Nginx is used as a reverse proxy server. Certbot is essential for creating and maintaining the required SSL certificates. If you prefer to install dependencies manually, you can use the following commands to install from the apt repository: `sudo apt update` `sudo apt install ` If you encounter any issues during installation, you can try running: `sudo apt --fix-broken install` This will help resolve any broken dependencies and ensure that a2d operates smoothly on your system.a2d-2.0.5/docs/lic.md000066400000000000000000000035721464731662700142340ustar00rootroot00000000000000#### MIT License Copyright (c) 2023 NGC2023 a2d/static/bootstrap/css/cdn_bootstrap_5-3_min.css Copyright: 2011-2023 The Bootstrap Authors License: MIT a2d/static/bootstrap/js/cdn_bootstrap_5-3.js Copyright: 2011-2023 The Bootstrap Authors License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #### CC-BY-3.0 License a2d/static/bootstrap/css/bootstrap_docs_5-3.css Copyright: 2011-2023 The Bootstrap Authors License: CC-BY-3.0 a2d/static/bootstrap/js/bootstrap_5-3_mode.js Copyright: 2011-2023 The Bootstrap Authors License: CC-BY-3.0 We have made modifications to a portion of the Bootstrap code to suit our project's requirements. These minor modifications enable the Color mode toggler from Bootstrap's docs to work with a2d. Licensed under the Creative Commons Attribution 3.0 Unported License. For details, see [https://creativecommons.org/licenses/by/3.0/](https://creativecommons.org/licenses/by/3.0/).a2d-2.0.5/docs/usage/000077500000000000000000000000001464731662700142405ustar00rootroot00000000000000a2d-2.0.5/docs/usage/access.md000066400000000000000000000025271464731662700160310ustar00rootroot00000000000000Once a2d is installed, open a web browser in the same computer and visit `http://localhost:9333` or `http://ipaddress:9333` The default a2d communication port is 9333 over http. If you installed nginx then the port is ***9331*** (local and remote). Alternatively, you can access the a2d portal directly from the application list on your Linux GUI desktop after installing a2d. ### Register PIN and Passphrase Security is paramount in the a2d portal. Here's how to safeguard your access: **PIN Registration:** Choose a personalized six-digit PIN. If this is your first time, select "Register" on the login page to set your 6-digit PIN and Passphrase. **Logging In:** After registration, use your assigned PIN to log in. The login session will automatically expire after 20 minutes of inactivity, ensuring the security of your data. **PIN Recovery:** If you forget your PIN, use your Passphrase to create a new one. Click "Forgot PIN!" on the login page, verify with your Passphrase, and proceed to set a new PIN. Note: There's no Passphrase reset option. Removing and Reinstalling the a2d portal is the only recourse to reset your Passphrase (Warning: You will loose all your a2d data). After uninstalling a2d, please use the following command to remove any existing user configuration files before proceeding with the reinstallation. `sudo rm -r /etc/a2d`a2d-2.0.5/docs/usage/dash.md000066400000000000000000000102671464731662700155070ustar00rootroot00000000000000**Navigation Bar:** Provides convenient links and icons for easy access to various sections such as Server, Mode (Light, Dark, and Auto), Options, and Logout. The Options section includes Configuration, Advanced Options, Backup/Restore Configuration, Change PIN, and Information. **Dashboard:** Displays Callsign, a2d Status, Run Interval, Last Run, Destination SSIDs. Start/Stop Relay buttons. **System Health:** Displays CPU temperature, Memory usage, and CPU Load. **APRS to DAPNET Transmits:** Shows logs of APRS to DAPNET transmitted messages. a2d app offers three screen modes with Bootstrap v5.3 support: Light, Dark, and Auto. **Light Mode:** Provides a clean and bright interface for comfortable usage. **Dark Mode:** Offers a sleek, darker interface that's easy on the eyes, especially in low-light conditions. **Auto Mode:** Adapts to your local system settings, ensuring a seamless experience that aligns with your device's theme. Choose the mode that suits your preferences and enhances your interaction with the a2d app. ### Setup Configuration If it's your first time, the dashboard will show 'Setup Configuration' instead of your Callsign. Click "Configuration" listed in options in the navigation bar to begin. **To complete setup:** 1. Enter your HAM radio callsign in ALL CAPS without any SSID for Callsign (no ssid). 2. Input your APRS API Key from [aprs.fi](http://aprs.fi). 3. Provide your DAPNET account username and password. 4. Include your DAPNET transmitter group code. Each DAPNET transmitter belongs to a specific group. Find your code during transmitter configuration. You can find your transmitter group code when setting up your transmitter at [hampager.de](http://hampager.de). 5. Click "Set Config" to save. ***Note***: Your data is securely encrypted. In case of security concerns, regenerate keys on respective websites and change passwords. ### Start/Stop Relay **Important:** Setup Configuration before pressing 'Start Relay' button. - After configuring, click "Start Relay" to begin and "Stop Relay" in the dashboard to halt. - The interval between each APRS to DAPNET transfer is managed by the crontab manager in your system. For example, if the transfer interval is set to 15 min, cron will trigger the transfer at 0, 15, 30, and 45 min past the hour. - a2d is designed to transfer only APRS messages received after its first run, preventing flooding DAPNET with a large number of messages all at once. ### Advanced Options **Note:** APRS servers block requests under 2 min. Please set intervals > 5 min for optimal performance. If needed, modify in "Advanced Options" via "Options" in the navigation bar and selecting "Advanced." - **Interval:** Set retrieval frequency. Default is 15 minutes. Avoid very short intervals (below 5 min). - **Preferred APRS SSIDs:** Define target SSIDs for messages. -0 is your callsign without SSID. Find HAM APRS SSIDs on [APRS SSID list](http://www.aprs.org/aprs11/SSIDs.txt). - **Transmit logs:** Set displayed logs on dashboard. ### Backup/Restore Configuration Once you've set up your a2d configuration, you can use the Backup/Restore in the navigation bar to safeguard your settings. Both actions—backup and restore—require Passphrase verification. **Backup:** Create a backup of your a2d configuration files. The backup file's name will be "a2d_config_backup.bin." This file is encoded binary and holds your API credentials. Store it securely. **Restore:** If you want to revert to a previous configuration, use the backup file to restore. However, be cautious. If the bin file has been tampered with, the restore won't work, and using a modified bin file is strongly discouraged. Note: Safeguard your settings with backups and restorations, but secure your bin file from unauthorized access and exercise caution to maintain the integrity of your a2d configuration. #### Errors while Backup/Restore: - **Invalid Passphrase:** Retry with the correct Passphrase. - **Configuration files missing:** Set up Configuration. - **Invalid file:** During restore configuration, use the correct a2d_config_backup.bin file. - **Configuration file tampered:** If you spot this, avoid using the modified file as it could harm the a2d app.a2d-2.0.5/docs/usage/err.md000066400000000000000000000052401464731662700153530ustar00rootroot00000000000000Error messages play a crucial role in guiding you through issues. They appear in two places: ### 1. Service Status Container in the Dashboard If you encounter any of these errors, you'll see them next to "Status" in the Service Status container. To resolve, ensure accurate information in your configuration and restart the service. - **Invalid APRS API key** - **Incorrect APRS API key** - **Incorrect DAPNET username or password** - **Incorrect DAPNET callsign or txgroup** DAPNET-related errors appear only when a new APRS message is received and a2d attempts to transmit it to DAPNET. ### 2. Error Messages Triggered by Click on the Dashboard For these errors, you'll see messages upon clicking. Solutions are provided below: - **Invalid PIN:** Retry with the correct PIN. - **Invalid Passphrase:** Retry with the correct Passphrase. - **Configuration files missing:** Set up Configuration. - **Invalid file:** During restore configuration, use the correct a2d_config_backup.bin file. - **Configuration file tampered:** If you spot this, avoid using the modified file as it could harm the a2d app. ### 3. Error Messages during Server Settings and SSL Certificates - **Create Self-Signed SSL Certificate before Enabling:** Ensure that you have created the required SSL certificates before configuring the server for SSL. - **Server and Common Name Mismatch:** Verify that your server name matches the Common Name (CN) of the certificate and corresponds to your domain name. - **CA SSL Certificate Already Exists:** Try deleting the CA SSL certificate before attempting to create a duplicate certificate. - **CA SSL Does Not Exist:** The SSL certificate you are trying to delete does not exist. Try refreshing the page. - **Selected CA SSL or Server Name in Use:** Do not delete an SSL certificate while it is in use. First, change the SSL settings in the server, and then attempt to delete it. - **Unknown Response from the Server:** This issue could be due to network problems, firewall blocking specific ports, or reaching Let's Encrypt's SSL certificate limits. - **Failed to Update Server:** Your server may be offline or unreachable. ### Other Unknown Errors and Troubleshooting: If new messages aren't reaching your DAPNET pager: **Potential Causes:** Too frequent APRS requests, or issues with APRS or DAPNET servers. **Resolution:** Confirm server functionality and your Configuration setup. If the problem persists, wait for a couple of runs, as a2d self-heals the database. If the issue continues, reinstall a2d to fix message database corruption. Database corruption may occur during message read/write interruptions, such as power outages, memory loss, or system failures.a2d-2.0.5/docs/usage/msg.md000066400000000000000000000011371464731662700153520ustar00rootroot00000000000000DAPNET paging messages are limited to 80 characters per message. To conserve space, a2d messages follow the structure given below and will be delivered to your pager: SourceCall (DestinationSSID): Message - **SourceCall:** The sender's callsign. - **DestinationSSID:** Your callsign's SSID. - **Message:** The message content. **Example**: `NY3W-5(7): Hello OM!` Here, `NY3W` is the sender's callsign, `7` is the SSID of the receiver's (your) callsign (e.g., NY3W-7), and `Hello OM!` is the message. If the DestinationSSID displays 0, it means the message is targeted for your callsign without any SSID.a2d-2.0.5/docs/usage/srv.md000066400000000000000000000126631464731662700154040ustar00rootroot00000000000000a2d offers server settings and advanced options for users who want to customize their server. Make changes cautiously. You can use a2d with its default settings for local access. If you want to access a2d from outside your network, you can do so by making careful adjustments to your router's firewall settings. ### Server Page Overview - **Navigation Bar:** Convenient links and icons for easy navigation are available, allowing you to access sections like Dashboard, Mode (Light, Dark, and Auto), Options, and Logout. The Options section includes Server Configuration, Self-Signed SSL, CA Signed SSL, Change PIN, Reset Portal, Logout, and Information. - **Server Status:** Provides essential information, including the start time of Nginx and Gunicorn, Listen Port, Server Name, SSL Status, and SSL Certificate details such as Common Name (CN), Organization Name (O), and Expiry Date. - **Network Health:** Displays the Round Trip Time (RTT) to APRS and DAPNET servers, indicating the speed at which data can transfer between your system and their servers. ### Server Configuration (Only if nginx & certbot installed) The server configuration allows you to modify a2d's default server settings. a2d's default settings include Listen Port: 9331, Server Name: _, and SSL disabled. These settings have the potential to override your existing NGINX configuration, particularly if NGINX is configured for another application. If you are using NGINX for another application, it is strongly recommended to manually configure NGINX for a2d. #### Desired Listen Port: You can choose the port number for a2d access. For standard HTTP, it's port 80, and for HTTPS, it's 443. Alternatively, you can select any other open port on your system. After changing the port, you'll need to access a2d using the specified port. #### Desired Server Name: You can set the server name to match your desired domain name. Ensure that the server name corresponds to your domain name, especially if you've enabled SSL, as it should match the Common Name (CN) in the SSL certificate. #### Choosing SSL Certificates: a2d offers the option to create and use either Self-Signed SSL or Certificate Authority (CA) SSL certificates for your convenience. The difference between them is the appearance of a green lock symbol in the browser when you access them. Ensure that your server name matches the CN in your certificate before making a choice. Before selecting an SSL option, you need to create the certificates. #### Creating Self-Signed SSL Certificate: In a2d, you can create and store one self-signed SSL certificate. There's no limit to how many times you can create it. You can generate it through the "Self Signed SSL" link in the navigation bar or by clicking the green "Self Signed SSL" button inside Server Configuration. Self-Signed SSL certificates are not verified, so you'll encounter an SSL security warning when accessing a2d in a web browser. You'll need to bypass this warning to access a2d once Self-Signed SSL is enabled. Once you enabled SSL certificate access a2d through HTTPS. #### Creating CA SSL Certificate: a2d offers the option to create CA SSL certificates directly through certbot and Let's Encrypt within the a2d portal. You can create them via the "CA Signed SSL" link in the navigation bar or by clicking the green "CA Signed SSL" button inside Server Configuration. You can generate as many CA Signed SSL certificates as needed. Ensure that the Common Name (CN) matches your domain name. Once you've created your desired SSL certificate, you can return to Server Configuration to set it for a2d. After certificate creation, a2d uses certbot for auto-renewal. If you no longer need CA SSL certificates, a2d provides an option to delete them by clicking "Delete CA SSL" in the CA Signed SSL and selecting the certificate to remove. Please note that Let's Encrypt has a limit on duplicate certificates. For more details, refer to [Let's Encrypt's documentation](https://letsencrypt.org/docs/duplicate-certificate-limit/). #### Firewall Settings: Understanding your firewall settings is crucial. Carefully open the necessary ports on your router to achieve the following: 1. Access a2d from outside your network. 2. Generate CA SSL certificates. Let's Encrypt will use ports 80 and 443 to validate your server before issuing the certificate. If you have both IPv4 and IPv6, it's strongly recommended to open required ports for both IPv4 and IPv6. After generating certificates you can safely close them and leave 443 open if you want remove access to a2d outside of your network. In case you are unable to open the ports for some reason and cannot generate Let's Encrypt certificates using the a2d portal, you can perform these tasks via the command line and adjust your DNS settings on your provider's website. For certbot user guide, visit the [Certbot user guide](https://eff-certbot.readthedocs.io/en/stable/using.html). ### Resetting a2d portal a2d provides a Reset Portal option that allows you to reset the portal and delete SSL certificates, if desired. You need to enter correct passphrase to reset the portal. You can choose to retain the SSL certificates during the factory reset. The Reset Portal option is available in the server page's options. This action will delete all user files, configurations, and data. If you are accessing a2d via SSL during the reset, the server settings will revert to the default a2d settings, and you will need to access a2d through HTTP port 9331 after the reset.a2d-2.0.5/mkdocs.yml000066400000000000000000000025431464731662700142130ustar00rootroot00000000000000site_name: a2d site_description: APRS to DAPNET portal repo_url: https://github.com/NGC2023/a2d theme: name: material features: - navigation.sections - navigation.footer icon: repo: fontawesome/brands/github palette: # Palette toggle for automatic mode - media: "(prefers-color-scheme)" toggle: icon: material/brightness-auto name: Switch to light mode # Palette toggle for light mode - media: "(prefers-color-scheme: light)" scheme: default toggle: icon: material/brightness-7 name: Switch to dark mode # Palette toggle for dark mode - media: "(prefers-color-scheme: dark)" scheme: slate toggle: icon: material/brightness-4 name: Switch to system preference use_directory_urls: false nav: - Home: "index.md" - Installation: "install.md" - Configuration and Usage: - Access a2d UI: "usage/access.md" - Dashboard Overview: "usage/dash.md" - Message Structure: "usage/msg.md" - Server Settings: "usage/srv.md" - Error Messages: "usage/err.md" - License: "lic.md" - Change Log: "changes.md" - Authors and Contributors: "authors.md" - Acknowledgements: "ack.md" extra: social: - icon: fontawesome/brands/github link: https://github.com/NGC2023 copyright: Copyright © 2023 - 2024 NGC2023. MIT, CC-BY-3.0.a2d-2.0.5/pyproject.toml000066400000000000000000000012521464731662700151200ustar00rootroot00000000000000[tool.poetry] name = "a2d" version = "2.0.5" description = "APRS to DAPNET portal" keywords = ["hamradio"] license = "MIT, CC-BY-3.0" homepage = "https://github.com/NGC2023/a2d" repository = "https://github.com/NGC2023/a2d.git" authors = ["NGC2023 "] maintainers = ["NGC2023 "] packages = [ {include = "a2d"} ] [tool.poetry.dependencies] python = "^3.9" cryptography = "*" requests = "*" flask = "*" gunicorn = "*" psutil = "*" pyyaml = "*" [tool.poetry.dev-dependencies] pytest = "^6.0" pytest-flask = "*" psutil = "*" mkdocs = "*" mkdocs-material = "*" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" a2d-2.0.5/scripts/000077500000000000000000000000001464731662700136735ustar00rootroot00000000000000a2d-2.0.5/scripts/a2d_desktop_url.sh000066400000000000000000000001331464731662700173050ustar00rootroot00000000000000#!/bin/bash # Path URL="http://localhost:9333" # Open the determined URL xdg-open "$URL" a2d-2.0.5/scripts/run_a2d.sh000066400000000000000000000000451464731662700155600ustar00rootroot00000000000000#!/bin/sh python3 -m a2d.runscripts a2d-2.0.5/system-service-doc.txt000066400000000000000000000015261464731662700164760ustar00rootroot00000000000000# a2d HTML app systemd service documentation ## Description This systemd service file is for managing the a2d HTML app. It ensures that the app starts, stops, and restarts as necessary. The service unit is dependent on network connectivity and runs under the 'a2d' user and group. ## Unit Section ### Description - **Description**: a2d HTML app - **Wants**: network-online.target - **After**: network-online.target ## Service Section ### Description - **Type**: simple - **User**: a2d - **Group**: a2d - **WorkingDirectory**: /usr/share/a2d - **ExecStart**: /usr/bin/python3 -m gunicorn app:app -c /usr/lib/python3/dist-packages/a2d/gunicorn_config.py - **Restart**: always - **Description**: a2d HTML App - **ExecReload**: /bin/kill -HUP $MAINPID - **ProtectHome**: true ## Install Section ### Description - **WantedBy**: multi-user.target a2d-2.0.5/tests/000077500000000000000000000000001464731662700133465ustar00rootroot00000000000000a2d-2.0.5/tests/test_app.py000066400000000000000000000042021464731662700155350ustar00rootroot00000000000000import os import tempfile import pytest from a2d.app import app @pytest.fixture def client(): db_fd, app.config['DATABASE'] = tempfile.mkstemp() app.config['TESTING'] = True with app.test_client() as client: with app.app_context(): pass yield client os.close(db_fd) os.unlink(app.config['DATABASE']) def test_home_route(client): """Test the home route.""" rv = client.get('/') assert rv.status_code == 302 # Should redirect to /login if no session def test_get_version(client): """Test the version route.""" rv = client.get('/version') assert rv.status_code == 200 assert rv.json == {'version': '2.0.5'} def test_session_key_generation(monkeypatch): """Test session key generation and loading.""" import tempfile import a2d.app with tempfile.TemporaryDirectory() as tempdir: SESSION_KEY_FILE = os.path.join(tempdir, "session_key.bin") def mock_generate_and_store_session_key(): session_key = os.urandom(32) with open(SESSION_KEY_FILE, "wb") as key_file: key_file.write(session_key) def mock_load_session_key(): try: with open(SESSION_KEY_FILE, "rb") as key_file: return key_file.read() except FileNotFoundError: return None monkeypatch.setattr(a2d.app, "generate_and_store_session_key", mock_generate_and_store_session_key) monkeypatch.setattr(a2d.app, "load_session_key", mock_load_session_key) a2d.app.generate_and_store_session_key() session_key = a2d.app.load_session_key() assert session_key is not None assert len(session_key) == 32 def test_blueprints_registered(): """Test if all blueprints are registered.""" expected_blueprints = [ 'auth', 'run', 'dns', 'system', 'network', 'config', 'reset', 'data' ] registered_blueprints = list(app.blueprints.keys()) for blueprint in expected_blueprints: assert blueprint in registered_blueprints a2d-2.0.5/tests/test_data.py000066400000000000000000000025321464731662700156720ustar00rootroot00000000000000import pytest from unittest.mock import patch from flask import Flask from a2d.routes.data import data_routes @pytest.fixture def client(): app = create_app() app.register_blueprint(data_routes) with app.test_client() as client: with app.app_context(): yield client def create_app(): app = Flask(__name__) app.config['TESTING'] = True app.secret_key = 'your_secret_key' # Example secret key for session return app def test_fetch_logs(client): with client.session_transaction() as sess: sess['user_id'] = 1 # Example user ID # Mocking the database query and configparser read with patch('os.path.exists', side_effect=lambda x: True), \ patch('sqlite3.connect') as mock_connect, \ patch('configparser.ConfigParser') as MockConfigParser: mock_cursor = mock_connect.return_value.cursor.return_value mock_cursor.fetchall.side_effect = [ [(1, 'msg1', 'content1', '2023-01-01 10:00:00')], [(2, 'msg2', 'content2', '2023-01-01 11:00:00')] ] # Simulate a GET request rv = client.get('/fetch-logs') assert rv.status_code == 302 def test_fetch_logs_no_login(client): # User not logged in rv = client.get('/fetch-logs') assert rv.status_code == 302 # Expect redirect to login page or another location a2d-2.0.5/tests/test_gunicorn.py000066400000000000000000000017311464731662700166050ustar00rootroot00000000000000import pytest import importlib.resources # Load gunicorn configuration def load_gunicorn_config(): gunicorn_config = {} a2d_files = importlib.resources.files('a2d') config_path = a2d_files / 'gunicorn_config.py' with config_path.open() as f: exec(f.read(), gunicorn_config) return gunicorn_config # Test for checking if gunicorn.conf.py exists def test_config_file_exists(): a2d_files = importlib.resources.files('a2d') config_path = a2d_files / 'gunicorn_config.py' assert config_path.exists(), "gunicorn.conf.py file does not exist" # Parameterized test for each configuration key @pytest.mark.parametrize('key', [ 'bind', 'workers', 'threads', 'max_requests', 'max_requests_jitter', 'timeout', 'errorlog', 'loglevel', 'logrotate' ]) def test_gunicorn_config_keys(key): gunicorn_config = load_gunicorn_config() assert key in gunicorn_config, f"Key '{key}' not found in gunicorn config" a2d-2.0.5/tests/test_reset.py000066400000000000000000000055331464731662700161070ustar00rootroot00000000000000import pytest from unittest.mock import patch from flask import Flask from a2d.routes.reset import reset_routes @pytest.fixture def app(): """Fixture to create a Flask app instance.""" app = Flask(__name__) app.config['TESTING'] = True app.register_blueprint(reset_routes) app.secret_key = 'test_secret_key' # Set a secret key for session management return app @pytest.fixture def client(app): """Fixture to create a test client with app context.""" with app.test_client() as client: with app.app_context(): yield client def test_reset_account(client): """Test case for resetting account with valid passphrase.""" with client.session_transaction() as sess: sess['user_id'] = 1 # Example user ID # Mock passphrase verification and form data with patch('a2d.routes.reset.verify_passphrase', return_value=True): data = { 'passphrase': 'testpassphrase', 'reset_confirm': 'delete', 'self_ssl_delete': 'true', 'ca_ssl_delete': 'false' } rv = client.post('/reset-portal', data=data) assert rv.status_code == 302 # Expecting redirect upon successful reset def test_reset_account_invalid_passphrase(client): """Test case for resetting account with invalid passphrase.""" with client.session_transaction() as sess: sess['user_id'] = 1 # Example user ID # Mock invalid passphrase verification with patch('a2d.routes.reset.verify_passphrase', return_value=False): data = { 'passphrase': 'wrongpass', 'reset_confirm': 'delete', 'self_ssl_delete': 'true', 'ca_ssl_delete': 'false' } rv = client.post('/reset-portal', data=data) assert rv.status_code == 302 # Expecting redirect upon failure due to invalid passphrase def test_reset_account_no_confirmation(client): """Test case for resetting account without confirmation.""" with client.session_transaction() as sess: sess['user_id'] = 1 # Example user ID # No confirmation provided data = { 'passphrase': 'testpassphrase', 'reset_confirm': '', 'self_ssl_delete': 'false', 'ca_ssl_delete': 'false' } rv = client.post('/reset-portal', data=data) assert rv.status_code == 302 # Expecting redirect or another appropriate status code def test_reset_account_no_login(client): """Test case for resetting account without user logged in.""" # No user logged in scenario data = { 'passphrase': 'testpassphrase', 'reset_confirm': 'delete', 'self_ssl_delete': 'true', 'ca_ssl_delete': 'false' } rv = client.post('/reset-portal', data=data, follow_redirects=True) assert rv.status_code == 404 # Expecting 404 when endpoint is not reachable without login a2d-2.0.5/tests/test_run.py000066400000000000000000000023741464731662700155710ustar00rootroot00000000000000import pytest from unittest.mock import patch, MagicMock from a2d.app import app from a2d.routes.run import add_cronjob @pytest.fixture def client(): app.config['TESTING'] = True with app.test_client() as client: with app.app_context(): yield client def test_add_and_check_cronjob(monkeypatch): # Mocking filesystem operations for add_cronjob and check_cronjob functions mock_file = MagicMock() mock_open = MagicMock(return_value=mock_file) monkeypatch.setattr('builtins.open', mock_open) # Mocking ConfigParser to return a mock instance with patch('a2d.routes.run.configparser.ConfigParser') as MockConfigParser: mock_config = MockConfigParser.return_value mock_config.read.return_value = [] # Simulate an empty configuration file # Test adding cron job add_cronjob() # Assert that the file was opened correctly mock_open.assert_called_once_with('/etc/cron.d/a2d', 'a') def test_service_routes(client): # Test start-service route rv = client.get('/start-service') assert rv.status_code == 302 # Redirects to '/' on success # Test stop-service route rv = client.get('/stop-service') assert rv.status_code == 302 # Redirects to '/' on success a2d-2.0.5/tests/test_system.py000066400000000000000000000022421464731662700163030ustar00rootroot00000000000000import pytest from unittest.mock import patch from flask import Flask from a2d.routes.system import system_routes @pytest.fixture def client(): app = create_app() app.register_blueprint(system_routes) with app.test_client() as client: with app.app_context(): yield client def create_app(): app = Flask(__name__) app.config['TESTING'] = True app.secret_key = 'your_secret_key' # Example secret key for session return app def test_system_info(client): with client.session_transaction() as sess: sess['user_id'] = 1 # Example user ID # Mocking get_cpu_temperature, get_system_memory_usage, and get_cpu_load with patch('a2d.routes.system.get_cpu_temperature', return_value=50.0), \ patch('a2d.routes.system.get_system_memory_usage', return_value=50.0), \ patch('a2d.routes.system.get_cpu_load', return_value=50.0): rv = client.get('/system-info') assert rv.status_code == 302 # Expecting a redirect def test_system_info_no_login(client): # User not logged in rv = client.get('/system-info') assert rv.status_code == 302 # Redirect to login page or another location