diff --git a/openwebrx/.dockerignore b/openwebrx/.dockerignore
new file mode 100644
index 0000000..8c815de
--- /dev/null
+++ b/openwebrx/.dockerignore
@@ -0,0 +1,7 @@
+.git
+.gitignore
+.idea
+**/*.pyc
+**/*.swp
+black-env
+debian
\ No newline at end of file
diff --git a/openwebrx/CHANGELOG.md b/openwebrx/CHANGELOG.md
new file mode 100644
index 0000000..4635f83
--- /dev/null
+++ b/openwebrx/CHANGELOG.md
@@ -0,0 +1,204 @@
+**1.1.0**
+- Reworked most graphical elements as SVGs for faster loadtimes and crispier display on hi-dpi displays
+- Updated pipelines to match changes in digiham
+- Changed D-Star and NXDN integrations to use new decoders from digiham
+- Added D-Star and NXDN metadata display
+
+**1.0.0**
+- Introduced `squelch_auto_margin` config option that allows configuring the auto squelch level
+- Removed `port` configuration option; `rtltcp_compat` takes the port number with the new connectors
+- Added support for new WSJT-X modes FST4, FST4W (only available with WSJT-X 2.3) and Q65 (only avilable with
+ WSJT-X 2.4)
+- Added support for demodulating M17 digital voice signals using m17-cxx-demod
+- New reporting infrastructure, allowing WSPR and FST4W spots to be sent to wsprnet.org
+- Add some basic filtering capabilities to the map
+- New arguments to the `openwebrx` command-line to facilitate the administration of users (try `openwebrx admin`)
+- Default bandwidth changes:
+ - "WFM" changed to 150kHz
+ - "Packet" (APRS) changed to 12.5kHz
+- Configuration rework:
+ - New: fully web-based configuration interface
+ - System configuration parameters have been moved to a new, separate `openwebrx.conf` file
+ - Remaining parameters are now editable in the web configuration
+ - Existing `config_webrx.py` files will still be read, but changes made in the web configuration will be written to
+ a new storage system
+ - Added upload of avatar and panorama image via web configuration
+- New devices supported:
+ - HPSDR devices (Hermes Lite 2) thanks to @jancona
+ - BBRF103 / RX666 / RX888 devices supported by libsddc
+ - R&S devices using the EB200 or Ammos protocols
+
+**0.20.3**
+- Fix a compatibility issue with python versions <= 3.6
+
+**0.20.2**
+- Fix a security problem that allowed arbitrary commands to be executed on the receiver
+ ([See github issue #215](https://github.com/jketterl/openwebrx/issues/215))
+
+**0.20.1**
+- Remove broken OSM map fallback
+
+**0.20.0**
+- Added the ability to sign multiple keys in a single request, thus enabling multiple users to claim a single receiver
+ on receiverbook.de
+- Fixed file descriptor leaks to prevent "too many open files" errors
+- Add new demodulator chain for FreeDV
+- Added new HD audio streaming mode along with a new WFM demodulator
+- Reworked AGC code for better results in AM, SSB and digital modes
+- Added support for demodulation of "Digital Radio Mondiale" (DRM) broadcast using the "dream" decoder.
+- New default waterfall color scheme
+- Prototype of a continuous automatic waterfall calibration mode
+- New devices supported:
+ - FunCube Dongle Pro+ (`"type": "fcdpp"`)
+ - Support for connections to rtl_tcp (`"type": "rtl_tcp"`)
+
+**0.19.1**
+- Added ability to authenticate receivers with listing sites using "receiver id" tokens
+
+**0.19.0**
+- Fix direwolf connection setup by implementing a retry loop
+- Pass direct sampling mode changes for rtl_sdr_soapy to owrx_connector
+- OSM maps instead of Google when google_maps_api_key is not set (thanks @jquagga)
+- Improved logic to pass parameters to soapy devices.
+ - `rtl_sdr_soapy`: added support for `bias_tee`
+ - `sdrplay`: added support for `bias_tee`, `rf_notch` and `dab_notch`
+ - `airspy`: added support for `bitpack`
+- Added support for Perseus-SDR devices, (thanks @amontefusco)
+- Property System has been rewritten so that defaults on sdr behave as expected
+- Waterfall range auto-adjustment now only takes the center 80% of the spectrum into account, which should work better
+ with SDRs that oversample or have rather flat filter curves towards the spectrum edges
+- Bugfix for negative network usage
+- FiFi SDR: prevent arecord from shutting down after 2GB of data has been sent
+- Added support for bias tee control on rtl_sdr devices
+- All connector driven SDRs now support `"rf_gain": "auto"` to enable AGC
+- `rtl_sdr` type now also supports the `direct_sampling` option
+- Added decoding implementation for for digimode "JS8Call"
+ (requires an installation of [js8call](http://js8call.com/) and
+ [the js8py library](https://github.com/jketterl/js8py))
+- Reorganization of the frontend demodulator code
+- Improve receiver load time by concatenating javascript assets
+- Docker images migrated to Debian slim images; This was necessary to allow the use of function multiversioning in
+ csdr and owrx_connector to allow the images to run on a wider range of CPUs
+- Docker containers have been updated to include the SDRplay driver version 3
+- HackRF support is now based on SoapyHackRF
+- Removed sdr.hu server listing support since the site has been shut down
+- Added support for Radioberry 2 Rasbperry Pi SDR Cape
+
+**0.18.0**
+- Support for SoapyRemote
+
+**2020-02-08**
+- Compression, resampling and filtering in the frontend have been rewritten in javascript, sdr.js has been removed
+- Decoding of Pocsag modulation is now possible
+- Removed the 3D waterfall since it had no real application and required ~1MB of javascript code to be downloaded
+- Improved the frontend handling of the "too many users" scenario
+- PSK63 digimode is now available (same decoding pipeline as PSK31, but with adopted parameters)
+- The frequency can now be manipulated with the mousewheel, which should allow the user to tune more precise. The tuning
+ step size is determined by the digit the mouse cursor is hovering over.
+- Clicking on the frequency now opens an input for direct frequency selection
+- URL hashes have been fixed and improved: They are now updated automatically, so a shared URL will include frequency
+ and demodulator, which allows for improved sharing and linking.
+- New daylight scheduler for background decoding, allows profiles to be selected by local sunrise / sunset times
+- New devices supported:
+ - LimeSDR (`"type": "lime_sdr"`)
+ - PlutoSDR (`"type": "pluto_sdr"`)
+ - RTL_SDR via Soapy (`"type": "rtl_sdr_soapy"`) on special request to allow use of the direct sampling mode
+
+**2020-01-04**
+- The [owrx_connector](https://github.com/jketterl/owrx_connector) is now the default way of communicating with sdr
+ devices. The old sdr types have been replaced, all `_connector` suffixes on the type must be removed!
+- The sources have been refactored, making it a lot easier to add support for other devices
+- SDR device failure handling has been improved, including user feedback
+- New devices supported:
+ - FiFiSDR (`"type": "fifi_sdr"`)
+
+**2019-12-15**
+- wsjt-x updated to 2.1.2
+- The rtl_tcp compatibility mode of the owrx_connector is now configurable using the `rtltcp_compat` flag
+
+**2019-12-10**
+- added support for airspyhf devices (Airspy HF+ / Discovery)
+
+**2019-12-05**
+- explicit device filter for soapy devices for multi-device setups
+
+**2019-12-03**
+- compatibility fixes for safari browsers (ios and mac)
+
+**2019-11-24**
+- There is now a new way to interface with SDR hardware, .
+ They talk directly to the hardware (no rtl_sdr / rx_sdr necessary) and offer I/Q data on a socket, just like nmux
+ did before. They additionally offer a control socket that allows openwebrx to control the SDR parameters directly,
+ without the need for repeated restarts. This allows for quicker profile changes, and also reduces the risk of your
+ SDR hardware from failing during the switchover. See `config_webrx.py` for further information and instructions.
+- Offset tuning using the `lfo_offset` has been reworked in a way that `center_freq` has to be set to the frequency you
+ actually want to listen to. If you're using an `lfo_offset` already, you will probably need to change its sign.
+- `initial_squelch_level` can now be set on each profile.
+- As usual, plenty of fixes and improvements.
+
+**2019-10-27**
+- Part of the frontend code has been reworked
+ - Audio buffer minimums have been completely stripped. As a result, you should get better latency. Unfortunately,
+ this also means there will be some skipping when audio starts.
+ - Now also supports AudioWorklets (for those browser that have it). The Raspberry Pi image has been updated to include
+ https due to the SecureContext requirement.
+ - Mousewheel controls for the receiver sliders
+- Error handling for failed SDR devices
+
+**2019-09-29**
+- One of the most-requested features is finally coming to OpenWebRX: Bookmarks (sometimes also referred to as labels).
+ There's two kinds of bookmarks available:
+ - Serverside bookmarks that are set up by the receiver administrator. Check the file `bookmarks.json` for examples!
+ - Clientside bookmarks which every user can store for themselves. They are stored in the browser's localStorage.
+- Some more bugs in the websocket handling have been fixed.
+
+**2019-09-25**
+- Automatic reporting of spots to [pskreporter](https://pskreporter.info/) is now possible. Please have a look at the
+ configuration on how to set it up.
+- Websocket communication has been overhauled in large parts. It should now be more reliable, and failing connections
+ should now have no impact on other users.
+- Profile scheduling allows to set up band-hopping if you are running background services.
+- APRS now has the ability to show symbols on the map, if a corresponding symbol set has been installed. Check the
+ config!
+- Debug logging has been disabled in a handful of modules, expect vastly reduced output on the shell.
+
+**2019-09-13**
+- New set of APRS-related features
+ - Decode Packet transmissions using [direwolf](https://github.com/wb2osz/direwolf) (1k2 only for now)
+ - APRS packets are mostly decoded and shown both in a new panel and on the map
+ - APRS is also available as a background service
+ - direwolfs I-gate functionality can be enabled, which allows your receiver to work as a receive-only I-gate for the
+ APRS network in the background
+- Demodulation for background services has been optimized to use less total bandwidth, saving CPU
+- More metrics have been added; they can be used together with collectd and its curl_json plugin for now, with some
+ limitations.
+
+**2019-07-21**
+- Latest Features:
+ - More WSJT-X modes have been added, including the new FT4 mode
+ - I started adding a bandplan feature, the first thing visible is the "dial" indicator that brings you right to the
+ dial frequency for digital modes
+ - fixed some bugs in the websocket communication which broke the map
+
+**2019-07-13**
+- Latest Features:
+ - FT8 Integration (using wsjt-x demodulators)
+ - New Map Feature that shows both decoded grid squares from FT8 and Locations decoded from YSF digital voice
+ - New Feature report that will show what functionality is available
+- There's a new Raspbian SD Card image available (see below)
+
+**2019-06-30**
+- I have done some major rework on the openwebrx core, and I am planning to continue adding more features in the near
+ future. Please check this place for updates.
+- My work has not been accepted into the upstream repository, so you will need to chose between my fork and the official
+ version.
+- I have enabled the issue tracker on this project, so feel free to file bugs or suggest enhancements there!
+- This version sports the following new and amazing features:
+ - Support of multiple SDR devices simultaneously
+ - Support for multiple profiles per SDR that allow the user to listen to different frequencies
+ - Support for digital voice decoding
+ - Feature detection that will disable functionality when dependencies are not available (if you're missing the digital
+ buttons, this is probably why)
+- Raspbian SD Card Images and Docker builds available (see below)
+- I am currently working on the feature set for a stable release, but you are more than welcome to test development
+ versions!
diff --git a/openwebrx/LICENSE.txt b/openwebrx/LICENSE.txt
new file mode 100644
index 0000000..dba13ed
--- /dev/null
+++ b/openwebrx/LICENSE.txt
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
diff --git a/openwebrx/README.md b/openwebrx/README.md
new file mode 100644
index 0000000..9517148
--- /dev/null
+++ b/openwebrx/README.md
@@ -0,0 +1,60 @@
+OpenWebRX
+=========
+
+OpenWebRX is a multi-user SDR receiver software with a web interface.
+
+![OpenWebRX](https://www.openwebrx.de/gfx/openwebrx-screenshot.png)
+
+It has the following features:
+
+- [csdr](https://github.com/jketterl/csdr) based demodulators (AM/FM/SSB/CW/BPSK31/BPSK63)
+- filter passband can be set from GUI
+- it extensively uses HTML5 features like WebSocket, Web Audio API, and Canvas
+- it works in Google Chrome, Chromium and Mozilla Firefox
+- supports a wide range of [SDR hardware](https://github.com/jketterl/openwebrx/wiki/Supported-Hardware#sdr-devices)
+- Multiple SDR devices can be used simultaneously
+- [digiham](https://github.com/jketterl/digiham) based demodularors (DMR, YSF, Pocsag, D-Star, NXDN)
+- [wsjt-x](https://physics.princeton.edu/pulsar/k1jt/wsjtx.html) based demodulators (FT8, FT4, WSPR, JT65, JT9, FST4,
+ FST4W)
+- [direwolf](https://github.com/wb2osz/direwolf) based demodulation of APRS packets
+- [JS8Call](http://js8call.com/) support
+- [DRM](https://github.com/jketterl/openwebrx/wiki/DRM-demodulator-notes) support
+- [FreeDV](https://github.com/jketterl/openwebrx/wiki/FreeDV-demodulator-notes) support
+- M17 support based on [m17-cxx-demod](https://github.com/mobilinkd/m17-cxx-demod)
+
+## Setup
+
+The following methods of setting up a receiver are currently available:
+
+- Raspberry Pi SD card images
+- Debian repository
+- Docker images
+- Manual installation
+
+Please checkout the [setup guide on the wiki](https://github.com/jketterl/openwebrx/wiki/Setup-Guide) for more details
+on the respective methods.
+
+## Community
+
+If you have trouble setting up or configuring your receiver, you have some great idea you want to see implemented, or
+you just generally want to have some OpenWebRX-related chat, come visit us over on
+[our groups.io group](https://groups.io/g/openwebrx).
+
+If you want to hang out, chat, or get in touch directly with the developers, receiver operators or users, feel free to
+drop by in [our Discord server](https://discord.gg/gnE9hPz).
+
+## Usage tips
+
+You can zoom the waterfall display by the mouse wheel. You can also drag the waterfall to pan across it.
+
+The filter envelope can be dragged at its ends and moved around to set the passband.
+
+However, if you hold down the shift key, you can drag the center line (BFO) or the whole passband (PBS).
+
+## Licensing
+
+OpenWebRX is available under Affero GPL v3 license
+([summary](https://tldrlegal.com/license/gnu-affero-general-public-license-v3-(agpl-3.0))).
+
+OpenWebRX is also available under a commercial license on request. Please contact me at the address
+*<randras@sdr.hu>* for licensing options.
diff --git a/openwebrx/bands.json b/openwebrx/bands.json
new file mode 100644
index 0000000..02bb703
--- /dev/null
+++ b/openwebrx/bands.json
@@ -0,0 +1,367 @@
+[
+ {
+ "name": "2190m",
+ "lower_bound": 135700,
+ "upper_bound": 137800,
+ "frequencies": {
+ "fst4": 136000,
+ "fst4w": 136000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "630m",
+ "lower_bound": 472000,
+ "upper_bound": 479000,
+ "frequencies": {
+ "fst4": 474200,
+ "fst4w": 474200
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "160m",
+ "lower_bound": 1810000,
+ "upper_bound": 2000000,
+ "frequencies": {
+ "bpsk31": 1838000,
+ "ft8": 1840000,
+ "wspr": 1836600,
+ "jt65": 1838000,
+ "jt9": 1839000,
+ "js8": 1842000,
+ "fst4": 1839000,
+ "fst4w": 1836800
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "80m",
+ "lower_bound": 3500000,
+ "upper_bound": 3800000,
+ "frequencies": {
+ "bpsk31": 3580000,
+ "ft8": 3573000,
+ "wspr": 3592600,
+ "jt65": 3570000,
+ "jt9": 3572000,
+ "ft4": [3568000, 3575000],
+ "js8": 3578000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "60m",
+ "lower_bound": 5351500,
+ "upper_bound": 5366500,
+ "frequencies": {
+ "ft8": 5357000,
+ "wspr": 5364700
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "40m",
+ "lower_bound": 7000000,
+ "upper_bound": 7200000,
+ "frequencies": {
+ "bpsk31": 7040000,
+ "ft8": 7074000,
+ "wspr": 7038600,
+ "jt65": 7076000,
+ "jt9": 7078000,
+ "ft4": 7047500,
+ "js8": 7078000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "30m",
+ "lower_bound": 10100000,
+ "upper_bound": 10150000,
+ "frequencies": {
+ "bpsk31": 10141000,
+ "ft8": 10136000,
+ "wspr": 10138700,
+ "jt65": 10138000,
+ "jt9": 10140000,
+ "ft4": 10140000,
+ "js8": 10130000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "20m",
+ "lower_bound": 14000000,
+ "upper_bound": 14350000,
+ "frequencies": {
+ "bpsk31": 14070000,
+ "ft8": 14074000,
+ "wspr": 14095600,
+ "jt65": 14076000,
+ "jt9": 14078000,
+ "ft4": 14080000,
+ "js8": 14078000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "17m",
+ "lower_bound": 18068000,
+ "upper_bound": 18168000,
+ "frequencies": {
+ "bpsk31": 18098000,
+ "ft8": 18100000,
+ "wspr": 18104600,
+ "jt65": 18102000,
+ "jt9": 18104000,
+ "ft4": 18104000,
+ "js8": 18104000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "15m",
+ "lower_bound": 21000000,
+ "upper_bound": 21450000,
+ "frequencies": {
+ "bpsk31": 21070000,
+ "ft8": 21074000,
+ "wspr": 21094600,
+ "jt65": 21076000,
+ "jt9": 21078000,
+ "ft4": 21140000,
+ "js8": 21078000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "12m",
+ "lower_bound": 24890000,
+ "upper_bound": 24990000,
+ "frequencies": {
+ "bpsk31": 24920000,
+ "ft8": 24915000,
+ "wspr": 24924600,
+ "jt65": 24917000,
+ "jt9": 24919000,
+ "ft4": 24919000,
+ "js8": 24922000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "10m",
+ "lower_bound": 28000000,
+ "upper_bound": 29700000,
+ "frequencies": {
+ "bpsk31": [28070000, 28120000],
+ "ft8": 28074000,
+ "wspr": 28124600,
+ "jt65": 28076000,
+ "jt9": 28078000,
+ "ft4": 28180000,
+ "js8": 28078000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "6m",
+ "lower_bound": 50030000,
+ "upper_bound": 51000000,
+ "frequencies": {
+ "bpsk31": 50305000,
+ "ft8": 50313000,
+ "wspr": 50293000,
+ "jt65": 50310000,
+ "jt9": 50312000,
+ "ft4": 50318000,
+ "js8": 50318000,
+ "q65": [50211000, 50275000]
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "4m",
+ "lower_bound": 70150000,
+ "upper_bound": 70200000,
+ "frequencies": {
+ "wspr": 70091000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "2m",
+ "lower_bound": 144000000,
+ "upper_bound": 146000000,
+ "frequencies": {
+ "wspr": 144489000,
+ "ft8": 144174000,
+ "ft4": 144170000,
+ "jt65": 144120000,
+ "packet": 144800000,
+ "q65": 144116000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "70cm",
+ "lower_bound": 430000000,
+ "upper_bound": 440000000,
+ "frequencies": {
+ "pocsag": 439987500,
+ "q65": 432065000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "23cm",
+ "lower_bound": 1240000000,
+ "upper_bound": 1300000000,
+ "frequencies": {
+ "q65": 1296065000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "13cm",
+ "lower_bound": 2320000000,
+ "upper_bound": 2450000000,
+ "frequencies": {
+ "q65": [2301065000, 2304065000, 2320065000]
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "9cm",
+ "lower_bound": 3400000000,
+ "upper_bound": 3475000000,
+ "frequencies": {
+ "q65": 3400065000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "6cm",
+ "lower_bound": 5650000000,
+ "upper_bound": 5850000000,
+ "frequencies": {
+ "q65": 5760200000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "3cm",
+ "lower_bound": 10000000000,
+ "upper_bound": 10500000000,
+ "frequencies": {
+ "q65": 10368200000
+ },
+ "tags": ["hamradio"]
+ },
+ {
+ "name": "120m Broadcast",
+ "lower_bound": 2300000,
+ "upper_bound": 2495000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "90m Broadcast",
+ "lower_bound": 3200000,
+ "upper_bound": 3400000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "75m Broadcast",
+ "lower_bound": 3900000,
+ "upper_bound": 4000000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "60m Broadcast",
+ "lower_bound": 4750000,
+ "upper_bound": 4995000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "49m Broadcast",
+ "lower_bound": 5900000,
+ "upper_bound": 6200000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "41m Broadcast",
+ "lower_bound": 7200000,
+ "upper_bound": 7450000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "31m Broadcast",
+ "lower_bound": 9400000,
+ "upper_bound": 9900000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "25m Broadcast",
+ "lower_bound": 11600000,
+ "upper_bound": 12100000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "22m Broadcast",
+ "lower_bound": 13570000,
+ "upper_bound": 13870000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "19m Broadcast",
+ "lower_bound": 15100000,
+ "upper_bound": 15830000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "16m Broadcast",
+ "lower_bound": 17480000,
+ "upper_bound": 17900000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "15m Broadcast",
+ "lower_bound": 18900000,
+ "upper_bound": 19020000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "13m Broadcast",
+ "lower_bound": 21450000,
+ "upper_bound": 21850000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "11m Broadcast",
+ "lower_bound": 25670000,
+ "upper_bound": 26100000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "FM Broadcast",
+ "lower_bound": 87500000,
+ "upper_bound": 108000000,
+ "tags": ["broadcast"]
+ },
+ {
+ "name": "11m CB",
+ "lower_bound": 26965000,
+ "upper_bound": 27405000,
+ "frequencies": {
+ "js8": 27245000
+ },
+ "tags": ["public"]
+ },
+ {
+ "name": "PMR446",
+ "lower_bound": 446000000,
+ "upper_bound": 446200000,
+ "tags": ["public"]
+ }
+]
\ No newline at end of file
diff --git a/openwebrx/config_webrx.py b/openwebrx/config_webrx.py
new file mode 100644
index 0000000..f06f50e
--- /dev/null
+++ b/openwebrx/config_webrx.py
@@ -0,0 +1,386 @@
+# -*- coding: utf-8 -*-
+
+"""
+config_webrx: configuration options for OpenWebRX
+
+ This file is part of OpenWebRX,
+ an open-source SDR receiver software with a web UI.
+ Copyright (c) 2013-2015 by Andras Retzler
+ Copyright (c) 2019-2021 by Jakob Ketterl
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ In addition, as a special exception, the copyright holders
+ state that config_rtl.py and config_webrx.py are not part of the
+ Corresponding Source defined in GNU AGPL version 3 section 1.
+
+ (It means that you do not have to redistribute config_rtl.py and
+ config_webrx.py if you make any changes to these two configuration files,
+ and use them for running your web service with OpenWebRX.)
+"""
+
+"""
+DEPRECATION notice
+
+As of OpenWebRX 0.21, the configuration system has been completely overhauled.
+The configuration of OpenWebRX should now be done in the new web-based
+configuration interface exclusively.
+
+Existing configurations can still be used, but their values will be migrated
+to the new storage infrastructure as soon as the web configuration is used to
+edit them.
+
+The new configuration storage is not intended to be edited manually.
+"""
+
+# configuration version. please only modify if you're able to perform the associated migration steps.
+version = 7
+
+# NOTE: you can find additional information about configuring OpenWebRX in the Wiki:
+# https://github.com/jketterl/openwebrx/wiki/Configuration-guide
+
+# ==== Server settings ====
+#max_clients = 20
+
+# ==== Web GUI configuration ====
+#receiver_name = "[Callsign]"
+#receiver_location = "Budapest, Hungary"
+#receiver_asl = 200
+#receiver_admin = "example@example.com"
+#receiver_gps = {"lat": 47.000000, "lon": 19.000000}
+#photo_title = "Panorama of Budapest from Schönherz Zoltán Dormitory"
+# photo_desc allows you to put pretty much any HTML you like into the receiver description.
+# The lines below should give you some examples of what's possible.
+#photo_desc = """
+#You can add your own background photo and receiver information.
+#Receiver is operated by: Receiver Operator
+#Device: Receiver Device
+#Antenna: Receiver Antenna
+#Website: http://localhost
+#"""
+
+# ==== Public receiver listings ====
+# You can publish your receiver on online receiver directories, like https://www.receiverbook.de
+# You will receive a receiver key from the directory that will authenticate you as the operator of this receiver.
+# Please note that you not share your receiver keys publicly since anyone that obtains your receiver key can take over
+# your public listing.
+# Your receiver keys should be placed into this array:
+#receiver_keys = []
+# If you list your receiver on multiple sites, you can place all your keys into the array above, or you can append
+# keys to the arraylike this:
+# receiver_keys += ["my-receiver-key"]
+
+# If you're not sure, simply copy & paste the code you received from your listing site below this line:
+
+# ==== DSP/RX settings ====
+#fft_fps = 9
+#fft_size = 4096 # Should be power of 2
+#fft_voverlap_factor = (
+# 0.3 # If fft_voverlap_factor is above 0, multiple FFTs will be used for creating a line on the diagram.
+#)
+
+#audio_compression = "adpcm" # valid values: "adpcm", "none"
+#fft_compression = "adpcm" # valid values: "adpcm", "none"
+
+# Tau setting for WFM (broadcast FM) deemphasis\
+# Quote from wikipedia https://en.wikipedia.org/wiki/FM_broadcasting#Pre-emphasis_and_de-emphasis
+# "In most of the world a 50 µs time constant is used. In the Americas and South Korea, 75 µs is used"
+# Enable one of the following lines, depending on your location:
+# wfm_deemphasis_tau = 75e-6 # for US and South Korea
+#wfm_deemphasis_tau = 50e-6 # for the rest of the world
+
+#digimodes_fft_size = 2048
+
+# enables lookup of DMR ids using the radioid api
+#digital_voice_dmr_id_lookup = True
+
+"""
+Note: if you experience audio underruns while CPU usage is 100%, you can:
+- decrease `samp_rate`,
+- set `fft_voverlap_factor` to 0,
+- decrease `fft_fps` and `fft_size`,
+- limit the number of users by decreasing `max_clients`.
+"""
+
+# ==== I/Q sources ====
+# (Uncomment the appropriate by removing # characters at the beginning of the corresponding lines.)
+
+###############################################################################
+# Is my SDR hardware supported? #
+# Check here: https://github.com/jketterl/openwebrx/wiki/Supported-Hardware #
+###############################################################################
+
+# Currently supported types of sdr receivers:
+# "rtl_sdr", "rtl_sdr_soapy", "sdrplay", "hackrf", "airspy", "airspyhf", "fifi_sdr",
+# "perseussdr", "lime_sdr", "pluto_sdr", "soapy_remote", "hpsdr", "uhd",
+# "radioberry", "fcdpp", "rtl_tcp", "sddc", "runds"
+
+# For more details on specific types, please checkout the wiki:
+# https://github.com/jketterl/openwebrx/wiki/Supported-Hardware#sdr-devices
+
+#sdrs = {
+# "rtlsdr": {
+# "name": "RTL-SDR USB Stick",
+# "type": "rtl_sdr",
+# "ppm": 0,
+# # you can change this if you use an upconverter. formula is:
+# # center_freq + lfo_offset = actual frequency on the sdr
+# # "lfo_offset": 0,
+# "profiles": {
+# "70cm": {
+# "name": "70cm Relais",
+# "center_freq": 438800000,
+# "rf_gain": 29,
+# "samp_rate": 2400000,
+# "start_freq": 439275000,
+# "start_mod": "nfm",
+# },
+# "2m": {
+# "name": "2m komplett",
+# "center_freq": 145000000,
+# "rf_gain": 29,
+# "samp_rate": 2048000,
+# "start_freq": 145725000,
+# "start_mod": "nfm",
+# },
+# },
+# },
+# "airspy": {
+# "name": "Airspy HF+",
+# "type": "airspyhf",
+# "ppm": 0,
+# "rf_gain": "auto",
+# "profiles": {
+# "20m": {
+# "name": "20m",
+# "center_freq": 14150000,
+# "samp_rate": 384000,
+# "start_freq": 14070000,
+# "start_mod": "usb",
+# },
+# "30m": {
+# "name": "30m",
+# "center_freq": 10125000,
+# "samp_rate": 192000,
+# "start_freq": 10142000,
+# "start_mod": "usb",
+# },
+# "40m": {
+# "name": "40m",
+# "center_freq": 7100000,
+# "samp_rate": 256000,
+# "start_freq": 7070000,
+# "start_mod": "lsb",
+# },
+# "80m": {
+# "name": "80m",
+# "center_freq": 3650000,
+# "samp_rate": 384000,
+# "start_freq": 3570000,
+# "start_mod": "lsb",
+# },
+# "49m": {
+# "name": "49m Broadcast",
+# "center_freq": 6050000,
+# "samp_rate": 384000,
+# "start_freq": 6070000,
+# "start_mod": "am",
+# },
+# },
+# },
+# "sdrplay": {
+# "name": "SDRPlay RSP2",
+# "type": "sdrplay",
+# "ppm": 0,
+# "antenna": "Antenna A",
+# "profiles": {
+# "20m": {
+# "name": "20m",
+# "center_freq": 14150000,
+# "rf_gain": 0,
+# "samp_rate": 500000,
+# "start_freq": 14070000,
+# "start_mod": "usb",
+# },
+# "30m": {
+# "name": "30m",
+# "center_freq": 10125000,
+# "rf_gain": 0,
+# "samp_rate": 250000,
+# "start_freq": 10142000,
+# "start_mod": "usb",
+# },
+# "40m": {
+# "name": "40m",
+# "center_freq": 7100000,
+# "rf_gain": 0,
+# "samp_rate": 500000,
+# "start_freq": 7070000,
+# "start_mod": "lsb",
+# },
+# "80m": {
+# "name": "80m",
+# "center_freq": 3650000,
+# "rf_gain": 0,
+# "samp_rate": 500000,
+# "start_freq": 3570000,
+# "start_mod": "lsb",
+# },
+# "49m": {
+# "name": "49m Broadcast",
+# "center_freq": 6000000,
+# "rf_gain": 0,
+# "samp_rate": 500000,
+# "start_freq": 6070000,
+# "start_mod": "am",
+# },
+# },
+# },
+#}
+
+# ==== Color themes ====
+
+### google turbo colormap (see: https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html)
+#waterfall_scheme = "GoogleTurboWaterfall"
+
+### original theme by teejez:
+#waterfall_scheme = "TeejeezWaterfall"
+
+### old theme by HA7ILM:
+#waterfall_scheme = "Ha7ilmWaterfall"
+##For the old colors, you might also want to set [fft_voverlap_factor] to 0.
+
+### custom waterfall schemes can be configured like this:
+#waterfall_scheme = "CustomWaterfall"
+#waterfall_colors = [0x0000FF, 0x00FF00, 0xFF0000]
+
+### Waterfall calibration
+#waterfall_levels = {"min": -88, "max": -20} # in dB
+
+#waterfall_auto_levels = {"min": 3, "max": 10}
+#waterfall_auto_min_range = 50
+
+# Note: When the auto waterfall level button is clicked, the following happens:
+# [waterfall_levels.min] = [current_min_power_level] - [waterfall_auto_levels["min"]]
+# [waterfall_levels.max] = [current_max_power_level] + [waterfall_auto_levels["max"]]
+#
+# ___|__________________________________|____________________________________|__________________________________|___> signal power
+# \_waterfall_auto_levels["min"]_/ |__ current_min_power_level | \_waterfall_auto_levels["max"]_/
+# current_max_power_level __|
+
+# This setting allows you to modify the precision of the frequency displays in OpenWebRX.
+# Set this to exponent of 10 to select the most precise digit in Hz you'd like to see
+# examples:
+# a value of 2 selects 10^2 = 100Hz tuning precision (default):
+#tuning_precision = 2
+# a value of 1 selects 10^1 = 10Hz tuning precision:
+#tuning_precision = 1
+
+# This setting tells the auto-squelch the offset to add to the current signal level to use as the new squelch level.
+# Lowering this setting will give you a more sensitive squelch, but it may also cause unwanted squelch openings when
+# using the auto squelch.
+#squelch_auto_margin = 10 # in dB
+
+#google_maps_api_key = ""
+
+# how long should positions be visible on the map?
+# they will start fading out after half of that
+# in seconds; default: 2 hours
+#map_position_retention_time = 2 * 60 * 60
+
+# decoder queue configuration
+# due to the nature of some operating modes (ft8, ft8, jt9, jt65, wspr and js8), the data is recorded for a given amount
+# of time (6 seconds up to 2 minutes) and decoded at the end. this can lead to very high peak loads.
+# to mitigate this, the recordings will be queued and processed in sequence.
+# the number of workers will limit the total amount of work (one worker will losely occupy one cpu / thread)
+#decoding_queue_workers = 2
+# the maximum queue length will cause decodes to be dumped if the workers cannot keep up
+# if you are running background services, make sure this number is high enough to accept the task influx during peaks
+# i.e. this should be higher than the number of decoding services running at the same time
+#decoding_queue_length = 10
+
+# wsjt decoding depth will allow more results, but will also consume more cpu
+#wsjt_decoding_depth = 3
+# can also be set for each mode separately
+# jt65 seems to be somewhat prone to erroneous decodes, this setting handles that to some extent
+#wsjt_decoding_depths = {"jt65": 1}
+
+# FST4 can be transmitted in different intervals. This setting determines which intervals will be decoded.
+# available values (in seconds): 15, 30, 60, 120, 300, 900, 1800
+#fst4_enabled_intervals = [15, 30]
+
+# FST4W can be transmitted in different intervals. This setting determines which intervals will be decoded.
+# available values (in seconds): 120, 300, 900, 1800
+#fst4w_enabled_intervals = [120, 300]
+
+# Q65 allows many combinations of intervals and submodes. This setting determines which combinations will be decoded.
+# Please use the mode letter followed by the decode interval in seconds to specify the combinations. For example:
+#q65_enabled_combinations = ["A30", "E120", "C60"]
+
+# JS8 comes in different speeds: normal, slow, fast, turbo. This setting controls which ones are enabled.
+#js8_enabled_profiles = ["normal", "slow"]
+# JS8 decoding depth; higher value will get more results, but will also consume more cpu
+#js8_decoding_depth = 3
+
+# Enable background service for decoding digital data. You can find more information at:
+# https://github.com/jketterl/openwebrx/wiki/Background-decoding
+#services_enabled = False
+#services_decoders = ["ft8", "ft4", "wspr", "packet"]
+
+# === aprs igate settings ===
+# If you want to share your APRS decodes with the aprs network, configure these settings accordingly.
+# Make sure that you have set services_enabled to true and customize services_decoders to your needs.
+#aprs_callsign = "N0CALL"
+#aprs_igate_enabled = False
+#aprs_igate_server = "euro.aprs2.net"
+#aprs_igate_password = ""
+# beacon uses the receiver_gps setting, so if you enable this, make sure the location is correct there
+#aprs_igate_beacon = False
+
+# Uncomment the following to customize gateway beacon details reported to the aprs network
+# Plese see Dire Wolf's documentation on PBEACON configuration for complete details:
+# https://github.com/wb2osz/direwolf/raw/master/doc/User-Guide.pdf
+
+# Symbol in its two-character form as specified by the APRS spec at http://www.aprs.org/symbols/symbols-new.txt
+# Default: Receive only IGate (do not send msgs back to RF)
+# aprs_igate_symbol = "R&"
+
+# Custom comment about igate
+# Default: OpenWebRX APRS gateway
+# aprs_igate_comment = "OpenWebRX APRS gateway"
+
+# Antenna Height and Gain details
+# Unspecified by default
+# Antenna height above average terrain (HAAT) in meters
+# aprs_igate_height = "5"
+# Antenna gain in dBi
+# aprs_igate_gain = "0"
+# Antenna direction (N, NE, E, SE, S, SW, W, NW). Omnidirectional by default
+# aprs_igate_dir = "NE"
+
+# === PSK Reporter settings ===
+# enable this if you want to upload all ft8, ft4 etc spots to pskreporter.info
+# this also uses the receiver_gps setting from above, so make sure it contains a correct locator
+#pskreporter_enabled = False
+#pskreporter_callsign = "N0CALL"
+# optional antenna information, uncomment to enable
+#pskreporter_antenna_information = "Dipole"
+
+# === WSPRNet reporting settings
+# enable this if you want to upload WSPR spots to wsprnet.ort
+# in addition to these settings also make sure that receiver_gps contains your correct location
+#wsprnet_enabled = False
+#wsprnet_callsign = "N0CALL"
diff --git a/openwebrx/csdr/__init__.py b/openwebrx/csdr/__init__.py
new file mode 100644
index 0000000..32e2b09
--- /dev/null
+++ b/openwebrx/csdr/__init__.py
@@ -0,0 +1,837 @@
+"""
+OpenWebRX csdr plugin: do the signal processing with csdr
+
+ This file is part of OpenWebRX,
+ an open-source SDR receiver software with a web UI.
+ Copyright (c) 2013-2015 by Andras Retzler
+ Copyright (c) 2019-2021 by Jakob Ketterl
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+"""
+
+import subprocess
+import os
+import signal
+import threading
+import math
+from functools import partial
+
+from csdr.output import Output
+
+from owrx.kiss import KissClient, DirewolfConfig, DirewolfConfigSubscriber
+from owrx.audio.chopper import AudioChopper
+
+from csdr.pipe import Pipe
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+class Dsp(DirewolfConfigSubscriber):
+ def __init__(self, output: Output):
+ self.samp_rate = 250000
+ self.output_rate = 11025
+ self.hd_output_rate = 44100
+ self.fft_size = 1024
+ self.fft_fps = 5
+ self.center_freq = 0
+ self.offset_freq = 0
+ self.low_cut = -4000
+ self.high_cut = 4000
+ self.bpf_transition_bw = 320 # Hz, and this is a constant
+ self.ddc_transition_bw_rate = 0.15 # of the IF sample rate
+ self.running = False
+ self.secondary_processes_running = False
+ self.audio_compression = "none"
+ self.fft_compression = "none"
+ self.demodulator = "nfm"
+ self.name = "csdr"
+ self.base_bufsize = 512
+ self.decimation = None
+ self.last_decimation = None
+ self.nc_port = None
+ self.squelch_level = -150
+ self.fft_averages = 50
+ self.wfm_deemphasis_tau = 50e-6
+ self.iqtee = False
+ self.iqtee2 = False
+ self.secondary_demodulator = None
+ self.secondary_fft_size = 1024
+ self.secondary_process_fft = None
+ self.secondary_process_demod = None
+ self.pipe_names = {
+ "bpf_pipe": Pipe.WRITE,
+ "shift_pipe": Pipe.WRITE,
+ "squelch_pipe": Pipe.WRITE,
+ "smeter_pipe": Pipe.READ,
+ "meta_pipe": Pipe.READ,
+ "iqtee_pipe": Pipe.NONE,
+ "iqtee2_pipe": Pipe.NONE,
+ "dmr_control_pipe": Pipe.WRITE,
+ }
+ self.pipes = {}
+ self.secondary_pipe_names = {"secondary_shift_pipe": Pipe.WRITE}
+ self.secondary_offset_freq = 1000
+ self.codecserver = None
+ self.modification_lock = threading.Lock()
+ self.output = output
+
+ self.temporary_directory = None
+ self.pipe_base_path = None
+ self.set_temporary_directory("/tmp")
+
+ self.is_service = False
+ self.direwolf_config = None
+ self.direwolf_config_path = None
+ self.process = None
+
+ def set_service(self, flag=True):
+ self.is_service = flag
+
+ def set_temporary_directory(self, what):
+ self.temporary_directory = what
+ self.pipe_base_path = "{tmp_dir}/openwebrx_pipe_".format(tmp_dir=self.temporary_directory)
+
+ def chain(self, which):
+ chain = ["nc -v 127.0.0.1 {nc_port}"]
+ if which == "fft":
+ chain += [
+ "csdr fft_cc {fft_size} {fft_block_size}",
+ "csdr logpower_cf -70"
+ if self.fft_averages == 0
+ else "csdr logaveragepower_cf -70 {fft_size} {fft_averages}",
+ "csdr fft_exchange_sides_ff {fft_size}",
+ ]
+ if self.fft_compression == "adpcm":
+ chain += ["csdr compress_fft_adpcm_f_u8 {fft_size}"]
+ return chain
+ chain += ["csdr shift_addfast_cc --fifo {shift_pipe}"]
+ if self.decimation > 1:
+ chain += ["csdr fir_decimate_cc {decimation} {ddc_transition_bw} HAMMING"]
+ chain += ["csdr bandpass_fir_fft_cc --fifo {bpf_pipe} {bpf_transition_bw} HAMMING"]
+ if self.output.supports_type("smeter"):
+ chain += [
+ "csdr squelch_and_smeter_cc --fifo {squelch_pipe} --outfifo {smeter_pipe} 5 {smeter_report_every}"
+ ]
+ if self.secondary_demodulator:
+ if self.output.supports_type("secondary_fft"):
+ chain += ["csdr tee {iqtee_pipe}"]
+ chain += ["csdr tee {iqtee2_pipe}"]
+ # early exit if we don't want audio
+ if not self.output.supports_type("audio"):
+ return chain
+ # safe some cpu cycles... no need to decimate if decimation factor is 1
+ last_decimation_block = []
+ if self.last_decimation >= 2.0:
+ # activate prefilter if signal has been oversampled, e.g. WFM
+ last_decimation_block = ["csdr fractional_decimator_ff {last_decimation} 12 --prefilter"]
+ elif self.last_decimation != 1.0:
+ last_decimation_block = ["csdr fractional_decimator_ff {last_decimation}"]
+ if which == "nfm":
+ chain += ["csdr fmdemod_quadri_cf", "csdr limit_ff"]
+ chain += last_decimation_block
+ chain += [
+ "csdr deemphasis_nfm_ff {audio_rate}",
+ "csdr agc_ff --profile slow --max 3",
+ ]
+ if self.get_audio_rate() != self.get_output_rate():
+ chain += [
+ "sox -t raw -r {audio_rate} -e floating-point -b 32 -c 1 --buffer 32 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - "
+ ]
+ else:
+ chain += ["csdr convert_f_s16"]
+ elif which == "wfm":
+ chain += [
+ "csdr fmdemod_quadri_cf",
+ "csdr limit_ff",
+ ]
+ chain += last_decimation_block
+ chain += ["csdr deemphasis_wfm_ff {audio_rate} {wfm_deemphasis_tau}", "csdr convert_f_s16"]
+ elif self.isDigitalVoice(which):
+ chain += ["csdr fmdemod_quadri_cf"]
+ chain += last_decimation_block
+ chain += ["dc_block"]
+ # m17
+ if which == "m17":
+ chain += [
+ "csdr limit_ff",
+ "csdr convert_f_s16",
+ "m17-demod",
+ ]
+ else:
+ # digiham modes
+ if which == "dstar":
+ chain += [
+ "fsk_demodulator -s 10",
+ "dstar_decoder --fifo {meta_pipe}",
+ "mbe_synthesizer -d {codecserver_arg}",
+ ]
+ elif which == "nxdn":
+ chain += [
+ "rrc_filter --narrow",
+ "gfsk_demodulator --samples 20",
+ "nxdn_decoder --fifo {meta_pipe}",
+ "mbe_synthesizer {codecserver_arg}",
+ ]
+ else:
+ chain += ["rrc_filter", "gfsk_demodulator"]
+ if which == "dmr":
+ chain += [
+ "dmr_decoder --fifo {meta_pipe} --control-fifo {dmr_control_pipe}",
+ "mbe_synthesizer {codecserver_arg}",
+ ]
+ elif which == "ysf":
+ chain += ["ysf_decoder --fifo {meta_pipe}", "mbe_synthesizer -y {codecserver_arg}"]
+ chain += ["digitalvoice_filter"]
+ chain += [
+ "CSDR_FIXED_BUFSIZE=32 csdr agc_s16 --max 30 --initial 3",
+ "sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --buffer 32 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - ",
+ ]
+ elif which == "am":
+ chain += ["csdr amdemod_cf", "csdr fastdcblock_ff"]
+ chain += last_decimation_block
+ chain += [
+ "csdr agc_ff --profile slow --initial 200",
+ "csdr convert_f_s16",
+ ]
+ elif self.isFreeDV(which):
+ chain += ["csdr realpart_cf"]
+ chain += last_decimation_block
+ chain += [
+ "csdr agc_ff",
+ "csdr convert_f_s16",
+ "freedv_rx 1600 - -",
+ "csdr agc_s16 --max 30 --initial 3",
+ "sox -t raw -r 8000 -e signed-integer -b 16 -c 1 --buffer 32 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - ",
+ ]
+ elif self.isDrm(which):
+ if self.last_decimation != 1.0:
+ # we are still dealing with complex samples here, so the regular last_decimation_block doesn't fit
+ chain += ["csdr fractional_decimator_cc {last_decimation}"]
+ chain += [
+ "csdr convert_f_s16",
+ "dream -c 6 --sigsrate 48000 --audsrate 48000 -I - -O -",
+ "sox -t raw -r 48000 -e signed-integer -b 16 -c 2 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - ",
+ ]
+ elif which == "ssb":
+ chain += ["csdr realpart_cf"]
+ chain += last_decimation_block
+ chain += ["csdr agc_ff"]
+ # fixed sample rate necessary for the wsjt-x tools. fix with sox...
+ if self.get_audio_rate() != self.get_output_rate():
+ chain += [
+ "sox -t raw -r {audio_rate} -e floating-point -b 32 -c 1 --buffer 32 - -t raw -r {output_rate} -e signed-integer -b 16 -c 1 - "
+ ]
+ else:
+ chain += ["csdr convert_f_s16"]
+
+ if self.audio_compression == "adpcm":
+ chain += ["csdr encode_ima_adpcm_i16_u8"]
+ return chain
+
+ def secondary_chain(self, which):
+ chain = ["cat {input_pipe}"]
+ if which == "fft":
+ chain += [
+ "csdr fft_cc {secondary_fft_input_size} {secondary_fft_block_size}",
+ "csdr logpower_cf -70"
+ if self.fft_averages == 0
+ else "csdr logaveragepower_cf -70 {secondary_fft_size} {fft_averages}",
+ "csdr fft_exchange_sides_ff {secondary_fft_input_size}",
+ ]
+ if self.fft_compression == "adpcm":
+ chain += ["csdr compress_fft_adpcm_f_u8 {secondary_fft_size}"]
+ return chain
+ elif which == "bpsk31" or which == "bpsk63":
+ return chain + [
+ "csdr shift_addfast_cc --fifo {secondary_shift_pipe}",
+ "csdr bandpass_fir_fft_cc -{secondary_bpf_cutoff} {secondary_bpf_cutoff} {secondary_bpf_cutoff}",
+ "csdr simple_agc_cc 0.001 0.5",
+ "csdr timing_recovery_cc GARDNER {secondary_samples_per_bits} 0.5 2 --add_q",
+ "CSDR_FIXED_BUFSIZE=1 csdr dbpsk_decoder_c_u8",
+ "CSDR_FIXED_BUFSIZE=1 csdr psk31_varicode_decoder_u8_u8",
+ ]
+ elif self.isWsjtMode(which) or self.isJs8(which):
+ chain += ["csdr realpart_cf"]
+ if self.last_decimation != 1.0:
+ chain += ["csdr fractional_decimator_ff {last_decimation}"]
+ return chain + ["csdr agc_ff", "csdr convert_f_s16"]
+ elif which == "packet":
+ chain += ["csdr fmdemod_quadri_cf"]
+ if self.last_decimation != 1.0:
+ chain += ["csdr fractional_decimator_ff {last_decimation}"]
+ return chain + ["csdr convert_f_s16", "direwolf -c {direwolf_config} -r {audio_rate} -t 0 -q d -q h 1>&2"]
+ elif which == "pocsag":
+ chain += ["csdr fmdemod_quadri_cf"]
+ if self.last_decimation != 1.0:
+ chain += ["csdr fractional_decimator_ff {last_decimation}"]
+ return chain + ["fsk_demodulator -i", "pocsag_decoder"]
+
+ def set_secondary_demodulator(self, what):
+ if self.get_secondary_demodulator() == what:
+ return
+ self.secondary_demodulator = what
+ self.calculate_decimation()
+ self.restart()
+
+ def secondary_fft_block_size(self):
+ base = (self.samp_rate / self.decimation) / (self.fft_fps * 2)
+ if self.fft_averages == 0:
+ return base
+ return base / self.fft_averages
+
+ def secondary_decimation(self):
+ return 1 # currently unused
+
+ def secondary_bpf_cutoff(self):
+ if self.secondary_demodulator == "bpsk31":
+ return 31.25 / self.if_samp_rate()
+ elif self.secondary_demodulator == "bpsk63":
+ return 62.5 / self.if_samp_rate()
+ return 0
+
+ def secondary_bpf_transition_bw(self):
+ if self.secondary_demodulator == "bpsk31":
+ return 31.25 / self.if_samp_rate()
+ elif self.secondary_demodulator == "bpsk63":
+ return 62.5 / self.if_samp_rate()
+ return 0
+
+ def secondary_samples_per_bits(self):
+ if self.secondary_demodulator == "bpsk31":
+ return int(round(self.if_samp_rate() / 31.25)) & ~3
+ elif self.secondary_demodulator == "bpsk63":
+ return int(round(self.if_samp_rate() / 62.5)) & ~3
+ return 0
+
+ def secondary_bw(self):
+ if self.secondary_demodulator == "bpsk31":
+ return 31.25
+ elif self.secondary_demodulator == "bpsk63":
+ return 62.5
+
+ def start_secondary_demodulator(self):
+ if not self.secondary_demodulator:
+ return
+ logger.debug("starting secondary demodulator from IF input sampled at %d" % self.if_samp_rate())
+ secondary_command_demod = " | ".join(self.secondary_chain(self.secondary_demodulator))
+ self.try_create_pipes(self.secondary_pipe_names, secondary_command_demod)
+ self.try_create_configs(secondary_command_demod)
+
+ secondary_command_demod = secondary_command_demod.format(
+ input_pipe=self.pipes["iqtee2_pipe"],
+ secondary_shift_pipe=self.pipes["secondary_shift_pipe"],
+ secondary_decimation=self.secondary_decimation(),
+ secondary_samples_per_bits=self.secondary_samples_per_bits(),
+ secondary_bpf_cutoff=self.secondary_bpf_cutoff(),
+ secondary_bpf_transition_bw=self.secondary_bpf_transition_bw(),
+ if_samp_rate=self.if_samp_rate(),
+ last_decimation=self.last_decimation,
+ audio_rate=self.get_audio_rate(),
+ direwolf_config=self.direwolf_config_path,
+ )
+
+ logger.debug("secondary command (demod) = %s", secondary_command_demod)
+ if self.output.supports_type("secondary_fft"):
+ secondary_command_fft = " | ".join(self.secondary_chain("fft"))
+ secondary_command_fft = secondary_command_fft.format(
+ input_pipe=self.pipes["iqtee_pipe"],
+ secondary_fft_input_size=self.secondary_fft_size,
+ secondary_fft_size=self.secondary_fft_size,
+ secondary_fft_block_size=self.secondary_fft_block_size(),
+ fft_averages=self.fft_averages,
+ )
+ logger.debug("secondary command (fft) = %s", secondary_command_fft)
+
+ self.secondary_process_fft = subprocess.Popen(
+ secondary_command_fft, stdout=subprocess.PIPE, shell=True, start_new_session=True
+ )
+ self.output.send_output(
+ "secondary_fft",
+ partial(self.secondary_process_fft.stdout.read, int(self.get_secondary_fft_bytes_to_read())),
+ )
+
+ # direwolf does not provide any meaningful data on stdout
+ # more specifically, it doesn't provide any data. if however, for any strange reason, it would start to do so,
+ # it would block if not read. by piping it to devnull, we avoid a potential pitfall here.
+ secondary_output = subprocess.DEVNULL if self.isPacket() else subprocess.PIPE
+ self.secondary_process_demod = subprocess.Popen(
+ secondary_command_demod, stdout=secondary_output, shell=True, start_new_session=True
+ )
+ self.secondary_processes_running = True
+
+ if self.isWsjtMode() or self.isJs8():
+ chopper = AudioChopper(self, self.get_secondary_demodulator())
+ chopper.send_output("audio", self.secondary_process_demod.stdout.read)
+ output_type = "js8_demod" if self.isJs8() else "wsjt_demod"
+ self.output.send_output(output_type, chopper.read)
+ elif self.isPacket():
+ # we best get the ax25 packets from the kiss socket
+ kiss = KissClient(self.direwolf_config.getPort())
+ self.output.send_output("packet_demod", kiss.read)
+ elif self.isPocsag():
+ self.output.send_output("pocsag_demod", self.secondary_process_demod.stdout.readline)
+ else:
+ self.output.send_output("secondary_demod", partial(self.secondary_process_demod.stdout.read, 1))
+
+ # open control pipes for csdr and send initialization data
+ if self.has_pipe("secondary_shift_pipe"): # TODO digimodes
+ self.set_secondary_offset_freq(self.secondary_offset_freq) # TODO digimodes
+
+ def set_secondary_offset_freq(self, value):
+ self.secondary_offset_freq = value
+ if self.secondary_processes_running and self.has_pipe("secondary_shift_pipe"):
+ self.pipes["secondary_shift_pipe"].write(
+ "%g\n" % (-float(self.secondary_offset_freq) / self.if_samp_rate())
+ )
+
+ def stop_secondary_demodulator(self):
+ if not self.secondary_processes_running:
+ return
+ self.try_delete_pipes(self.secondary_pipe_names)
+ self.try_delete_configs()
+ if self.secondary_process_fft:
+ try:
+ os.killpg(os.getpgid(self.secondary_process_fft.pid), signal.SIGTERM)
+ # drain any leftover data to free file descriptors
+ self.secondary_process_fft.communicate()
+ self.secondary_process_fft = None
+ except ProcessLookupError:
+ # been killed by something else, ignore
+ pass
+ if self.secondary_process_demod:
+ try:
+ os.killpg(os.getpgid(self.secondary_process_demod.pid), signal.SIGTERM)
+ # drain any leftover data to free file descriptors
+ self.secondary_process_demod.communicate()
+ self.secondary_process_demod = None
+ except ProcessLookupError:
+ # been killed by something else, ignore
+ pass
+ self.secondary_processes_running = False
+
+ def get_secondary_demodulator(self):
+ return self.secondary_demodulator
+
+ def set_secondary_fft_size(self, secondary_fft_size):
+ if self.secondary_fft_size == secondary_fft_size:
+ return
+ self.secondary_fft_size = secondary_fft_size
+ self.restart()
+
+ def set_audio_compression(self, what):
+ if self.audio_compression == what:
+ return
+ self.audio_compression = what
+ self.restart()
+
+ def get_audio_bytes_to_read(self):
+ # desired latency: 5ms
+ # uncompressed audio has 16 bits = 2 bytes per sample
+ base = self.output_rate * 0.005 * 2
+ # adpcm compresses the bitstream by 4
+ if self.audio_compression == "adpcm":
+ base = base / 4
+ return int(base)
+
+ def set_fft_compression(self, what):
+ if self.fft_compression == what:
+ return
+ self.fft_compression = what
+ self.restart()
+
+ def get_fft_bytes_to_read(self):
+ if self.fft_compression == "none":
+ return self.fft_size * 4
+ if self.fft_compression == "adpcm":
+ return int((self.fft_size / 2) + (10 / 2))
+
+ def get_secondary_fft_bytes_to_read(self):
+ if self.fft_compression == "none":
+ return self.secondary_fft_size * 4
+ if self.fft_compression == "adpcm":
+ return (self.secondary_fft_size / 2) + (10 / 2)
+
+ def set_samp_rate(self, samp_rate):
+ self.samp_rate = samp_rate
+ self.calculate_decimation()
+ if self.running:
+ self.restart()
+
+ def calculate_decimation(self):
+ (self.decimation, self.last_decimation) = self.get_decimation(self.samp_rate, self.get_audio_rate())
+
+ def get_decimation(self, input_rate, output_rate):
+ if output_rate <= 0:
+ raise ValueError("invalid output rate: {rate}".format(rate=output_rate))
+ decimation = 1
+ target_rate = output_rate
+ # wideband fm has a much higher frequency deviation (75kHz).
+ # we cannot cover this if we immediately decimate to the sample rate the audio will have later on, so we need
+ # to compensate here.
+ if self.get_demodulator() == "wfm" and output_rate < 200000:
+ target_rate = 200000
+ while input_rate / (decimation + 1) >= target_rate:
+ decimation += 1
+ fraction = float(input_rate / decimation) / output_rate
+ return decimation, fraction
+
+ def if_samp_rate(self):
+ return self.samp_rate / self.decimation
+
+ def get_name(self):
+ return self.name
+
+ def get_output_rate(self):
+ return self.output_rate
+
+ def get_hd_output_rate(self):
+ return self.hd_output_rate
+
+ def get_audio_rate(self):
+ if self.isDigitalVoice() or self.isPacket() or self.isPocsag() or self.isDrm():
+ return 48000
+ elif self.isWsjtMode() or self.isJs8():
+ return 12000
+ elif self.isFreeDV():
+ return 8000
+ elif self.isHdAudio():
+ return self.get_hd_output_rate()
+ return self.get_output_rate()
+
+ def isDigitalVoice(self, demodulator=None):
+ if demodulator is None:
+ demodulator = self.get_demodulator()
+ return demodulator in ["dmr", "dstar", "nxdn", "ysf", "m17"]
+
+ def isWsjtMode(self, demodulator=None):
+ if demodulator is None:
+ demodulator = self.get_secondary_demodulator()
+ return demodulator in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w", "q65"]
+
+ def isJs8(self, demodulator=None):
+ if demodulator is None:
+ demodulator = self.get_secondary_demodulator()
+ return demodulator == "js8"
+
+ def isPacket(self, demodulator=None):
+ if demodulator is None:
+ demodulator = self.get_secondary_demodulator()
+ return demodulator == "packet"
+
+ def isPocsag(self, demodulator=None):
+ if demodulator is None:
+ demodulator = self.get_secondary_demodulator()
+ return demodulator == "pocsag"
+
+ def isFreeDV(self, demodulator=None):
+ if demodulator is None:
+ demodulator = self.get_demodulator()
+ return demodulator == "freedv"
+
+ def isHdAudio(self, demodulator=None):
+ if demodulator is None:
+ demodulator = self.get_demodulator()
+ return demodulator == "wfm"
+
+ def isDrm(self, demodulator=None):
+ if demodulator is None:
+ demodulator = self.get_demodulator()
+ return demodulator == "drm"
+
+ def set_output_rate(self, output_rate):
+ if self.output_rate == output_rate:
+ return
+ self.output_rate = output_rate
+ self.calculate_decimation()
+ self.restart()
+
+ def set_hd_output_rate(self, hd_output_rate):
+ if self.hd_output_rate == hd_output_rate:
+ return
+ self.hd_output_rate = hd_output_rate
+ self.calculate_decimation()
+ self.restart()
+
+ def set_demodulator(self, demodulator):
+ if demodulator in ["usb", "lsb", "cw"]:
+ demodulator = "ssb"
+ if self.demodulator == demodulator:
+ return
+ self.demodulator = demodulator
+ self.calculate_decimation()
+ self.restart()
+
+ def get_demodulator(self):
+ return self.demodulator
+
+ def set_fft_size(self, fft_size):
+ if self.fft_size == fft_size:
+ return
+ self.fft_size = fft_size
+ self.restart()
+
+ def set_fft_fps(self, fft_fps):
+ self.fft_fps = fft_fps
+ self.restart()
+
+ def set_fft_averages(self, fft_averages):
+ self.fft_averages = fft_averages
+ self.restart()
+
+ def fft_block_size(self):
+ if self.fft_averages == 0:
+ return self.samp_rate / self.fft_fps
+ else:
+ return self.samp_rate / self.fft_fps / self.fft_averages
+
+ def set_offset_freq(self, offset_freq):
+ if offset_freq is None:
+ return
+ self.offset_freq = offset_freq
+ if self.running:
+ self.pipes["shift_pipe"].write("%g\n" % (-float(self.offset_freq) / self.samp_rate))
+
+ def set_center_freq(self, center_freq):
+ # dsp only needs to know this to be able to pass it to decoders in the form of get_operating_freq()
+ self.center_freq = center_freq
+
+ def get_operating_freq(self):
+ return self.center_freq + self.offset_freq
+
+ def set_bandpass(self, bandpass):
+ self.set_bpf(bandpass.low_cut, bandpass.high_cut)
+
+ def set_bpf(self, low_cut, high_cut):
+ self.low_cut = low_cut
+ self.high_cut = high_cut
+ if self.running:
+ self.pipes["bpf_pipe"].write(
+ "%g %g\n" % (float(self.low_cut) / self.if_samp_rate(), float(self.high_cut) / self.if_samp_rate())
+ )
+
+ def get_bpf(self):
+ return [self.low_cut, self.high_cut]
+
+ def convertToLinear(self, db):
+ return float(math.pow(10, db / 10))
+
+ def set_squelch_level(self, squelch_level):
+ self.squelch_level = squelch_level
+ # no squelch required on digital voice modes
+ actual_squelch = (
+ -150
+ if self.isDigitalVoice() or self.isPacket() or self.isPocsag() or self.isFreeDV() or self.isDrm()
+ else self.squelch_level
+ )
+ if self.running:
+ self.pipes["squelch_pipe"].write("%g\n" % (self.convertToLinear(actual_squelch)))
+
+ def set_codecserver(self, s):
+ if self.codecserver == s:
+ return
+ self.codecserver = s
+ self.restart()
+
+ def get_codecserver_arg(self):
+ return "-s {}".format(self.codecserver) if self.codecserver else ""
+
+ def set_dmr_filter(self, filter):
+ if self.has_pipe("dmr_control_pipe"):
+ self.pipes["dmr_control_pipe"].write("{0}\n".format(filter))
+
+ def set_wfm_deemphasis_tau(self, tau):
+ if self.wfm_deemphasis_tau == tau:
+ return
+ self.wfm_deemphasis_tau = tau
+ self.restart()
+
+ def ddc_transition_bw(self):
+ return self.ddc_transition_bw_rate * (self.if_samp_rate() / float(self.samp_rate))
+
+ def try_create_pipes(self, pipe_names, command_base):
+ for pipe_name, pipe_type in pipe_names.items():
+ if self.has_pipe(pipe_name):
+ logger.warning("{pipe_name} is still in use", pipe_name=pipe_name)
+ self.pipes[pipe_name].close()
+ if "{" + pipe_name + "}" in command_base:
+ p = self.pipe_base_path + pipe_name
+ encoding = None
+ # TODO make digiham output unicode and then change this here
+ # the whole pipe enoding feature onlye exists because of this
+ if pipe_name == "meta_pipe":
+ encoding = "cp437"
+ self.pipes[pipe_name] = Pipe.create(p, pipe_type, encoding=encoding)
+ else:
+ self.pipes[pipe_name] = None
+
+ def has_pipe(self, name):
+ return name in self.pipes and self.pipes[name] is not None
+
+ def try_delete_pipes(self, pipe_names):
+ for pipe_name in pipe_names:
+ if self.has_pipe(pipe_name):
+ self.pipes[pipe_name].close()
+ self.pipes[pipe_name] = None
+
+ def try_create_configs(self, command):
+ if "{direwolf_config}" in command:
+ self.direwolf_config_path = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format(
+ tmp_dir=self.temporary_directory, myid=id(self)
+ )
+ self.direwolf_config = DirewolfConfig()
+ self.direwolf_config.wire(self)
+ file = open(self.direwolf_config_path, "w")
+ file.write(self.direwolf_config.getConfig(self.is_service))
+ file.close()
+ else:
+ self.direwolf_config = None
+ self.direwolf_config_path = None
+
+ def try_delete_configs(self):
+ if self.direwolf_config is not None:
+ self.direwolf_config.unwire(self)
+ self.direwolf_config = None
+ if self.direwolf_config_path is not None:
+ try:
+ os.unlink(self.direwolf_config_path)
+ except FileNotFoundError:
+ # result suits our expectations. fine :)
+ pass
+ except Exception:
+ logger.exception("try_delete_configs()")
+ self.direwolf_config_path = None
+
+ def onConfigChanged(self):
+ self.restart()
+
+ def start(self):
+ with self.modification_lock:
+ if self.running:
+ return
+ self.running = True
+
+ command_base = " | ".join(self.chain(self.demodulator))
+
+ # create control pipes for csdr
+ self.try_create_pipes(self.pipe_names, command_base)
+
+ # send initial config through the pipes
+ if self.has_pipe("bpf_pipe"):
+ self.set_bpf(self.low_cut, self.high_cut)
+ if self.has_pipe("shift_pipe"):
+ self.set_offset_freq(self.offset_freq)
+ if self.has_pipe("squelch_pipe"):
+ self.set_squelch_level(self.squelch_level)
+ if self.has_pipe("dmr_control_pipe"):
+ self.set_dmr_filter(3)
+
+ # run the command
+ command = command_base.format(
+ bpf_pipe=self.pipes["bpf_pipe"],
+ shift_pipe=self.pipes["shift_pipe"],
+ squelch_pipe=self.pipes["squelch_pipe"],
+ smeter_pipe=self.pipes["smeter_pipe"],
+ meta_pipe=self.pipes["meta_pipe"],
+ iqtee_pipe=self.pipes["iqtee_pipe"],
+ iqtee2_pipe=self.pipes["iqtee2_pipe"],
+ dmr_control_pipe=self.pipes["dmr_control_pipe"],
+ decimation=self.decimation,
+ last_decimation=self.last_decimation,
+ fft_size=self.fft_size,
+ fft_block_size=self.fft_block_size(),
+ fft_averages=self.fft_averages,
+ bpf_transition_bw=float(self.bpf_transition_bw) / self.if_samp_rate(),
+ ddc_transition_bw=self.ddc_transition_bw(),
+ flowcontrol=int(self.samp_rate * 2),
+ start_bufsize=self.base_bufsize * self.decimation,
+ nc_port=self.nc_port,
+ output_rate=self.get_output_rate(),
+ smeter_report_every=int(self.if_samp_rate() / 6000),
+ codecserver_arg=self.get_codecserver_arg(),
+ audio_rate=self.get_audio_rate(),
+ wfm_deemphasis_tau=self.wfm_deemphasis_tau,
+ )
+
+ logger.debug("Command = %s", command)
+
+ out = subprocess.PIPE if self.output.supports_type("audio") else subprocess.DEVNULL
+ self.process = subprocess.Popen(command, stdout=out, shell=True, start_new_session=True)
+
+ def watch_thread():
+ rc = self.process.wait()
+ logger.debug("dsp thread ended with rc=%d", rc)
+ if rc == 0 and self.running and not self.modification_lock.locked():
+ logger.debug("restarting since rc = 0, self.running = true, and no modification")
+ self.restart()
+
+ threading.Thread(target=watch_thread, name="csdr_watch_thread").start()
+
+ audio_type = "hd_audio" if self.isHdAudio() else "audio"
+ if self.output.supports_type(audio_type):
+ self.output.send_output(
+ audio_type,
+ partial(
+ self.process.stdout.read,
+ self.get_fft_bytes_to_read() if self.demodulator == "fft" else self.get_audio_bytes_to_read(),
+ ),
+ )
+
+ self.start_secondary_demodulator()
+
+ if self.has_pipe("smeter_pipe"):
+
+ def read_smeter():
+ raw = self.pipes["smeter_pipe"].readline()
+ if len(raw) == 0:
+ return None
+ else:
+ return float(raw.rstrip("\n"))
+
+ self.output.send_output("smeter", read_smeter)
+ if self.has_pipe("meta_pipe"):
+
+ def read_meta():
+ raw = self.pipes["meta_pipe"].readline()
+ if len(raw) == 0:
+ return None
+ else:
+ return raw.rstrip("\n")
+
+ self.output.send_output("meta", read_meta)
+
+ def stop(self):
+ with self.modification_lock:
+ self.running = False
+ if self.process is not None:
+ try:
+ os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
+ # drain any leftover data to free file descriptors
+ self.process.communicate()
+ self.process = None
+ except ProcessLookupError:
+ # been killed by something else, ignore
+ pass
+ self.stop_secondary_demodulator()
+
+ self.try_delete_pipes(self.pipe_names)
+ self.try_delete_configs()
+
+ def restart(self):
+ if not self.running:
+ return
+ self.stop()
+ self.start()
diff --git a/openwebrx/csdr/__pycache__/__init__.cpython-37.pyc b/openwebrx/csdr/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..d1fed4e
Binary files /dev/null and b/openwebrx/csdr/__pycache__/__init__.cpython-37.pyc differ
diff --git a/openwebrx/csdr/__pycache__/output.cpython-37.pyc b/openwebrx/csdr/__pycache__/output.cpython-37.pyc
new file mode 100644
index 0000000..6bc9702
Binary files /dev/null and b/openwebrx/csdr/__pycache__/output.cpython-37.pyc differ
diff --git a/openwebrx/csdr/__pycache__/pipe.cpython-37.pyc b/openwebrx/csdr/__pycache__/pipe.cpython-37.pyc
new file mode 100644
index 0000000..d993c0a
Binary files /dev/null and b/openwebrx/csdr/__pycache__/pipe.cpython-37.pyc differ
diff --git a/openwebrx/csdr/output.py b/openwebrx/csdr/output.py
new file mode 100644
index 0000000..5fef242
--- /dev/null
+++ b/openwebrx/csdr/output.py
@@ -0,0 +1,36 @@
+import threading
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+class Output(object):
+ def send_output(self, t, read_fn):
+ if not self.supports_type(t):
+ # TODO rewrite the output mechanism in a way that avoids producing unnecessary data
+ logger.warning("dumping output of type %s since it is not supported.", t)
+ threading.Thread(target=self.pump(read_fn, lambda x: None), name="csdr_pump_thread").start()
+ return
+ self.receive_output(t, read_fn)
+
+ def receive_output(self, t, read_fn):
+ pass
+
+ def pump(self, read, write):
+ def copy():
+ run = True
+ while run:
+ data = None
+ try:
+ data = read()
+ except ValueError:
+ pass
+ if data is None or (isinstance(data, bytes) and len(data) == 0):
+ run = False
+ else:
+ write(data)
+
+ return copy
+
+ def supports_type(self, t):
+ return True
diff --git a/openwebrx/csdr/pipe.py b/openwebrx/csdr/pipe.py
new file mode 100644
index 0000000..025e287
--- /dev/null
+++ b/openwebrx/csdr/pipe.py
@@ -0,0 +1,156 @@
+import os
+import select
+import time
+import threading
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+class Pipe(object):
+ READ = "r"
+ WRITE = "w"
+ NONE = None
+
+ @staticmethod
+ def create(path, t, encoding=None):
+ if t == Pipe.READ:
+ return ReadingPipe(path, encoding=encoding)
+ elif t == Pipe.WRITE:
+ return WritingPipe(path, encoding=encoding)
+ elif t == Pipe.NONE:
+ return Pipe(path, None, encoding=encoding)
+
+ def __init__(self, path, direction, encoding=None):
+ self.doOpen = True
+ self.path = "{base}_{myid}".format(base=path, myid=id(self))
+ self.direction = direction
+ self.encoding = encoding
+ self.file = None
+ os.mkfifo(self.path)
+
+ def open(self):
+ """
+ this method opens the file descriptor with an added O_NONBLOCK flag. This gives us a special behaviour for
+ FIFOS, when they are not opened by the opposing side:
+
+ - opening a pipe for writing will throw an OSError with errno = 6 (ENXIO). This is handled specially in the
+ WritingPipe class.
+ - opening a pipe for reading will pass through this method instantly, even if the opposing end has not been
+ opened yet, but the resulting file descriptor will behave as if O_NONBLOCK is set (even if we remove it
+ immediately here), resulting in empty reads until data is available. This is handled specially in the
+ ReadingPipe class.
+ """
+
+ def opener(path, flags):
+ fd = os.open(path, flags | os.O_NONBLOCK)
+ os.set_blocking(fd, True)
+ return fd
+
+ self.file = open(self.path, self.direction, encoding=self.encoding, opener=opener)
+
+ def close(self):
+ self.doOpen = False
+ try:
+ if self.file is not None:
+ self.file.close()
+ os.unlink(self.path)
+ except FileNotFoundError:
+ # it seems like we keep calling this twice. no idea why, but we don't need the resulting error.
+ pass
+ except Exception:
+ logger.exception("Pipe.close()")
+
+ def __str__(self):
+ return self.path
+
+
+class WritingPipe(Pipe):
+ def __init__(self, path, encoding=None):
+ self.queue = []
+ self.queueLock = threading.Lock()
+ super().__init__(path, "w", encoding=encoding)
+ self.open()
+
+ def open_and_dequeue(self):
+ """
+ This method implements a retry loop that can be interrupted in case the Pipe gets shutdown before actually
+ being connected.
+
+ After the pipe is opened successfully, all data that has been queued is sent in the order it was passed into
+ write().
+ """
+ retries = 0
+
+ while self.file is None and self.doOpen and retries < 10:
+ try:
+ super().open()
+ except OSError as error:
+ # ENXIO = FIFO has not been opened for reading
+ if error.errno == 6:
+ time.sleep(0.1)
+ retries += 1
+ else:
+ raise
+
+ # if doOpen is false, opening has been canceled, so no warning in that case.
+ if self.file is None:
+ if self.doOpen:
+ logger.warning("could not open FIFO %s", self.path)
+ return
+
+ with self.queueLock:
+ for i in self.queue:
+ self.file.write(i)
+ self.file.flush()
+ self.queue = None
+
+ def open(self):
+ """
+ This sends the opening operation off to a background thread. If we were to block the thread here, another pipe
+ may be waiting in the queue to be opened on the opposing side, resulting in a deadlock
+ """
+ threading.Thread(target=self.open_and_dequeue, name="csdr_pipe_thread").start()
+
+ def write(self, data):
+ """
+ This method queues all data to be written until the file is actually opened. As soon as a file is available,
+ it becomes a passthrough.
+ """
+ if self.file is None:
+ with self.queueLock:
+ self.queue.append(data)
+ return
+ r = self.file.write(data)
+ self.file.flush()
+ return r
+
+
+class ReadingPipe(Pipe):
+ def __init__(self, path, encoding=None):
+ super().__init__(path, "r", encoding=encoding)
+
+ def open(self):
+ """
+ This method implements an interruptible loop that waits for the file descriptor to be opened and the first
+ batch of data coming in using repeated select() calls.
+ :return:
+ """
+ if not self.doOpen:
+ return
+ super().open()
+ while self.doOpen:
+ (read, _, _) = select.select([self.file], [], [], 1)
+ if self.file in read:
+ break
+
+ def read(self):
+ if self.file is None:
+ self.open()
+ return self.file.read()
+
+ def readline(self):
+ if self.file is None:
+ self.open()
+ return self.file.readline()
diff --git a/openwebrx/debian/changelog b/openwebrx/debian/changelog
new file mode 100644
index 0000000..fda5286
--- /dev/null
+++ b/openwebrx/debian/changelog
@@ -0,0 +1,217 @@
+openwebrx (1.1.0) buster hirsute; urgency=low
+
+ * Reworked most graphical elements as SVGs for faster loadtimes and crispier
+ display on hi-dpi displays
+ * Updated pipelines to match changes in digiham
+ * Changed D-Star and NXDN integrations to use new decoder from digiham
+ * Added D-Star and NXDN metadata display
+
+ -- Jakob Ketterl Mon, 02 Aug 2021 16:24:00 +0000
+
+openwebrx (1.0.0) buster hirsute; urgency=low
+ * Introduced `squelch_auto_margin` config option that allows configuring the
+ auto squelch level
+ * Removed `port` configuration option; `rtltcp_compat` takes the port number
+ with the new connectors
+ * Added support for new WSJT-X modes FST4, FST4W (only available with WSJT-X
+ 2.3) and Q65 (only available with WSJT-X 2.4)
+ * Added support for demodulating M17 digital voice signals using
+ m17-cxx-demod
+ * New reporting infrastructure, allowing WSPR and FST4W spots to be sent to
+ wsprnet.org
+ * Add some basic filtering capabilities to the map
+ * New arguments to the `openwebrx` command-line to facilitate the
+ administration of users (try `openwebrx admin`)
+ * New command-line tool `openwebrx-admin` that facilitates the
+ administration of users
+ * Default bandwidth changes:
+ - "WFM" changed to 150kHz
+ - "Packet" (APRS) changed to 12.5kHz
+ * Configuration rework:
+ - New: fully web-based configuration interface
+ - System configuration parameters have been moved to a new, separate
+ `openwebrx.conf` file
+ - Remaining parameters are now editable in the web configuration
+ - Existing `config_webrx.py` files will still be read, but changes made in
+ the web configuration will be written to a new storage system
+ - Added upload of avatar and panorama image via web configuration
+ * New devices supported:
+ - HPSDR devices (Hermes Lite 2) thanks to @jancona
+ - BBRF103 / RX666 / RX888 devices supported by libsddc
+ - R&S devices using the EB200 or Ammos protocols
+
+ -- Jakob Ketterl Thu, 06 May 2021 17:22:00 +0000
+
+openwebrx (0.20.3) buster focal; urgency=low
+
+ * Fix a compatibility issue with python versions <= 3.6
+
+ -- Jakob Ketterl Tue, 26 Jan 2021 15:28:00 +0000
+
+openwebrx (0.20.2) buster focal; urgency=high
+
+ * Fix a security problem that allowed arbitrary commands to be executed on
+ the receiver (See github issue #215:
+ https://github.com/jketterl/openwebrx/issues/215)
+
+ -- Jakob Ketterl Sun, 24 Jan 2021 22:50:00 +0000
+
+openwebrx (0.20.1) buster focal; urgency=low
+
+ * Remove broken OSM map fallback
+
+ -- Jakob Ketterl Mon, 30 Nov 2020 17:29:00 +0000
+
+openwebrx (0.20.0) buster focal; urgency=low
+
+ * Added the ability to sign multiple keys in a single request, thus enabling
+ multiple users to claim a single receiver on receiverbook.de
+ * Fixed file descriptor leaks to prevent "too many open files" errors
+ * Add new demodulator chain for FreeDV
+ * Added new HD audio streaming mode along with a new WFM demodulator
+ * Reworked AGC code for better results in AM, SSB and digital modes
+ * Added support for demodulation of "Digital Radio Mondiale" (DRM) broadcast
+ using the "dream" decoder.
+ * New default waterfall color scheme
+ * Prototype of a continuous automatic waterfall calibration mode
+ * New devices supported:
+ - FunCube Dongle Pro+ (`"type": "fcdpp"`)
+ - Support for connections to rtl_tcp (`"type": "rtl_tcp"`)
+
+ -- Jakob Ketterl Sun, 11 Oct 2020 13:02:00 +0000
+
+openwebrx (0.19.1) buster focal; urgency=low
+
+ * Added ability to authenticate receivers with listing sites using
+ "receiver id" tokens
+
+ -- Jakob Ketterl Sat, 13 Jun 2020 16:46:00 +0000
+
+openwebrx (0.19.0) buster focal; urgency=low
+ * Fix direwolf connection setup by implementing a retry loop
+ * Pass direct sampling mode changes for rtl_sdr_soapy to owrx_connector
+ * OSM maps instead of Google when google_maps_api_key is not set (thanks
+ @jquagga)
+ * Improved logic to pass parameters to soapy devices.
+ - `rtl_sdr_soapy`: added support for `bias_tee`
+ - `sdrplay`: added support for `bias_tee`, `rf_notch` and `dab_notch`
+ - `airspy`: added support for `bitpack`
+ * Added support for Perseus-SDR devices, (thanks @amontefusco)
+ * Property System has been rewritten so that defaults on sdr behave as
+ expected
+ * Waterfall range auto-adjustment now only takes the center 80% of the
+ spectrum into account, which should work better with SDRs that oversample
+ or have rather flat filter curves towards the spectrum edges
+ * Bugfix for negative network usage
+ * FiFi SDR: prevent arecord from shutting down after 2GB of data has been
+ sent
+ * Added support for bias tee control on rtl_sdr devices
+ * All connector driven SDRs now support `"rf_gain": "auto"` to enable AGC
+ * `rtl_sdr` type now also supports the `direct_sampling` option
+ * Added decoding implementation for for digimode "JS8Call" (requires an
+ installation of js8call and the js8py library)
+ * Reorganization of the frontend demodulator code
+ * Improve receiver load time by concatenating javascript assets
+ * HackRF support is now based on SoapyHackRF
+ * Removed sdr.hu server listing support since the site has been shut down
+ * Added support for Radioberry 2 Rasbperry Pi SDR Cape
+
+ -- Jakob Ketterl Mon, 01 Jun 2020 17:02:00 +0000
+
+openwebrx (0.18.0) buster; urgency=low
+
+ * Compression, resampling and filtering in the frontend have been rewritten
+ in javascript, sdr.js has been removed
+ * Decoding of Pocsag modulation is now possible
+ * Removed the 3D waterfall since it had no real application and required ~1MB
+ of javascript code to be downloaded
+ * Improved the frontend handling of the "too many users" scenario
+ * PSK63 digimode is now available (same decoding pipeline as PSK31, but with
+ adopted parameters)
+ * The frequency can now be manipulated with the mousewheel, which should
+ allow the user to tune more precise. The tuning step size is determined by
+ the digit the mouse cursor is hovering over.
+ * Clicking on the frequency now opens an input for direct frequency selection
+ * URL hashes have been fixed and improved: They are now updated
+ automatically, so a shared URL will include frequency and demodulator,
+ which allows for improved sharing and linking.
+ * New daylight scheduler for background decoding, allows profiles to be
+ selected by local sunrise / sunset times
+ * The owrx_connector is now the default way of communicating with sdr
+ devices. The old sdr types have been replaced, all `_connector` suffixes on
+ the type must be removed!
+ * The sources have been refactored, making it a lot easier to add support for
+ other devices
+ * SDR device failure handling has been improved, including user feedback
+ * New devices supported:
+ * wsjt-x updated to 2.1.2
+ * The rtl_tcp compatibility mode of the owrx_connector is now configurable
+ using the `rtltcp_compat` flag
+ * explicit device filter for soapy devices for multi-device setups
+ * compatibility fixes for safari browsers (ios and mac)
+ * Offset tuning using the `lfo_offset` has been reworked in a way that
+ `center_freq` has to be set to the frequency you actually want to listen
+ to. If you're using an `lfo_offset` already, you will probably need to
+ change its sign.
+ * `initial_squelch_level` can now be set on each profile.
+ * Part of the frontend code has been reworked
+ - Audio buffer minimums have been completely stripped. As a result, you
+ should get better latency. Unfortunately, this also means there will be
+ some skipping when audio starts.
+ - Now also supports AudioWorklets (for those browser that have it).
+ - Mousewheel controls for the receiver sliders
+ * Error handling for failed SDR devices
+ * One of the most-requested features is finally coming to OpenWebRX:
+ Bookmarks (sometimes also referred to as labels).
+ There's two kinds of bookmarks available:
+ - Serverside bookmarks that are set up by the receiver administrator.
+ Check the file `bookmarks.json` for examples!
+ - Clientside bookmarks which every user can store for themselves. They are
+ stored in the browser's localStorage.
+ * Automatic reporting of spots to [pskreporter](https://pskreporter.info/) is
+ now possible. Please have a look at the configuration on how to set it up.
+ * Websocket communication has been overhauled in large parts. It should now
+ be more reliable, and failing connections should now have no impact on
+ other users.
+ * Profile scheduling allows to set up band-hopping if you are running
+ background services.
+ * APRS now has the ability to show symbols on the map, if a corresponding
+ symbol set has been installed. Check the config!
+ * Debug logging has been disabled in a handful of modules, expect vastly
+ reduced output on the shell.
+ * New set of APRS-related features
+ - Decode Packet transmissions using direwolf (1k2 only for now)
+ - APRS packets are mostly decoded and shown both in a new panel and on the
+ map
+ - APRS is also available as a background service
+ - direwolfs I-gate functionality can be enabled, which allows your receiver
+ to work as a receive-only I-gate for the APRS network in the background
+ * Demodulation for background services has been optimized to use less total
+ bandwidth, saving CPU
+ * More metrics have been added; they can be used together with collectd and
+ its curl_json plugin for now, with some limitations.
+ * New bandplan feature, the first thing visible is the "dial" indicator that
+ brings you right to the dial frequency for digital modes
+ * fixed some bugs in the websocket communication which broke the map
+ * WSJT-X integration (FT8, FT4, WSPR, JT65, JT9 using wsjt-x demodulators)
+ * New Map Feature that shows both decoded grid squares from FT8 and Locations
+ decoded from YSF digital voice
+ * New Feature report that will show what functionality is available
+ * major rework on the openwebrx core
+ * Support of multiple SDR devices simultaneously
+ * Support for multiple profiles per SDR that allow the user to listen to
+ different frequencies
+ * Support for digital voice decoding
+ * Feature detection that will disable functionality when dependencies are not
+ available (if you're missing the digital
+ buttons, this is probably why)
+ * Support added for the following SDR sources:
+ - LimeSDR (`"type": "lime_sdr"`)
+ - PlutoSDR (`"type": "pluto_sdr"`)
+ - RTL_SDR via Soapy (`"type": "rtl_sdr_soapy"`) on special request to allow
+ use of the direct sampling mode
+ - SoapyRemote (`"type": "soapy_remote"`)
+ - FiFiSDR (`"type": "fifi_sdr"`)
+ - airspyhf devices (Airspy HF+ / Discovery) (`"type": "airspyhf"`)
+
+ -- Jakob Ketterl Tue, 18 Feb 2020 20:09:00 +0000
diff --git a/openwebrx/debian/compat b/openwebrx/debian/compat
new file mode 100644
index 0000000..f599e28
--- /dev/null
+++ b/openwebrx/debian/compat
@@ -0,0 +1 @@
+10
diff --git a/openwebrx/debian/control b/openwebrx/debian/control
new file mode 100644
index 0000000..6b274ba
--- /dev/null
+++ b/openwebrx/debian/control
@@ -0,0 +1,16 @@
+Source: openwebrx
+Maintainer: Jakob Ketterl
+Section: hamradio
+Priority: optional
+Standards-Version: 4.2.0
+Build-Depends: debhelper (>= 11), dh-python, python3-all (>= 3.5), python3-setuptools
+Homepage: https://www.openwebrx.de/
+Vcs-Browser: https://github.com/jketterl/openwebrx
+Vcs-Git: https://github.com/jketterl/openwebrx.git
+
+Package: openwebrx
+Architecture: all
+Depends: adduser, python3 (>= 3.5), python3-pkg-resources, csdr (>= 0.17), netcat, owrx-connector (>= 0.5), soapysdr-tools, python3-js8py (>= 0.1), ${python3:Depends}, ${misc:Depends}
+Recommends: digiham (>= 0.5), sox, direwolf (>= 1.4), wsjtx, runds-connector (>= 0.2), hpsdrconnector, aprs-symbols, m17-demod, js8call
+Description: multi-user web sdr
+ Open source, multi-user SDR receiver with a web interface
diff --git a/openwebrx/debian/openwebrx.config b/openwebrx/debian/openwebrx.config
new file mode 100644
index 0000000..9a19393
--- /dev/null
+++ b/openwebrx/debian/openwebrx.config
@@ -0,0 +1,8 @@
+#!/bin/sh -e
+. /usr/share/debconf/confmodule
+
+db_get openwebrx/admin_user_configured
+if [ "${1:-}" = "reconfigure" ] || [ "${RET}" != true ]; then
+ db_input high openwebrx/admin_user_password || true
+ db_go
+fi
diff --git a/openwebrx/debian/openwebrx.dirs b/openwebrx/debian/openwebrx.dirs
new file mode 100644
index 0000000..c87b1b2
--- /dev/null
+++ b/openwebrx/debian/openwebrx.dirs
@@ -0,0 +1 @@
+/etc/openwebrx/openwebrx.conf.d
\ No newline at end of file
diff --git a/openwebrx/debian/openwebrx.install b/openwebrx/debian/openwebrx.install
new file mode 100644
index 0000000..8db9344
--- /dev/null
+++ b/openwebrx/debian/openwebrx.install
@@ -0,0 +1,3 @@
+bands.json etc/openwebrx/
+openwebrx.conf etc/openwebrx/
+systemd/openwebrx.service lib/systemd/system/
\ No newline at end of file
diff --git a/openwebrx/debian/openwebrx.postinst b/openwebrx/debian/openwebrx.postinst
new file mode 100644
index 0000000..935a0fe
--- /dev/null
+++ b/openwebrx/debian/openwebrx.postinst
@@ -0,0 +1,59 @@
+#!/bin/bash
+. /usr/share/debconf/confmodule
+
+set -euo pipefail
+
+OWRX_USER="openwebrx"
+OWRX_DATADIR="/var/lib/openwebrx"
+OWRX_USERS_FILE="${OWRX_DATADIR}/users.json"
+OWRX_SETTINGS_FILE="${OWRX_DATADIR}/settings.json"
+OWRX_BOOKMARKS_FILE="${OWRX_DATADIR}/bookmarks.json"
+
+case "$1" in
+ configure|reconfigure)
+ adduser --system --group --no-create-home --home /nonexistent --quiet "${OWRX_USER}"
+ usermod -aG plugdev "${OWRX_USER}"
+
+ # create OpenWebRX data directory and set the correct permissions
+ if [ ! -d "${OWRX_DATADIR}" ] && [ ! -L "${OWRX_DATADIR}" ]; then mkdir "${OWRX_DATADIR}"; fi
+ chown "${OWRX_USER}". ${OWRX_DATADIR}
+
+ # create empty config files now to avoid permission problems later
+ if [ ! -e "${OWRX_USERS_FILE}" ]; then
+ echo "[]" > "${OWRX_USERS_FILE}"
+ chown "${OWRX_USER}". "${OWRX_USERS_FILE}"
+ chmod 0600 "${OWRX_USERS_FILE}"
+ fi
+
+ if [ ! -e "${OWRX_SETTINGS_FILE}" ]; then
+ echo "{}" > "${OWRX_SETTINGS_FILE}"
+ chown "${OWRX_USER}". "${OWRX_SETTINGS_FILE}"
+ fi
+
+ if [ ! -e "${OWRX_BOOKMARKS_FILE}" ]; then
+ touch "${OWRX_BOOKMARKS_FILE}"
+ chown "${OWRX_USER}". "${OWRX_BOOKMARKS_FILE}"
+ fi
+
+ db_get openwebrx/admin_user_password
+ if [ ! -z "${RET}" ]; then
+ if ! openwebrx admin --silent hasuser admin; then
+ # create initial openwebrx user
+ OWRX_PASSWORD="${RET}" openwebrx admin --noninteractive adduser admin
+ else
+ # change existing user's password
+ OWRX_PASSWORD="${RET}" openwebrx admin --noninteractive resetpassword admin
+ fi
+ fi
+ # remove password from debconf database
+ db_unregister openwebrx/admin_user_password
+ # set a marker that admin is configured to avoid future questions
+ db_set openwebrx/admin_user_configured true
+ ;;
+ *)
+ echo "postinst called with unknown argument '$1'" 1>&2
+ exit 1
+ ;;
+esac
+
+#DEBHELPER#
diff --git a/openwebrx/debian/openwebrx.postrm b/openwebrx/debian/openwebrx.postrm
new file mode 100644
index 0000000..9260b8e
--- /dev/null
+++ b/openwebrx/debian/openwebrx.postrm
@@ -0,0 +1,8 @@
+#!/bin/sh -e
+
+if [ "$1" = purge ] && [ -e /usr/share/debconf/confmodule ]; then
+ . /usr/share/debconf/confmodule
+ db_purge
+fi
+
+#DEBHELPER#
diff --git a/openwebrx/debian/openwebrx.templates b/openwebrx/debian/openwebrx.templates
new file mode 100644
index 0000000..8ef7e84
--- /dev/null
+++ b/openwebrx/debian/openwebrx.templates
@@ -0,0 +1,23 @@
+Template: openwebrx/admin_user_password
+Type: password
+Description: OpenWebRX "admin" user password:
+ The system can create a user for the OpenWebRX web configuration interface for
+ you. Using this user, you will be able to log into the "settings" area of
+ OpenWebRX to configure your receiver conveniently through your browser.
+ .
+ The name of the created user will be "admin".
+ .
+ If you do not wish to create a web admin user right now, you can leave this
+ empty for now. You can return to this prompt at a later time by running the
+ command "sudo dpkg-reconfigure openwebrx".
+ .
+ You can also use the "openwebrx admin" command to create, delete or manage
+ existing users. More information is available in by running the command
+ "openwebrx admin --help".
+
+Template: openwebrx/admin_user_configured
+Type: boolean
+Default: false
+Description: OpenWebRX "admin" user previously configured?
+ Marker used internally by the config scripts to remember if an admin user has
+ been created.
\ No newline at end of file
diff --git a/openwebrx/debian/rules b/openwebrx/debian/rules
new file mode 100644
index 0000000..3b7418e
--- /dev/null
+++ b/openwebrx/debian/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+export PYBUILD_NAME=openwebrx
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild --with systemd
+
+override_dh_strip_nondeterminism:
+ dh_strip_nondeterminism -X.png
diff --git a/openwebrx/debian/source/format b/openwebrx/debian/source/format
new file mode 100644
index 0000000..9f67427
--- /dev/null
+++ b/openwebrx/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
\ No newline at end of file
diff --git a/openwebrx/docker.sh b/openwebrx/docker.sh
new file mode 100644
index 0000000..c2bfe7f
--- /dev/null
+++ b/openwebrx/docker.sh
@@ -0,0 +1,97 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ARCH=$(uname -m)
+IMAGES="openwebrx-rtlsdr openwebrx-sdrplay openwebrx-hackrf openwebrx-airspy openwebrx-rtlsdr-soapy openwebrx-plutosdr openwebrx-limesdr openwebrx-soapyremote openwebrx-perseus openwebrx-fcdpp openwebrx-radioberry openwebrx-uhd openwebrx-rtltcp openwebrx-runds openwebrx-hpsdr openwebrx-full openwebrx"
+ALL_ARCHS="x86_64 armv7l aarch64"
+TAG=${TAG:-"latest"}
+ARCHTAG="${TAG}-${ARCH}"
+
+usage () {
+ echo "Usage: ${0} [command]"
+ echo "Available commands:"
+ echo " help Show this usage information"
+ echo " build Build all docker images"
+ echo " push Push built docker images to the docker hub"
+ echo " manifest Compile the docker hub manifest (combines arm and x86 tags into one)"
+ echo " tag Tag a release"
+}
+
+build () {
+ # build the base images
+ docker build --pull -t openwebrx-base:${ARCHTAG} -f docker/Dockerfiles/Dockerfile-base .
+ docker build --build-arg ARCHTAG=${ARCHTAG} -t openwebrx-soapysdr-base:${ARCHTAG} -f docker/Dockerfiles/Dockerfile-soapysdr .
+
+ for image in ${IMAGES}; do
+ i=${image:10}
+ # "openwebrx" is a special image that gets tag-aliased later on
+ if [[ ! -z "${i}" ]] ; then
+ docker build --build-arg ARCHTAG=$ARCHTAG -t jketterl/${image}:${ARCHTAG} -f docker/Dockerfiles/Dockerfile-${i} .
+ fi
+ done
+
+ # tag openwebrx alias image
+ docker tag jketterl/openwebrx-full:${ARCHTAG} jketterl/openwebrx:${ARCHTAG}
+}
+
+push () {
+ for image in ${IMAGES}; do
+ docker push jketterl/${image}:${ARCHTAG}
+ done
+}
+
+manifest () {
+ for image in ${IMAGES}; do
+ # there's no docker manifest rm command, and the create --amend does not work, so we have to clean up manually
+ rm -rf "${HOME}/.docker/manifests/docker.io_jketterl_${image}-${TAG}"
+ IMAGE_LIST=""
+ for a in ${ALL_ARCHS}; do
+ IMAGE_LIST="${IMAGE_LIST} jketterl/${image}:${TAG}-${a}"
+ done
+ docker manifest create jketterl/${image}:${TAG} ${IMAGE_LIST}
+ docker manifest push --purge jketterl/${image}:${TAG}
+ done
+}
+
+tag () {
+ if [[ -x ${1:-} || -z ${2:-} ]] ; then
+ echo "Usage: ${0} tag [SRC_TAG] [TARGET_TAG]"
+ return
+ fi
+
+ local SRC_TAG=${1}
+ local TARGET_TAG=${2}
+
+ for image in ${IMAGES}; do
+ # there's no docker manifest rm command, and the create --amend does not work, so we have to clean up manually
+ rm -rf "${HOME}/.docker/manifests/docker.io_jketterl_${image}-${TARGET_TAG}"
+ IMAGE_LIST=""
+ for a in ${ALL_ARCHS}; do
+ docker pull jketterl/${image}:${SRC_TAG}-${a}
+ docker tag jketterl/${image}:${SRC_TAG}-${a} jketterl/${image}:${TARGET_TAG}-${a}
+ docker push jketterl/${image}:${TARGET_TAG}-${a}
+ IMAGE_LIST="${IMAGE_LIST} jketterl/${image}:${TARGET_TAG}-${a}"
+ done
+ docker manifest create jketterl/${image}:${TARGET_TAG} ${IMAGE_LIST}
+ docker manifest push --purge jketterl/${image}:${TARGET_TAG}
+ docker pull jketterl/${image}:${TARGET_TAG}
+ done
+}
+
+case ${1:-} in
+ build)
+ build
+ ;;
+ push)
+ push
+ ;;
+ manifest)
+ manifest
+ ;;
+ tag)
+ tag ${@:2}
+ ;;
+ *)
+ usage
+ ;;
+esac
\ No newline at end of file
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-airspy b/openwebrx/docker/Dockerfiles/Dockerfile-airspy
new file mode 100644
index 0000000..94b348b
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-airspy
@@ -0,0 +1,8 @@
+ARG ARCHTAG
+FROM openwebrx-soapysdr-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-airspy.sh /
+RUN /install-dependencies-airspy.sh &&\
+ rm /install-dependencies-airspy.sh
+
+ADD . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-base b/openwebrx/docker/Dockerfiles/Dockerfile-base
new file mode 100644
index 0000000..6fd184f
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-base
@@ -0,0 +1,28 @@
+FROM debian:buster-slim
+
+COPY docker/files/js8call/js8call-hamlib.patch \
+ docker/files/wsjtx/wsjtx.patch \
+ docker/files/wsjtx/wsjtx-hamlib.patch \
+ docker/files/dream/dream.patch \
+ docker/files/direwolf/direwolf-hamlib.patch \
+ docker/scripts/install-dependencies.sh /
+RUN /install-dependencies.sh && \
+ rm /install-dependencies.sh && \
+ rm /*.patch
+COPY docker/scripts/install-owrx-tools.sh /
+RUN /install-owrx-tools.sh && \
+ rm /install-owrx-tools.sh
+
+COPY docker/files/services/codecserver /etc/services.d/codecserver
+
+ENTRYPOINT ["/init"]
+
+WORKDIR /opt/openwebrx
+
+VOLUME /etc/openwebrx
+VOLUME /var/lib/openwebrx
+
+ENV S6_CMD_ARG0="/opt/openwebrx/docker/scripts/run.sh"
+CMD []
+
+EXPOSE 8073
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-fcdpp b/openwebrx/docker/Dockerfiles/Dockerfile-fcdpp
new file mode 100644
index 0000000..3e28ac7
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-fcdpp
@@ -0,0 +1,8 @@
+ARG ARCHTAG
+FROM openwebrx-soapysdr-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-fcdpp.sh /
+RUN /install-dependencies-fcdpp.sh &&\
+ rm /install-dependencies-fcdpp.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-full b/openwebrx/docker/Dockerfiles/Dockerfile-full
new file mode 100644
index 0000000..cecf45a
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-full
@@ -0,0 +1,30 @@
+ARG ARCHTAG
+FROM openwebrx-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-*.sh \
+ docker/files/sdrplay/install-lib.*.patch \
+ docker/scripts/install-connectors.sh /
+
+RUN /install-dependencies-rtlsdr.sh &&\
+ /install-dependencies-soapysdr.sh &&\
+ /install-dependencies-hackrf.sh &&\
+ /install-dependencies-sdrplay.sh &&\
+ /install-dependencies-airspy.sh &&\
+ /install-dependencies-rtlsdr-soapy.sh &&\
+ /install-dependencies-plutosdr.sh &&\
+ /install-dependencies-limesdr.sh &&\
+ /install-dependencies-soapyremote.sh &&\
+ /install-dependencies-perseus.sh &&\
+ /install-dependencies-fcdpp.sh &&\
+ /install-dependencies-radioberry.sh &&\
+ /install-dependencies-uhd.sh &&\
+ /install-dependencies-hpsdr.sh &&\
+ /install-connectors.sh &&\
+ /install-dependencies-runds.sh &&\
+ rm /install-dependencies-*.sh &&\
+ rm /install-lib.*.patch && \
+ rm /install-connectors.sh
+
+COPY docker/files/services/sdrplay /etc/services.d/sdrplay
+
+ADD . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-hackrf b/openwebrx/docker/Dockerfiles/Dockerfile-hackrf
new file mode 100644
index 0000000..6dab0f1
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-hackrf
@@ -0,0 +1,8 @@
+ARG ARCHTAG
+FROM openwebrx-soapysdr-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-hackrf.sh /
+RUN /install-dependencies-hackrf.sh &&\
+ rm /install-dependencies-hackrf.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-hpsdr b/openwebrx/docker/Dockerfiles/Dockerfile-hpsdr
new file mode 100644
index 0000000..96d58b9
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-hpsdr
@@ -0,0 +1,9 @@
+ARG ARCHTAG
+FROM openwebrx-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-hpsdr.sh /
+
+RUN /install-dependencies-hpsdr.sh &&\
+ rm /install-dependencies-hpsdr.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-limesdr b/openwebrx/docker/Dockerfiles/Dockerfile-limesdr
new file mode 100644
index 0000000..9603c60
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-limesdr
@@ -0,0 +1,8 @@
+ARG ARCHTAG
+FROM openwebrx-soapysdr-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-limesdr.sh /
+RUN /install-dependencies-limesdr.sh &&\
+ rm /install-dependencies-limesdr.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-perseus b/openwebrx/docker/Dockerfiles/Dockerfile-perseus
new file mode 100644
index 0000000..bc16583
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-perseus
@@ -0,0 +1,8 @@
+ARG ARCHTAG
+FROM openwebrx-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-perseus.sh /
+RUN /install-dependencies-perseus.sh &&\
+ rm /install-dependencies-perseus.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-plutosdr b/openwebrx/docker/Dockerfiles/Dockerfile-plutosdr
new file mode 100644
index 0000000..4a263e8
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-plutosdr
@@ -0,0 +1,8 @@
+ARG ARCHTAG
+FROM openwebrx-soapysdr-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-plutosdr.sh /
+RUN /install-dependencies-plutosdr.sh &&\
+ rm /install-dependencies-plutosdr.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-radioberry b/openwebrx/docker/Dockerfiles/Dockerfile-radioberry
new file mode 100644
index 0000000..3cbe978
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-radioberry
@@ -0,0 +1,8 @@
+ARG ARCHTAG
+FROM openwebrx-soapysdr-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-radioberry.sh /
+RUN /install-dependencies-radioberry.sh &&\
+ rm /install-dependencies-radioberry.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-rtlsdr b/openwebrx/docker/Dockerfiles/Dockerfile-rtlsdr
new file mode 100644
index 0000000..6144641
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-rtlsdr
@@ -0,0 +1,12 @@
+ARG ARCHTAG
+FROM openwebrx-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-rtlsdr.sh \
+ docker/scripts/install-connectors.sh /
+
+RUN /install-dependencies-rtlsdr.sh &&\
+ rm /install-dependencies-rtlsdr.sh &&\
+ /install-connectors.sh &&\
+ rm /install-connectors.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-rtlsdr-soapy b/openwebrx/docker/Dockerfiles/Dockerfile-rtlsdr-soapy
new file mode 100644
index 0000000..5dce90f
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-rtlsdr-soapy
@@ -0,0 +1,8 @@
+ARG ARCHTAG
+FROM openwebrx-soapysdr-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-rtlsdr-soapy.sh /
+RUN /install-dependencies-rtlsdr-soapy.sh &&\
+ rm /install-dependencies-rtlsdr-soapy.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-rtltcp b/openwebrx/docker/Dockerfiles/Dockerfile-rtltcp
new file mode 100644
index 0000000..240799d
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-rtltcp
@@ -0,0 +1,9 @@
+ARG ARCHTAG
+FROM openwebrx-base:$ARCHTAG
+
+COPY docker/scripts/install-connectors.sh /
+
+RUN /install-connectors.sh &&\
+ rm /install-connectors.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-runds b/openwebrx/docker/Dockerfiles/Dockerfile-runds
new file mode 100644
index 0000000..2a087e1
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-runds
@@ -0,0 +1,12 @@
+ARG ARCHTAG
+FROM openwebrx-base:$ARCHTAG
+
+COPY docker/scripts/install-connectors.sh \
+ docker/scripts/install-dependencies-runds.sh /
+
+RUN /install-connectors.sh &&\
+ rm /install-connectors.sh && \
+ /install-dependencies-runds.sh && \
+ rm /install-dependencies-runds.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-sdrplay b/openwebrx/docker/Dockerfiles/Dockerfile-sdrplay
new file mode 100644
index 0000000..bb53d7e
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-sdrplay
@@ -0,0 +1,12 @@
+ARG ARCHTAG
+FROM openwebrx-soapysdr-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-sdrplay.sh \
+ docker/files/sdrplay/install-lib.*.patch /
+RUN /install-dependencies-sdrplay.sh &&\
+ rm /install-dependencies-sdrplay.sh &&\
+ rm /install-lib.*.patch
+
+COPY docker/files/services/sdrplay /etc/services.d/sdrplay
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-soapyremote b/openwebrx/docker/Dockerfiles/Dockerfile-soapyremote
new file mode 100644
index 0000000..e5c207c
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-soapyremote
@@ -0,0 +1,8 @@
+ARG ARCHTAG
+FROM openwebrx-soapysdr-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-soapyremote.sh /
+RUN /install-dependencies-soapyremote.sh &&\
+ rm /install-dependencies-soapyremote.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-soapysdr b/openwebrx/docker/Dockerfiles/Dockerfile-soapysdr
new file mode 100644
index 0000000..45ac693
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-soapysdr
@@ -0,0 +1,9 @@
+ARG ARCHTAG
+FROM openwebrx-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-soapysdr.sh \
+ docker/scripts/install-connectors.sh /
+RUN /install-dependencies-soapysdr.sh &&\
+ rm /install-dependencies-soapysdr.sh &&\
+ /install-connectors.sh &&\
+ rm /install-connectors.sh
diff --git a/openwebrx/docker/Dockerfiles/Dockerfile-uhd b/openwebrx/docker/Dockerfiles/Dockerfile-uhd
new file mode 100644
index 0000000..ae1e758
--- /dev/null
+++ b/openwebrx/docker/Dockerfiles/Dockerfile-uhd
@@ -0,0 +1,8 @@
+ARG ARCHTAG
+FROM openwebrx-soapysdr-base:$ARCHTAG
+
+COPY docker/scripts/install-dependencies-uhd.sh /
+RUN /install-dependencies-uhd.sh &&\
+ rm /install-dependencies-uhd.sh
+
+COPY . /opt/openwebrx
diff --git a/openwebrx/docker/files/direwolf/direwolf-hamlib.patch b/openwebrx/docker/files/direwolf/direwolf-hamlib.patch
new file mode 100644
index 0000000..2347c24
--- /dev/null
+++ b/openwebrx/docker/files/direwolf/direwolf-hamlib.patch
@@ -0,0 +1,20 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 9e710f5..da90b43 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -257,13 +257,8 @@ else()
+ set(GPSD_LIBRARIES "")
+ endif()
+
+-find_package(hamlib)
+-if(HAMLIB_FOUND)
+- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_HAMLIB")
+-else()
+- set(HAMLIB_INCLUDE_DIRS "")
+- set(HAMLIB_LIBRARIES "")
+-endif()
++set(HAMLIB_INCLUDE_DIRS "")
++set(HAMLIB_LIBRARIES "")
+
+ if(LINUX)
+ find_package(ALSA REQUIRED)
diff --git a/openwebrx/docker/files/dream/dream.patch b/openwebrx/docker/files/dream/dream.patch
new file mode 100644
index 0000000..9a7a8e2
--- /dev/null
+++ b/openwebrx/docker/files/dream/dream.patch
@@ -0,0 +1,96 @@
+--- dream.pro.org 2020-09-04 22:51:51.579926191 +0200
++++ dream.pro 2020-09-04 22:52:57.609434707 +0200
+@@ -70,9 +70,6 @@
+ exists(/opt/local/include/speex/speex_preprocess.h) {
+ CONFIG += speexdsp
+ }
+- exists(/opt/local/include/hamlib/rig.h) {
+- CONFIG += hamlib
+- }
+ contains(QT_VERSION, ^4\\.7.*) {
+ QT += phonon opengl svg
+ DEFINES -= QWT_NO_SVG
+@@ -138,12 +135,6 @@
+ packagesExist(sndfile) {
+ CONFIG += sndfile
+ }
+- packagesExist(hamlib) {
+- CONFIG += hamlib
+- }
+- packagesExist(gpsd) {
+- CONFIG += gps
+- }
+ packagesExist(pcap) {
+ CONFIG += pcap
+ }
+@@ -159,14 +150,6 @@
+ exists(/usr/local/include/sndfile.h) {
+ CONFIG += sndfile
+ }
+- exists(/usr/include/hamlib/rig.h) | \
+- exists(/usr/local/include/hamlib/rig.h) {
+- CONFIG += hamlib
+- }
+- exists(/usr/include/gps.h) | \
+- exists(/usr/local/include/gps.h) {
+- CONFIG += gps
+- }
+ exists(/usr/include/pcap.h) | \
+ exists(/usr/local/include/pcap.h) {
+ CONFIG += pcap
+@@ -194,9 +177,6 @@
+ exists($$OUT_PWD/include/speex/speex_preprocess.h) {
+ CONFIG += speexdsp
+ }
+- exists($$OUT_PWD/include/hamlib/rig.h) {
+- CONFIG += hamlib
+- }
+ exists($$OUT_PWD/include/pcap.h) {
+ CONFIG += pcap
+ }
+@@ -225,7 +205,7 @@
+ LIBS += -lz
+ }
+ }
+-exists($$OUT_PWD/include/neaacdec.h) {
++exists(/usr/include/neaacdec.h) {
+ DEFINES += HAVE_LIBFAAD \
+ USE_FAAD2_LIBRARY
+ LIBS += -lfaad_drm
+@@ -257,11 +237,6 @@
+ win32:LIBS += libspeexdsp.lib
+ message("with libspeexdsp")
+ }
+-gps {
+- DEFINES += HAVE_LIBGPS
+- unix:LIBS += -lgps
+- message("with gps")
+-}
+ pcap {
+ DEFINES += HAVE_LIBPCAP
+ unix:LIBS += -lpcap
+@@ -269,24 +244,6 @@
+ win32-g++:LIBS += -lwpcap -lpacket
+ message("with pcap")
+ }
+-hamlib {
+- DEFINES += HAVE_LIBHAMLIB
+- macx:LIBS += -framework IOKit
+- unix:LIBS += -lhamlib
+- win32:LIBS += libhamlib-2.lib
+- HEADERS += src/util/Hamlib.h
+- SOURCES += src/util/Hamlib.cpp
+- qt {
+- HEADERS += src/util-QT/Rig.h
+- SOURCES += src/util-QT/Rig.cpp
+- }
+- gui {
+- HEADERS += src/GUI-QT/RigDlg.h
+- SOURCES += src/GUI-QT/RigDlg.cpp
+- FORMS += RigDlg.ui
+- }
+- message("with hamlib")
+-}
+ qwt {
+ DEFINES += QWT_NO_SVG
+ macx {
diff --git a/openwebrx/docker/files/js8call/js8call-hamlib.patch b/openwebrx/docker/files/js8call/js8call-hamlib.patch
new file mode 100644
index 0000000..899f83e
--- /dev/null
+++ b/openwebrx/docker/files/js8call/js8call-hamlib.patch
@@ -0,0 +1,151 @@
+diff -ur js8call-orig/CMake/Modules/Findhamlib.cmake js8call/CMake/Modules/Findhamlib.cmake
+--- js8call-orig/CMake/Modules/Findhamlib.cmake 2020-07-22 18:14:18.014499840 +0200
++++ js8call/CMake/Modules/Findhamlib.cmake 2020-07-22 18:16:07.200375473 +0200
+@@ -78,4 +78,4 @@
+ # Handle the QUIETLY and REQUIRED arguments and set HAMLIB_FOUND to
+ # TRUE if all listed variables are TRUE
+ include (FindPackageHandleStandardArgs)
+-find_package_handle_standard_args (hamlib DEFAULT_MSG hamlib_INCLUDE_DIRS hamlib_LIBRARIES hamlib_LIBRARY_DIRS)
++find_package_handle_standard_args (hamlib DEFAULT_MSG hamlib_INCLUDE_DIRS hamlib_LIBRARIES)
+diff -ur js8call-orig/CMakeLists.txt js8call/CMakeLists.txt
+--- js8call-orig/CMakeLists.txt 2020-07-22 18:14:18.014499840 +0200
++++ js8call/CMakeLists.txt 2020-07-22 18:17:55.629633825 +0200
+@@ -558,7 +558,7 @@
+ #
+ # libhamlib setup
+ #
+-set (hamlib_STATIC 1)
++set (hamlib_STATIC 0)
+ find_package (hamlib 3 REQUIRED)
+ find_program (RIGCTL_EXE rigctl)
+ find_program (RIGCTLD_EXE rigctld)
+@@ -911,56 +911,6 @@
+ target_link_libraries (js8 wsjt_fort wsjt_cxx Qt5::Core)
+ endif (${OPENMP_FOUND} OR APPLE)
+
+-# build the main application
+-add_executable (js8call MACOSX_BUNDLE
+- ${sqlite3_CSRCS}
+- ${wsjtx_CXXSRCS}
+- ${wsjtx_GENUISRCS}
+- wsjtx.rc
+- ${WSJTX_ICON_FILE}
+- ${wsjtx_RESOURCES_RCC}
+- images.qrc
+- )
+-
+-if (WSJT_CREATE_WINMAIN)
+- set_target_properties (js8call PROPERTIES WIN32_EXECUTABLE ON)
+-endif (WSJT_CREATE_WINMAIN)
+-
+-set_target_properties (js8call PROPERTIES
+- MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Darwin/Info.plist.in"
+- MACOSX_BUNDLE_INFO_STRING "${WSJTX_DESCRIPTION_SUMMARY}"
+- MACOSX_BUNDLE_ICON_FILE "${WSJTX_ICON_FILE}"
+- MACOSX_BUNDLE_BUNDLE_VERSION ${wsjtx_VERSION}
+- MACOSX_BUNDLE_SHORT_VERSION_STRING "v${wsjtx_VERSION}"
+- MACOSX_BUNDLE_LONG_VERSION_STRING "Version ${wsjtx_VERSION}"
+- MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}"
+- MACOSX_BUNDLE_BUNDLE_EXECUTABLE_NAME "${PROJECT_NAME}"
+- MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT}"
+- MACOSX_BUNDLE_GUI_IDENTIFIER "org.kn4crd.js8call"
+- )
+-
+-target_include_directories (js8call PRIVATE ${FFTW3_INCLUDE_DIRS})
+-if (APPLE)
+- target_link_libraries (js8call wsjt_fort wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
+-else ()
+- target_link_libraries (js8call wsjt_fort_omp wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
+- if (OpenMP_C_FLAGS)
+- set_target_properties (js8call PROPERTIES
+- COMPILE_FLAGS "${OpenMP_C_FLAGS}"
+- LINK_FLAGS "${OpenMP_C_FLAGS}"
+- )
+- endif ()
+- set_target_properties (js8call PROPERTIES
+- Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
+- )
+- if (WIN32)
+- set_target_properties (js8call PROPERTIES
+- LINK_FLAGS -Wl,--stack,16777216
+- )
+- endif ()
+-endif ()
+-qt5_use_modules (js8call SerialPort) # not sure why the interface link library syntax above doesn't work
+-
+ # if (UNIX)
+ # if (NOT WSJT_SKIP_MANPAGES)
+ # add_subdirectory (manpages)
+@@ -976,38 +926,10 @@
+ #
+ # installation
+ #
+-install (TARGETS js8call
+- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
+- BUNDLE DESTINATION . COMPONENT runtime
+- )
+-
+ install (TARGETS js8 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
+ BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
+ )
+
+-install (PROGRAMS
+- ${RIGCTL_EXE}
+- DESTINATION ${CMAKE_INSTALL_BINDIR}
+- #COMPONENT runtime
+- RENAME rigctl-local${CMAKE_EXECUTABLE_SUFFIX}
+- )
+-
+-install (PROGRAMS
+- ${RIGCTLD_EXE}
+- DESTINATION ${CMAKE_INSTALL_BINDIR}
+- #COMPONENT runtime
+- RENAME rigctld-local${CMAKE_EXECUTABLE_SUFFIX}
+- )
+-
+-install (FILES
+- README
+- COPYING
+- INSTALL
+- INSTALL-WSJTX
+- DESTINATION ${CMAKE_INSTALL_DOCDIR}
+- #COMPONENT runtime
+- )
+-
+ install (FILES
+ contrib/Ephemeris/JPLEPH
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}
+@@ -1061,32 +983,6 @@
+ "${CMAKE_CURRENT_BINARY_DIR}/wsjtx_config.h"
+ )
+
+-
+-if (NOT WIN32 AND NOT APPLE)
+- # install a desktop file so js8call appears in the application start
+- # menu with an icon
+- install (
+- FILES js8call.desktop
+- DESTINATION /usr/share/applications
+- #COMPONENT runtime
+- )
+- install (
+- FILES icons/Unix/js8call_icon.png
+- DESTINATION /usr/share/pixmaps
+- #COMPONENT runtime
+- )
+-
+- IF("${CMAKE_INSTALL_PREFIX}" STREQUAL "/opt/js8call")
+- execute_process(COMMAND ln -s /opt/js8call/bin/js8call ljs8call)
+-
+- install(FILES
+- ${CMAKE_BINARY_DIR}/ljs8call DESTINATION /usr/bin/ RENAME js8call
+- #COMPONENT runtime
+- )
+- endif()
+-endif (NOT WIN32 AND NOT APPLE)
+-
+-
+ #
+ # bundle fixup only done in Release or MinSizeRel configurations
+ #
+Only in js8call/: .idea
diff --git a/openwebrx/docker/files/sdrplay/install-lib.aarch64.patch b/openwebrx/docker/files/sdrplay/install-lib.aarch64.patch
new file mode 100644
index 0000000..1f3dc57
--- /dev/null
+++ b/openwebrx/docker/files/sdrplay/install-lib.aarch64.patch
@@ -0,0 +1,23 @@
+diff -ur sdrplay-orig/install_lib.sh sdrplay/install_lib.sh
+--- sdrplay-orig/install_lib.sh 2020-05-24 14:30:06.022483867 +0000
++++ sdrplay/install_lib.sh 2020-05-24 14:30:49.093435726 +0000
+@@ -4,19 +4,6 @@
+ export MAJVERS="3"
+
+ echo "Installing SDRplay RSP API library ${VERS}..."
+-read -p "Press RETURN to view the license agreement" ret
+-
+-more sdrplay_license.txt
+-
+-while true; do
+- echo "Press y and RETURN to accept the license agreement and continue with"
+- read -p "the installation, or press n and RETURN to exit the installer [y/n] " yn
+- case $yn in
+- [Yy]* ) break;;
+- [Nn]* ) exit;;
+- * ) echo "Please answer y or n";;
+- esac
+-done
+
+ export ARCH=`uname -m`
+
diff --git a/openwebrx/docker/files/sdrplay/install-lib.armv7l.patch b/openwebrx/docker/files/sdrplay/install-lib.armv7l.patch
new file mode 100644
index 0000000..22a78f6
--- /dev/null
+++ b/openwebrx/docker/files/sdrplay/install-lib.armv7l.patch
@@ -0,0 +1,40 @@
+diff -ur sdrplay-orig/install_lib.sh sdrplay/install_lib.sh
+--- sdrplay-orig/install_lib.sh 2020-05-24 14:13:04.561271707 +0000
++++ sdrplay/install_lib.sh 2020-05-24 14:16:20.068329040 +0000
+@@ -4,19 +4,6 @@
+ MAJVERS="3"
+
+ echo "Installing SDRplay RSP API library ${VERS}..."
+-read -p "Press RETURN to view the license agreement" ret
+-
+-more sdrplay_license.txt
+-
+-while true; do
+- echo "Press y and RETURN to accept the license agreement and continue with"
+- read -p "the installation, or press n and RETURN to exit the installer [y/n] " yn
+- case $yn in
+- [Yy]* ) break;;
+- [Nn]* ) exit;;
+- * ) echo "Please answer y or n";;
+- esac
+-done
+
+ ARCH=`uname -m`
+
+@@ -141,16 +128,6 @@
+ echo "SDRplay API ${VERS} Installation Finished"
+ echo " "
+
+-while true; do
+- echo "Would you like to add SDRplay USB IDs to the local database for easier
+-"
+- read -p "identification in applications such as lsusb? [y/n] " yn
+- case $yn in
+- [Yy]* ) break;;
+- [Nn]* ) exit;;
+- * ) echo "Please answer y or n";;
+- esac
+-done
+ sudo cp scripts/sdrplay_usbids.sh ${INSTALLBINDIR}/.
+ sudo chmod 755 ${INSTALLBINDIR}/sdrplay_usbids.sh
+ sudo cp scripts/sdrplay_ids.txt ${INSTALLBINDIR}/.
diff --git a/openwebrx/docker/files/sdrplay/install-lib.x86_64.patch b/openwebrx/docker/files/sdrplay/install-lib.x86_64.patch
new file mode 100644
index 0000000..d66023b
--- /dev/null
+++ b/openwebrx/docker/files/sdrplay/install-lib.x86_64.patch
@@ -0,0 +1,39 @@
+diff -ur sdrplay-orig/install_lib.sh sdrplay/install_lib.sh
+--- sdrplay-orig/install_lib.sh 2020-05-24 13:56:56.622000041 +0000
++++ sdrplay/install_lib.sh 2020-05-24 13:58:51.837801559 +0000
+@@ -4,19 +4,6 @@
+ MAJVERS="3"
+
+ echo "Installing SDRplay RSP API library ${VERS}..."
+-read -p "Press RETURN to view the license agreement" ret
+-
+-more sdrplay_license.txt
+-
+-while true; do
+- echo "Press y and RETURN to accept the license agreement and continue with"
+- read -p "the installation, or press n and RETURN to exit the installer [y/n] " yn
+- case $yn in
+- [Yy]* ) break;;
+- [Nn]* ) exit;;
+- * ) echo "Please answer y or n";;
+- esac
+-done
+
+ ARCH=`uname -m`
+ OSDIST="Unknown"
+@@ -157,15 +144,6 @@
+ echo " "
+ echo "SDRplay API ${VERS} Installation Finished"
+ echo " "
+-while true; do
+- echo "Would you like to add SDRplay USB IDs to the local database for easier"
+- read -p "identification in applications such as lsusb? [y/n] " yn
+- case $yn in
+- [Yy]* ) break;;
+- [Nn]* ) exit;;
+- * ) echo "Please answer y or n";;
+- esac
+-done
+ sudo cp scripts/sdrplay_usbids.sh ${INSTALLBINDIR}/.
+ sudo chmod 755 ${INSTALLBINDIR}/sdrplay_usbids.sh
+ sudo cp scripts/sdrplay_ids.txt ${INSTALLBINDIR}/.
diff --git a/openwebrx/docker/files/services/codecserver/run b/openwebrx/docker/files/services/codecserver/run
new file mode 100644
index 0000000..43c8212
--- /dev/null
+++ b/openwebrx/docker/files/services/codecserver/run
@@ -0,0 +1,2 @@
+#!/usr/bin/execlineb -P
+/usr/local/bin/codecserver
\ No newline at end of file
diff --git a/openwebrx/docker/files/services/sdrplay/run b/openwebrx/docker/files/services/sdrplay/run
new file mode 100644
index 0000000..0f31c4c
--- /dev/null
+++ b/openwebrx/docker/files/services/sdrplay/run
@@ -0,0 +1,2 @@
+#!/usr/bin/execlineb -P
+/usr/local/bin/sdrplay_apiService
\ No newline at end of file
diff --git a/openwebrx/docker/files/wsjtx/wsjtx-hamlib.patch b/openwebrx/docker/files/wsjtx/wsjtx-hamlib.patch
new file mode 100644
index 0000000..47f37d9
--- /dev/null
+++ b/openwebrx/docker/files/wsjtx/wsjtx-hamlib.patch
@@ -0,0 +1,50 @@
+--- CMakeLists.txt.orig 2021-03-30 15:28:36.956587995 +0200
++++ CMakeLists.txt 2021-03-30 15:29:45.719326832 +0200
+@@ -106,24 +106,6 @@
+
+
+ #
+-# build and install hamlib locally so it can be referenced by the
+-# WSJT-X build
+-#
+-ExternalProject_Add (hamlib
+- GIT_REPOSITORY ${hamlib_repo}
+- GIT_TAG ${hamlib_TAG}
+- GIT_SHALLOW False
+- URL ${CMAKE_CURRENT_SOURCE_DIR}/src/${__hamlib_upstream}.tar.gz
+- URL_HASH MD5=${hamlib_md5sum}
+- #UPDATE_COMMAND ${CMAKE_COMMAND} -E env "[ -f ./bootstrap ] && ./bootstrap"
+- PATCH_COMMAND ${PATCH_EXECUTABLE} -p1 -N < ${CMAKE_CURRENT_SOURCE_DIR}/hamlib.patch
+- CONFIGURE_COMMAND /configure --prefix= --disable-shared --enable-static --without-cxx-binding ${EXTRA_FLAGS} # LIBUSB_LIBS=${USB_LIBRARY}
+- BUILD_COMMAND $(MAKE) all V=1 # $(MAKE) is ExternalProject_Add() magic to do recursive make
+- INSTALL_COMMAND $(MAKE) install-strip V=1 DESTDIR=""
+- STEP_TARGETS update install
+- )
+-
+-#
+ # custom target to make a hamlib source tarball
+ #
+ add_custom_target (hamlib_sources
+@@ -161,7 +143,6 @@
+ # build and optionally install WSJT-X using the hamlib package built
+ # above
+ #
+-ExternalProject_Get_Property (hamlib INSTALL_DIR)
+ ExternalProject_Add (wsjtx
+ GIT_REPOSITORY ${wsjtx_repo}
+ GIT_TAG ${WSJTX_TAG}
+@@ -186,14 +167,8 @@
+ DEPENDEES build
+ )
+
+-set_target_properties (hamlib PROPERTIES EXCLUDE_FROM_ALL 1)
+ set_target_properties (wsjtx PROPERTIES EXCLUDE_FROM_ALL 1)
+
+-add_dependencies (wsjtx-configure hamlib-install)
+-add_dependencies (wsjtx-build hamlib-install)
+-add_dependencies (wsjtx-install hamlib-install)
+-add_dependencies (wsjtx-package hamlib-install)
+-
+ # export traditional targets
+ add_custom_target (build ALL DEPENDS wsjtx-build)
+ add_custom_target (install DEPENDS wsjtx-install)
diff --git a/openwebrx/docker/files/wsjtx/wsjtx.patch b/openwebrx/docker/files/wsjtx/wsjtx.patch
new file mode 100644
index 0000000..61ef53e
--- /dev/null
+++ b/openwebrx/docker/files/wsjtx/wsjtx.patch
@@ -0,0 +1,316 @@
+diff -ur wsjtx-orig/CMake/Modules/Findhamlib.cmake wsjtx/CMake/Modules/Findhamlib.cmake
+--- wsjtx-orig/CMake/Modules/Findhamlib.cmake 2021-05-31 18:56:20.657682124 +0200
++++ wsjtx/CMake/Modules/Findhamlib.cmake 2021-05-31 18:57:03.963994898 +0200
+@@ -85,4 +85,4 @@
+ # Handle the QUIETLY and REQUIRED arguments and set HAMLIB_FOUND to
+ # TRUE if all listed variables are TRUE
+ include (FindPackageHandleStandardArgs)
+-find_package_handle_standard_args (hamlib DEFAULT_MSG hamlib_INCLUDE_DIRS hamlib_LIBRARIES hamlib_LIBRARY_DIRS)
++find_package_handle_standard_args (hamlib DEFAULT_MSG hamlib_INCLUDE_DIRS hamlib_LIBRARIES)
+diff -ur wsjtx-orig/CMakeLists.txt wsjtx/CMakeLists.txt
+--- wsjtx-orig/CMakeLists.txt 2021-05-31 18:56:20.657682124 +0200
++++ wsjtx/CMakeLists.txt 2021-05-31 19:08:02.768474060 +0200
+@@ -122,7 +122,7 @@
+ option (WSJT_QDEBUG_TO_FILE "Redirect Qt debuging messages to a trace file.")
+ option (WSJT_SOFT_KEYING "Apply a ramp to CW keying envelope to reduce transients." ON)
+ option (WSJT_SKIP_MANPAGES "Skip *nix manpage generation.")
+-option (WSJT_GENERATE_DOCS "Generate documentation files." ON)
++option (WSJT_GENERATE_DOCS "Generate documentation files.")
+ option (WSJT_RIG_NONE_CAN_SPLIT "Allow split operation with \"None\" as rig.")
+ option (WSJT_TRACE_UDP "Debugging option that turns on UDP message protocol diagnostics.")
+ option (WSJT_BUILD_UTILS "Build simulators and code demonstrators." ON)
+@@ -169,74 +169,7 @@
+ )
+
+ set (wsjt_qt_CXXSRCS
+- qt_helpers.cpp
+- widgets/MessageBox.cpp
+- MetaDataRegistry.cpp
+- Network/NetworkServerLookup.cpp
+ revision_utils.cpp
+- L10nLoader.cpp
+- WFPalette.cpp
+- Radio.cpp
+- RadioMetaType.cpp
+- NonInheritingProcess.cpp
+- models/IARURegions.cpp
+- models/Bands.cpp
+- models/Modes.cpp
+- models/FrequencyList.cpp
+- models/StationList.cpp
+- widgets/FrequencyLineEdit.cpp
+- widgets/FrequencyDeltaLineEdit.cpp
+- item_delegates/CandidateKeyFilter.cpp
+- item_delegates/ForeignKeyDelegate.cpp
+- validators/LiveFrequencyValidator.cpp
+- GetUserId.cpp
+- Audio/AudioDevice.cpp
+- Transceiver/Transceiver.cpp
+- Transceiver/TransceiverBase.cpp
+- Transceiver/EmulateSplitTransceiver.cpp
+- Transceiver/TransceiverFactory.cpp
+- Transceiver/PollingTransceiver.cpp
+- Transceiver/HamlibTransceiver.cpp
+- Transceiver/HRDTransceiver.cpp
+- Transceiver/DXLabSuiteCommanderTransceiver.cpp
+- Network/NetworkMessage.cpp
+- Network/MessageClient.cpp
+- widgets/LettersSpinBox.cpp
+- widgets/HintedSpinBox.cpp
+- widgets/RestrictedSpinBox.cpp
+- widgets/HelpTextWindow.cpp
+- SampleDownloader.cpp
+- SampleDownloader/DirectoryDelegate.cpp
+- SampleDownloader/Directory.cpp
+- SampleDownloader/FileNode.cpp
+- SampleDownloader/RemoteFile.cpp
+- DisplayManual.cpp
+- MultiSettings.cpp
+- validators/MaidenheadLocatorValidator.cpp
+- validators/CallsignValidator.cpp
+- widgets/SplashScreen.cpp
+- EqualizationToolsDialog.cpp
+- widgets/DoubleClickablePushButton.cpp
+- widgets/DoubleClickableRadioButton.cpp
+- Network/LotWUsers.cpp
+- models/DecodeHighlightingModel.cpp
+- widgets/DecodeHighlightingListView.cpp
+- models/FoxLog.cpp
+- widgets/AbstractLogWindow.cpp
+- widgets/FoxLogWindow.cpp
+- widgets/CabrilloLogWindow.cpp
+- item_delegates/CallsignDelegate.cpp
+- item_delegates/MaidenheadLocatorDelegate.cpp
+- item_delegates/FrequencyDelegate.cpp
+- item_delegates/FrequencyDeltaDelegate.cpp
+- item_delegates/SQLiteDateTimeDelegate.cpp
+- models/CabrilloLog.cpp
+- logbook/AD1CCty.cpp
+- logbook/WorkedBefore.cpp
+- logbook/Multiplier.cpp
+- Network/NetworkAccessManager.cpp
+- widgets/LazyFillComboBox.cpp
+- widgets/CheckableItemComboBox.cpp
+ )
+
+ set (wsjt_qtmm_CXXSRCS
+@@ -857,7 +790,7 @@
+ #
+ # libhamlib setup
+ #
+-set (hamlib_STATIC 1)
++set (hamlib_STATIC 0)
+ find_package (hamlib 3 REQUIRED)
+ find_program (RIGCTL_EXE rigctl)
+ find_program (RIGCTLD_EXE rigctld)
+@@ -895,9 +828,6 @@
+ if (WSJT_GENERATE_DOCS)
+ add_subdirectory (doc)
+ endif (WSJT_GENERATE_DOCS)
+-if (EXISTS ${CMAKE_SOURCE_DIR}/tests AND IS_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
+- add_subdirectory (tests)
+-endif ()
+
+ #
+ # Library building setup
+@@ -1380,60 +1310,6 @@
+ target_link_libraries (jt9 wsjt_fort wsjt_cxx fort_qt)
+ endif (${OPENMP_FOUND} OR APPLE)
+
+-# build the main application
+-generate_version_info (wsjtx_VERSION_RESOURCES
+- NAME wsjtx
+- BUNDLE ${PROJECT_BUNDLE_NAME}
+- ICON ${WSJTX_ICON_FILE}
+- )
+-
+-add_executable (wsjtx MACOSX_BUNDLE
+- ${wsjtx_CXXSRCS}
+- ${wsjtx_GENUISRCS}
+- ${WSJTX_ICON_FILE}
+- ${wsjtx_RESOURCES_RCC}
+- ${wsjtx_VERSION_RESOURCES}
+- )
+-
+-if (WSJT_CREATE_WINMAIN)
+- set_target_properties (wsjtx PROPERTIES WIN32_EXECUTABLE ON)
+-endif (WSJT_CREATE_WINMAIN)
+-
+-set_target_properties (wsjtx PROPERTIES
+- MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Darwin/Info.plist.in"
+- MACOSX_BUNDLE_INFO_STRING "${PROJECT_DESCRIPTION}"
+- MACOSX_BUNDLE_ICON_FILE "${WSJTX_ICON_FILE}"
+- MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}
+- MACOSX_BUNDLE_SHORT_VERSION_STRING "v${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"
+- MACOSX_BUNDLE_LONG_VERSION_STRING "Version ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${SCS_VERSION_STR}"
+- MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_BUNDLE_NAME}"
+- MACOSX_BUNDLE_BUNDLE_EXECUTABLE_NAME "${PROJECT_NAME}"
+- MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT}"
+- MACOSX_BUNDLE_GUI_IDENTIFIER "org.k1jt.wsjtx"
+- )
+-
+-target_include_directories (wsjtx PRIVATE ${FFTW3_INCLUDE_DIRS})
+-if (APPLE)
+- target_link_libraries (wsjtx wsjt_fort)
+-else ()
+- target_link_libraries (wsjtx wsjt_fort_omp)
+- if (OpenMP_C_FLAGS)
+- set_target_properties (wsjtx PROPERTIES
+- COMPILE_FLAGS "${OpenMP_C_FLAGS}"
+- LINK_FLAGS "${OpenMP_C_FLAGS}"
+- )
+- endif ()
+- set_target_properties (wsjtx PROPERTIES
+- Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
+- )
+- if (WIN32)
+- set_target_properties (wsjtx PROPERTIES
+- LINK_FLAGS -Wl,--stack,0x1000000,--heap,0x20000000
+- )
+- endif ()
+-endif ()
+-target_link_libraries (wsjtx Qt5::SerialPort wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES} ${LIBM_LIBRARIES})
+-
+ # make a library for WSJT-X UDP servers
+ # add_library (wsjtx_udp SHARED ${UDP_library_CXXSRCS})
+ add_library (wsjtx_udp-static STATIC ${UDP_library_CXXSRCS})
+@@ -1473,47 +1349,9 @@
+ add_executable (wsjtx_app_version AppVersion/AppVersion.cpp ${wsjtx_app_version_VERSION_RESOURCES})
+ target_link_libraries (wsjtx_app_version wsjt_qt)
+
+-generate_version_info (message_aggregator_VERSION_RESOURCES
+- NAME message_aggregator
+- BUNDLE ${PROJECT_BUNDLE_NAME}
+- ICON ${WSJTX_ICON_FILE}
+- FILE_DESCRIPTION "Example WSJT-X UDP Message Protocol application"
+- )
+-add_resources (message_aggregator_RESOURCES /qss ${message_aggregator_STYLESHEETS})
+-configure_file (UDPExamples/message_aggregator.qrc.in message_aggregator.qrc @ONLY)
+-qt5_add_resources (message_aggregator_RESOURCES_RCC
+- ${CMAKE_CURRENT_BINARY_DIR}/message_aggregator.qrc
+- contrib/QDarkStyleSheet/qdarkstyle/style.qrc
+- )
+-add_executable (message_aggregator
+- ${message_aggregator_CXXSRCS}
+- ${message_aggregator_RESOURCES_RCC}
+- ${message_aggregator_VERSION_RESOURCES}
+- )
+-target_link_libraries (message_aggregator wsjt_qt Qt5::Widgets wsjtx_udp-static)
+-
+-if (WSJT_CREATE_WINMAIN)
+- set_target_properties (message_aggregator PROPERTIES WIN32_EXECUTABLE ON)
+-endif (WSJT_CREATE_WINMAIN)
+-
+-if (UNIX)
+- if (NOT WSJT_SKIP_MANPAGES)
+- add_subdirectory (manpages)
+- add_dependencies (wsjtx manpages)
+- endif (NOT WSJT_SKIP_MANPAGES)
+- if (NOT APPLE)
+- add_subdirectory (debian)
+- add_dependencies (wsjtx debian)
+- endif (NOT APPLE)
+-endif (UNIX)
+-
+ #
+ # installation
+ #
+-install (TARGETS wsjtx
+- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
+- BUNDLE DESTINATION . COMPONENT runtime
+- )
+
+ # install (TARGETS wsjtx_udp EXPORT udp
+ # RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+@@ -1532,12 +1370,7 @@
+ # DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/wsjtx
+ # )
+
+-install (TARGETS udp_daemon message_aggregator wsjtx_app_version
+- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
+- BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
+- )
+-
+-install (TARGETS jt9 wsprd fmtave fcal fmeasure
++install (TARGETS wsjtx_app_version jt9 wsprd
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
+ BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
+ )
+@@ -1549,38 +1382,6 @@
+ )
+ endif(WSJT_BUILD_UTILS)
+
+-install (PROGRAMS
+- ${RIGCTL_EXE}
+- DESTINATION ${CMAKE_INSTALL_BINDIR}
+- #COMPONENT runtime
+- RENAME rigctl-wsjtx${CMAKE_EXECUTABLE_SUFFIX}
+- )
+-
+-install (PROGRAMS
+- ${RIGCTLD_EXE}
+- DESTINATION ${CMAKE_INSTALL_BINDIR}
+- #COMPONENT runtime
+- RENAME rigctld-wsjtx${CMAKE_EXECUTABLE_SUFFIX}
+- )
+-
+-install (PROGRAMS
+- ${RIGCTLCOM_EXE}
+- DESTINATION ${CMAKE_INSTALL_BINDIR}
+- #COMPONENT runtime
+- RENAME rigctlcom-wsjtx${CMAKE_EXECUTABLE_SUFFIX}
+- )
+-
+-install (FILES
+- README
+- COPYING
+- AUTHORS
+- THANKS
+- NEWS
+- BUGS
+- DESTINATION ${CMAKE_INSTALL_DOCDIR}
+- #COMPONENT runtime
+- )
+-
+ install (FILES
+ cty.dat
+ cty.dat_copyright.txt
+@@ -1589,13 +1390,6 @@
+ #COMPONENT runtime
+ )
+
+-install (DIRECTORY
+- example_log_configurations
+- DESTINATION ${CMAKE_INSTALL_DOCDIR}
+- FILES_MATCHING REGEX "^.*[^~]$"
+- #COMPONENT runtime
+- )
+-
+ #
+ # Mac installer files
+ #
+@@ -1648,22 +1442,6 @@
+ )
+
+
+-if (NOT WIN32 AND NOT APPLE)
+- # install a desktop file so wsjtx appears in the application start
+- # menu with an icon
+- install (
+- FILES wsjtx.desktop message_aggregator.desktop
+- DESTINATION share/applications
+- #COMPONENT runtime
+- )
+- install (
+- FILES icons/Unix/wsjtx_icon.png
+- DESTINATION share/pixmaps
+- #COMPONENT runtime
+- )
+-endif (NOT WIN32 AND NOT APPLE)
+-
+-
+ #
+ # bundle fixup only done in non-Debug configurations
+ #
+Only in wsjtx/: CMakeLists.txt.orig
+Only in wsjtx/: .idea
diff --git a/openwebrx/docker/scripts/install-connectors.sh b/openwebrx/docker/scripts/install-connectors.sh
new file mode 100644
index 0000000..8386bc8
--- /dev/null
+++ b/openwebrx/docker/scripts/install-connectors.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+BUILD_PACKAGES="git cmake make gcc g++"
+
+apt-get update
+apt-get -y install --no-install-recommends $BUILD_PACKAGES
+
+git clone https://github.com/jketterl/owrx_connector.git
+cmakebuild owrx_connector 0.5.0
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-airspy.sh b/openwebrx/docker/scripts/install-dependencies-airspy.sh
new file mode 100644
index 0000000..72032ba
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-airspy.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="libusb-1.0-0"
+BUILD_PACKAGES="git libusb-1.0-0-dev cmake make gcc g++ pkg-config"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/airspy/airspyone_host.git
+# latest from master as of 2020-09-04
+cmakebuild airspyone_host 652fd7f1a8f85687641e0bd91f739694d7258ecc
+
+git clone https://github.com/pothosware/SoapyAirspy.git
+cmakebuild SoapyAirspy 10d697b209e7f1acc8b2c8d24851d46170ef77e3
+
+git clone https://github.com/airspy/airspyhf.git
+# latest from master as of 2020-09-04
+cmakebuild airspyhf 8891387edddcd185e2949e9814e9ef35f46f0722
+
+git clone https://github.com/pothosware/SoapyAirspyHF.git
+# latest from master as of 2020-09-04
+cmakebuild SoapyAirspyHF 5488dac5b44f1432ce67b40b915f7e61d3bd4853
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-fcdpp.sh b/openwebrx/docker/scripts/install-dependencies-fcdpp.sh
new file mode 100644
index 0000000..49f1439
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-fcdpp.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="libhidapi-hidraw0 libhidapi-libusb0 libasound2"
+BUILD_PACKAGES="git cmake make gcc g++ libhidapi-dev libasound2-dev"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/pothosware/SoapyFCDPP.git
+cmakebuild SoapyFCDPP soapy-fcdpp-0.1.1
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-hackrf.sh b/openwebrx/docker/scripts/install-dependencies-hackrf.sh
new file mode 100644
index 0000000..03704a3
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-hackrf.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="libusb-1.0-0 libfftw3-3 udev"
+BUILD_PACKAGES="git cmake make patch wget sudo gcc g++ libusb-1.0-0-dev libfftw3-dev pkg-config"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/mossmann/hackrf.git
+cd hackrf
+# latest from master as of 2020-09-04
+git checkout 6e5cbda2945c3bab0e6e1510eae418eda60c358e
+cmakebuild host
+cd ..
+rm -rf hackrf
+
+git clone https://github.com/pothosware/SoapyHackRF.git
+# latest from master as of 2020-09-04
+cmakebuild SoapyHackRF 7d530872f96c1cbe0ed62617c32c48ce7e103e1d
+
+SUDO_FORCE_REMOVE=yes apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-hpsdr.sh b/openwebrx/docker/scripts/install-dependencies-hpsdr.sh
new file mode 100644
index 0000000..03ff176
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-hpsdr.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+BUILD_PACKAGES="git wget gcc libc6-dev"
+
+apt-get update
+apt-get -y install --no-install-recommends $BUILD_PACKAGES
+
+pushd /tmp
+
+ARCH=$(uname -m)
+GOVERSION=1.15.5
+
+case ${ARCH} in
+ x86_64)
+ PACKAGE=go${GOVERSION}.linux-amd64.tar.gz
+ ;;
+ armv*)
+ PACKAGE=go${GOVERSION}.linux-armv6l.tar.gz
+ ;;
+ aarch64)
+ PACKAGE=go${GOVERSION}.linux-arm64.tar.gz
+ ;;
+esac
+
+wget https://golang.org/dl/${PACKAGE}
+tar xfz $PACKAGE
+
+git clone https://github.com/jancona/hpsdrconnector.git
+pushd hpsdrconnector
+git checkout v0.4.2
+/tmp/go/bin/go build
+install -m 0755 hpsdrconnector /usr/local/bin
+
+popd
+
+rm -rf hpsdrconnector
+rm -rf go
+rm $PACKAGE
+
+popd
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-limesdr.sh b/openwebrx/docker/scripts/install-dependencies-limesdr.sh
new file mode 100644
index 0000000..4f83298
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-limesdr.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+set -euo pipefail
+export MAKEFLAGS="-j4"
+
+cd /tmp
+
+STATIC_PACKAGES="libusb-1.0-0 libatomic1"
+BUILD_PACKAGES="git libusb-1.0-0-dev cmake make gcc g++"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+SIMD_FLAGS=""
+if [[ 'x86_64' == `uname -m` ]] ; then
+ SIMD_FLAGS="-DDEFAULT_SIMD_FLAGS=SSE3"
+fi
+
+git clone https://github.com/myriadrf/LimeSuite.git
+cd LimeSuite
+# latest from master as of 2020-09-04
+git checkout 9526621f8b4c9e2a7f638b5ef50c45560dcad22a
+mkdir builddir
+cd builddir
+cmake .. -DENABLE_EXAMPLES=OFF -DENABLE_DESKTOP=OFF -DENABLE_LIME_UTIL=OFF -DENABLE_QUICKTEST=OFF -DENABLE_OCTAVE=OFF -DENABLE_GUI=OFF -DCMAKE_CXX_STANDARD_LIBRARIES="-latomic" ${SIMD_FLAGS}
+make
+make install
+cd ../..
+rm -rf LimeSuite
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-perseus.sh b/openwebrx/docker/scripts/install-dependencies-perseus.sh
new file mode 100644
index 0000000..1d8f1c9
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-perseus.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+cd /tmp
+
+STATIC_PACKAGES="libusb-1.0-0 libudev1"
+BUILD_PACKAGES="git make gcc autoconf automake libtool libusb-1.0-0-dev xxd"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/Microtelecom/libperseus-sdr.git
+cd libperseus-sdr
+# latest from master as of 2020-09-04
+git checkout c2c95daeaa08bf0daed0e8ada970ab17cc264e1b
+./bootstrap.sh
+./configure
+make
+make install
+ldconfig /etc/ld.so.conf.d
+cd ..
+rm -rf libperseus-sdr
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-plutosdr.sh b/openwebrx/docker/scripts/install-dependencies-plutosdr.sh
new file mode 100644
index 0000000..aa801b5
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-plutosdr.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+set -euo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake .. ${3:-}
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="libusb-1.0-0 libxml2"
+BUILD_PACKAGES="git libusb-1.0-0-dev cmake make gcc g++ libxml2-dev flex bison pkg-config"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/analogdevicesinc/libiio.git
+cmakebuild libiio v0.21 -DCMAKE_INSTALL_PREFIX=/usr/local
+
+git clone https://github.com/analogdevicesinc/libad9361-iio.git
+cmakebuild libad9361-iio v0.2
+
+git clone https://github.com/pothosware/SoapyPlutoSDR.git
+# latest from master as of 2020-09-04
+cmakebuild SoapyPlutoSDR 93717b32ef052e0dfa717aa2c1a4eb27af16111f
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-radioberry.sh b/openwebrx/docker/scripts/install-dependencies-radioberry.sh
new file mode 100644
index 0000000..0172462
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-radioberry.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="libusb-1.0-0 libfftw3-3 udev"
+BUILD_PACKAGES="git cmake make patch wget sudo gcc g++ libusb-1.0-0-dev libfftw3-dev pkg-config"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/pa3gsb/Radioberry-2.x
+cd Radioberry-2.x/SBC/rpi-4
+
+# latest from master as of 2020-09-04
+cmakebuild SoapyRadioberrySDR 8d17de6b4dc076e628900a82f05c7cf0b16cbe24
+cd ../../..
+rm -rf Radioberry-2.x
+
+SUDO_FORCE_REMOVE=yes apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-rtlsdr-soapy.sh b/openwebrx/docker/scripts/install-dependencies-rtlsdr-soapy.sh
new file mode 100644
index 0000000..695f31d
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-rtlsdr-soapy.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+set -euo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="libusb-1.0-0"
+BUILD_PACKAGES="git libusb-1.0-0-dev cmake make gcc g++ pkg-config"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/osmocom/rtl-sdr.git
+# latest from master as of 2020-09-04
+cmakebuild rtl-sdr ed0317e6a58c098874ac58b769cf2e609c18d9a5
+
+git clone https://github.com/pothosware/SoapyRTLSDR.git
+cmakebuild SoapyRTLSDR soapy-rtl-sdr-0.3.1
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-rtlsdr.sh b/openwebrx/docker/scripts/install-dependencies-rtlsdr.sh
new file mode 100644
index 0000000..942241a
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-rtlsdr.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="libusb-1.0.0"
+BUILD_PACKAGES="git libusb-1.0.0-dev cmake make gcc g++ pkg-config"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/osmocom/rtl-sdr.git
+# latest from master as of 2020-09-04
+cmakebuild rtl-sdr ed0317e6a58c098874ac58b769cf2e609c18d9a5
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-runds.sh b/openwebrx/docker/scripts/install-dependencies-runds.sh
new file mode 100644
index 0000000..9a4be02
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-runds.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES=""
+BUILD_PACKAGES="git cmake make gcc g++ pkg-config"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/jketterl/runds_connector.git
+cmakebuild runds_connector 0.2.0
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-sdrplay.sh b/openwebrx/docker/scripts/install-dependencies-sdrplay.sh
new file mode 100644
index 0000000..d91ec6c
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-sdrplay.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="libusb-1.0.0 udev"
+BUILD_PACKAGES="git cmake make patch wget sudo gcc g++ libusb-1.0-0-dev"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+ARCH=$(uname -m)
+
+case $ARCH in
+ x86_64)
+ BINARY=SDRplay_RSP_API-Linux-3.07.1.run
+ ;;
+ armv*)
+ BINARY=SDRplay_RSP_API-ARM32-3.07.2.run
+ ;;
+ aarch64)
+ BINARY=SDRplay_RSP_API-ARM64-3.07.1.run
+ ;;
+esac
+
+wget https://www.sdrplay.com/software/$BINARY
+sh $BINARY --noexec --target sdrplay
+patch --verbose -Np0 < /install-lib.$ARCH.patch
+
+cd sdrplay
+./install_lib.sh
+cd ..
+rm -rf sdrplay
+rm $BINARY
+
+git clone https://github.com/pothosware/SoapySDRPlay3.git
+# latest from master as of 2021-06-19 (reliability fixes)
+cmakebuild SoapySDRPlay3 a869f25364a1f0d5b16169ff908aa21a2ace475d
+
+SUDO_FORCE_REMOVE=yes apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-soapyremote.sh b/openwebrx/docker/scripts/install-dependencies-soapyremote.sh
new file mode 100644
index 0000000..a74c465
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-soapyremote.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+set -euo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="avahi-daemon libavahi-client3"
+BUILD_PACKAGES="git cmake make gcc g++ libavahi-client-dev"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/pothosware/SoapyRemote.git
+cmakebuild SoapyRemote soapy-remote-0.5.2
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-soapysdr.sh b/openwebrx/docker/scripts/install-dependencies-soapysdr.sh
new file mode 100644
index 0000000..bd312b4
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-soapysdr.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="libudev1"
+BUILD_PACKAGES="git cmake make patch wget sudo gcc g++"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/pothosware/SoapySDR
+# latest from master as of 2020-09-04
+cmakebuild SoapySDR 580b94f3dad46899f34ec0a060dbb4534e844e57
+
+SUDO_FORCE_REMOVE=yes apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies-uhd.sh b/openwebrx/docker/scripts/install-dependencies-uhd.sh
new file mode 100644
index 0000000..c71ff4e
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies-uhd.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+set -euo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="libusb-1.0.0 libboost-chrono1.67.0 libboost-date-time1.67.0 libboost-filesystem1.67.0 libboost-program-options1.67.0 libboost-regex1.67.0 libboost-test1.67.0 libboost-serialization1.67.0 libboost-thread1.67.0 libboost-system1.67.0 python3-numpy python3-mako"
+BUILD_PACKAGES="git cmake make gcc g++ libusb-1.0-0-dev libboost-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-program-options-dev libboost-regex-dev libboost-test-dev libboost-serialization-dev libboost-thread-dev libboost-system-dev"
+
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/EttusResearch/uhd.git
+# 3.15.0.0 Release
+mkdir -p uhd/host/build
+cd uhd/host/build
+git checkout v3.15.0.0
+# see https://github.com/EttusResearch/uhd/issues/350
+case `uname -m` in
+ arm*)
+ cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_UTILS=OFF -DENABLE_PYTHON_API=OFF -DENABLE_EXAMPLES=OFF -DENABLE_TESTS=OFF -DENABLE_OCTOCLOCK=OFF -DENABLE_MAN_PAGES=OFF -DSTRIP_BINARIES=ON \
+ -DCMAKE_CXX_FLAGS:STRING="-march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a8 -Wno-psabi" \
+ -DCMAKE_C_FLAGS:STRING="-march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a8 -Wno-psabi" \
+ -DCMAKE_ASM_FLAGS:STRING="-march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a8 -g" ..
+ ;;
+ aarch64*)
+ cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_UTILS=OFF -DENABLE_PYTHON_API=OFF -DENABLE_EXAMPLES=OFF -DENABLE_TESTS=OFF -DENABLE_OCTOCLOCK=OFF -DENABLE_MAN_PAGES=OFF -DSTRIP_BINARIES=ON \
+ -DCMAKE_CXX_FLAGS:STRING="-march=armv8-a -mtune=cortex-a72 -Wno-psabi" \
+ -DCMAKE_C_FLAGS:STRING="-march=armv8-a -mtune=cortex-a72 -Wno-psabi" \
+ -DCMAKE_ASM_FLAGS:STRING="-march=armv8-a -mtune=cortex-a72 -g" ..
+ ;;
+ x86_64)
+ cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_UTILS=OFF -DENABLE_PYTHON_API=OFF -DENABLE_EXAMPLES=OFF -DENABLE_TESTS=OFF -DENABLE_OCTOCLOCK=OFF -DENABLE_MAN_PAGES=OFF -DSTRIP_BINARIES=ON ..
+ ;;
+esac
+make
+make install
+cd ../../..
+rm -rf uhd
+
+git clone https://github.com/pothosware/SoapyUHD.git
+cmakebuild SoapyUHD soapy-uhd-0.4.1
+
+SUDO_FORCE_REMOVE=yes apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-dependencies.sh b/openwebrx/docker/scripts/install-dependencies.sh
new file mode 100644
index 0000000..8820f93
--- /dev/null
+++ b/openwebrx/docker/scripts/install-dependencies.sh
@@ -0,0 +1,116 @@
+#!/bin/bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ${CMAKE_ARGS:-} ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="sox libfftw3-bin python3 python3-setuptools netcat-openbsd libsndfile1 liblapack3 libusb-1.0-0 libqt5core5a libreadline7 libgfortran4 libgomp1 libasound2 libudev1 ca-certificates libqt5gui5 libqt5sql5 libqt5printsupport5 libpulse0 libfaad2 libopus0 libboost-program-options1.67.0 libboost-log1.67.0"
+BUILD_PACKAGES="wget git libsndfile1-dev libfftw3-dev cmake make gcc g++ liblapack-dev texinfo gfortran libusb-1.0-0-dev qtbase5-dev qtmultimedia5-dev qttools5-dev libqt5serialport5-dev qttools5-dev-tools asciidoctor asciidoc libasound2-dev libudev-dev libhamlib-dev patch xsltproc qt5-default libfaad-dev libopus-dev libboost-dev libboost-program-options-dev libboost-log-dev libboost-regex-dev"
+apt-get update
+apt-get -y install auto-apt-proxy
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+case `uname -m` in
+ arm*)
+ PLATFORM=armhf
+ ;;
+ aarch64*)
+ PLATFORM=aarch64
+ ;;
+ x86_64*)
+ PLATFORM=amd64
+ ;;
+esac
+
+wget https://github.com/just-containers/s6-overlay/releases/download/v1.21.8.0/s6-overlay-${PLATFORM}.tar.gz
+tar xzf s6-overlay-${PLATFORM}.tar.gz -C /
+rm s6-overlay-${PLATFORM}.tar.gz
+
+JS8CALL_VERSION=2.2.0
+JS8CALL_DIR=js8call
+JS8CALL_TGZ=js8call-${JS8CALL_VERSION}.tgz
+wget http://files.js8call.com/${JS8CALL_VERSION}/${JS8CALL_TGZ}
+tar xfz ${JS8CALL_TGZ}
+# patch allows us to build against the packaged hamlib
+patch -Np1 -d ${JS8CALL_DIR} < /js8call-hamlib.patch
+rm /js8call-hamlib.patch
+CMAKE_ARGS="-D CMAKE_CXX_FLAGS=-DJS8_USE_HAMLIB_THREE" cmakebuild ${JS8CALL_DIR}
+rm ${JS8CALL_TGZ}
+
+WSJT_DIR=wsjtx-2.4.0
+WSJT_TGZ=${WSJT_DIR}.tgz
+wget http://physics.princeton.edu/pulsar/k1jt/${WSJT_TGZ}
+tar xfz ${WSJT_TGZ}
+patch -Np0 -d ${WSJT_DIR} < /wsjtx-hamlib.patch
+mv /wsjtx.patch ${WSJT_DIR}
+cmakebuild ${WSJT_DIR}
+rm ${WSJT_TGZ}
+
+git clone --depth 1 -b 1.6 https://github.com/wb2osz/direwolf.git
+cd direwolf
+# hamlib is present (necessary for the wsjt-x and js8call builds) and would be used, but there's no real need.
+# this patch prevents direwolf from linking to it, and it can be stripped at the end of the script.
+patch -Np1 < /direwolf-hamlib.patch
+mkdir build
+cd build
+cmake ..
+make
+make install
+cd ../..
+rm -rf direwolf
+# strip lots of generic documentation that will never be read inside a docker container
+rm /usr/local/share/doc/direwolf/*.pdf
+# examples are pointless, too
+rm -rf /usr/local/share/doc/direwolf/examples/
+
+git clone https://github.com/drowe67/codec2.git
+cd codec2
+# latest commit from master as of 2020-10-04
+git checkout 55d7bb8d1bddf881bdbfcb971a718b83e6344598
+mkdir build
+cd build
+cmake ..
+make
+make install
+install -m 0755 src/freedv_rx /usr/local/bin
+cd ../..
+rm -rf codec2
+
+wget https://downloads.sourceforge.net/project/drm/dream/2.1.1/dream-2.1.1-svn808.tar.gz
+tar xvfz dream-2.1.1-svn808.tar.gz
+pushd dream
+patch -Np0 < /dream.patch
+qmake CONFIG+=console
+make
+make install
+popd
+rm -rf dream
+rm dream-2.1.1-svn808.tar.gz
+
+git clone https://github.com/mobilinkd/m17-cxx-demod.git
+cmakebuild m17-cxx-demod v2.0
+
+git clone https://github.com/hessu/aprs-symbols /usr/share/aprs-symbols
+pushd /usr/share/aprs-symbols
+git checkout 5c2abe2658ee4d2563f3c73b90c6f59124839802
+# remove unused files (including git meta information)
+rm -rf .git aprs-symbols.ai aprs-sym-export.js
+popd
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/install-owrx-tools.sh b/openwebrx/docker/scripts/install-owrx-tools.sh
new file mode 100644
index 0000000..1cdfb32
--- /dev/null
+++ b/openwebrx/docker/scripts/install-owrx-tools.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+set -euxo pipefail
+export MAKEFLAGS="-j4"
+
+function cmakebuild() {
+ cd $1
+ if [[ ! -z "${2:-}" ]]; then
+ git checkout $2
+ fi
+ mkdir build
+ cd build
+ cmake ${CMAKE_ARGS:-} ..
+ make
+ make install
+ cd ../..
+ rm -rf $1
+}
+
+cd /tmp
+
+STATIC_PACKAGES="libfftw3-bin libprotobuf17"
+BUILD_PACKAGES="git autoconf automake libtool libfftw3-dev pkg-config cmake make gcc g++ libprotobuf-dev protobuf-compiler"
+apt-get update
+apt-get -y install --no-install-recommends $STATIC_PACKAGES $BUILD_PACKAGES
+
+git clone https://github.com/jketterl/js8py.git
+pushd js8py
+git checkout 0.1.0
+python3 setup.py install
+popd
+rm -rf js8py
+
+git clone https://github.com/jketterl/csdr.git
+cd csdr
+git checkout 0.17.0
+autoreconf -i
+./configure
+make
+make install
+cd ..
+rm -rf csdr
+
+git clone https://github.com/jketterl/codecserver.git
+mkdir -p /usr/local/etc/codecserver
+cp codecserver/conf/codecserver.conf /usr/local/etc/codecserver
+cmakebuild codecserver 0.1.0
+
+git clone https://github.com/jketterl/digiham.git
+cmakebuild digiham 0.5.0
+
+apt-get -y purge --autoremove $BUILD_PACKAGES
+apt-get clean
+rm -rf /var/lib/apt/lists/*
diff --git a/openwebrx/docker/scripts/run.sh b/openwebrx/docker/scripts/run.sh
new file mode 100644
index 0000000..cde6fdf
--- /dev/null
+++ b/openwebrx/docker/scripts/run.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+set -euo pipefail
+
+mkdir -p /etc/openwebrx/openwebrx.conf.d
+mkdir -p /var/lib/openwebrx
+mkdir -p /tmp/openwebrx/
+if [[ ! -f /etc/openwebrx/openwebrx.conf.d/20-temporary-directory.conf ]] ; then
+ cat << EOF > /etc/openwebrx/openwebrx.conf.d/20-temporary-directory.conf
+[core]
+temporary_directory = /tmp/openwebrx
+EOF
+fi
+if [[ ! -f /etc/openwebrx/bands.json ]] ; then
+ cp bands.json /etc/openwebrx/
+fi
+if [[ ! -f /etc/openwebrx/openwebrx.conf ]] ; then
+ cp openwebrx.conf /etc/openwebrx/
+fi
+if [[ ! -z "${OPENWEBRX_ADMIN_USER:-}" ]] && [[ ! -z "${OPENWEBRX_ADMIN_PASSWORD:-}" ]] ; then
+ if ! python3 openwebrx.py admin --silent hasuser "${OPENWEBRX_ADMIN_USER}" ; then
+ OWRX_PASSWORD="${OPENWEBRX_ADMIN_PASSWORD}" python3 openwebrx.py admin --noninteractive adduser "${OPENWEBRX_ADMIN_USER}"
+ fi
+fi
+
+
+_term() {
+ echo "Caught signal!"
+ kill -TERM "$child" 2>/dev/null
+}
+
+trap _term SIGTERM SIGINT
+
+python3 openwebrx.py $@ &
+
+child=$!
+wait "$child"
+
diff --git a/openwebrx/htdocs/__init__.py b/openwebrx/htdocs/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/openwebrx/htdocs/__pycache__/__init__.cpython-37.pyc b/openwebrx/htdocs/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..e5957a1
Binary files /dev/null and b/openwebrx/htdocs/__pycache__/__init__.cpython-37.pyc differ
diff --git a/openwebrx/htdocs/apple-touch-icon.png b/openwebrx/htdocs/apple-touch-icon.png
new file mode 100644
index 0000000..5bc3c15
Binary files /dev/null and b/openwebrx/htdocs/apple-touch-icon.png differ
diff --git a/openwebrx/htdocs/css/admin.css b/openwebrx/htdocs/css/admin.css
new file mode 100644
index 0000000..b956e9e
--- /dev/null
+++ b/openwebrx/htdocs/css/admin.css
@@ -0,0 +1,162 @@
+@import url("openwebrx-header.css");
+@import url("openwebrx-globals.css");
+
+html, body {
+ height: unset;
+}
+
+body {
+ margin-bottom: 5rem;
+}
+
+hr {
+ background: #444;
+}
+
+.buttons {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background-color: #222;
+ z-index: 2;
+ padding: 10px;
+ text-align: right;
+ border-top: 1px solid #444;
+}
+
+.row .map-input {
+ margin: 15px 15px 0;
+}
+
+.settings-section h3 {
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+h1 {
+ margin: 1em 0;
+ text-align: center;
+}
+
+.matrix {
+ display: grid;
+}
+
+.q65-matrix {
+ grid-template-columns: repeat(5, auto);
+}
+
+.imageupload .image-container {
+ max-width: 100%;
+ padding: 7px;
+}
+
+.imageupload img.webrx-top-photo {
+ max-height: 350px;
+ max-width: 100%;
+}
+
+.settings-grid > div {
+ padding: 20px;
+}
+
+.settings-grid .btn {
+ width: 100%;
+ height: 100px;
+ padding: 20px;
+ font-size: 1.2rem;
+}
+
+.tab-body {
+ overflow: auto;
+ border: 1px solid #444;
+ border-top: none;
+ border-bottom-left-radius: 0.25rem;
+ border-bottom-right-radius: 0.25rem;
+}
+
+.tab-body .form-group {
+ padding-right: 15px;
+}
+
+.bookmarks table .frequency, .bookmark-list table .frequency {
+ text-align: right;
+}
+
+.bookmarks table input, .bookmarks table select {
+ width: initial;
+ text-align: inherit;
+ display: initial;
+}
+
+.bookmark-list table .form-check-input {
+ margin-left: 0;
+}
+
+.actions {
+ margin: 1rem 0;
+}
+
+.actions .btn {
+ width: 100%;
+}
+
+.wsjt-decoding-depths-table {
+ width: auto;
+ margin: 0;
+}
+
+.wsjt-decoding-depths-table td:first-child {
+ padding-left: 0;
+}
+
+.sdr-device-list .list-group-item,
+.sdr-profile-list .list-group-item {
+ background: initial;
+}
+
+.sdr-device-list .sdr-profile-list {
+ max-height: 20rem;
+ overflow-y: auto;
+}
+
+.removable-group.removable, .add-group {
+ display: flex;
+ flex-direction: row;
+}
+
+.removable-group.removable .removable-item, .add-group .add-group-select {
+ flex: 1 0 auto;
+ margin-right: .25rem;
+}
+
+.removable-group.removable .option-remove-button, .add-group .option-add-button {
+ flex: 0 0 70px;
+}
+
+.option-add-button, .option-remove-button {
+ width: 70px;
+}
+
+.scheduler-static-time-inputs {
+ display: flex;
+ flex-direction: row;
+}
+
+.scheduler-static-time-inputs > * {
+ flex: 0 0 auto;
+ width: unset;
+}
+
+.scheduler-static-time-inputs > select {
+ flex: 1 0 auto;
+}
+
+.breadcrumb {
+ margin-top: .5rem;
+}
+
+.imageupload.is-invalid ~ .invalid-feedback {
+ display: block;
+}
\ No newline at end of file
diff --git a/openwebrx/htdocs/css/bootstrap.min.css b/openwebrx/htdocs/css/bootstrap.min.css
new file mode 100644
index 0000000..43d80a0
--- /dev/null
+++ b/openwebrx/htdocs/css/bootstrap.min.css
@@ -0,0 +1,12 @@
+/*!
+ * Bootswatch v4.5.0
+ * Homepage: https://bootswatch.com
+ * Copyright 2012-2020 Thomas Park
+ * Licensed under MIT
+ * Based on Bootstrap
+*//*!
+ * Bootstrap v4.5.0 (https://getbootstrap.com/)
+ * Copyright 2011-2020 The Bootstrap Authors
+ * Copyright 2011-2020 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */@import url("https://fonts.googleapis.com/css?family=Lato:400,700,400italic&display=swap");:root{--blue: #375a7f;--indigo: #6610f2;--purple: #6f42c1;--pink: #e83e8c;--red: #E74C3C;--orange: #fd7e14;--yellow: #F39C12;--green: #00bc8c;--teal: #20c997;--cyan: #3498DB;--white: #fff;--gray: #888;--gray-dark: #303030;--primary: #375a7f;--secondary: #444;--success: #00bc8c;--info: #3498DB;--warning: #F39C12;--danger: #E74C3C;--light: #adb5bd;--dark: #303030;--breakpoint-xs: 0;--breakpoint-sm: 576px;--breakpoint-md: 768px;--breakpoint-lg: 992px;--breakpoint-xl: 1200px;--font-family-sans-serif: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}*,*::before,*::after{-webkit-box-sizing:border-box;box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:"Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-size:0.9375rem;font-weight:400;line-height:1.5;color:#fff;text-align:left;background-color:#222}[tabindex="-1"]:focus:not(:focus-visible){outline:0 !important}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:0.5rem}p{margin-top:0;margin-bottom:1rem}abbr[title],abbr[data-original-title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#00bc8c;text-decoration:none;background-color:transparent}a:hover{color:#007053;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}pre,code,kbd,samp{font-family:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:0.75rem;padding-bottom:0.75rem;color:#888;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:0.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role="button"]{cursor:pointer}select{word-wrap:normal}button,[type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button:not(:disabled),[type="button"]:not(:disabled),[type="reset"]:not(:disabled),[type="submit"]:not(:disabled){cursor:pointer}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{padding:0;border-style:none}input[type="radio"],input[type="checkbox"]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{outline-offset:-2px;-webkit-appearance:none}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none !important}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{margin-bottom:0.5rem;font-weight:500;line-height:1.2}h1,.h1{font-size:3rem}h2,.h2{font-size:2.5rem}h3,.h3{font-size:2rem}h4,.h4{font-size:1.40625rem}h5,.h5{font-size:1.171875rem}h6,.h6{font-size:0.9375rem}.lead{font-size:1.171875rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,0.1)}small,.small{font-size:80%;font-weight:400}mark,.mark{padding:0.2em;background-color:#fcf8e3}.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:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.171875rem}.blockquote-footer{display:block;font-size:80%;color:#888}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:0.25rem;background-color:#222;border:1px solid #dee2e6;border-radius:0.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:0.5rem;line-height:1}.figure-caption{font-size:90%;color:#888}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:0.2rem 0.4rem;font-size:87.5%;color:#fff;background-color:#222;border-radius:0.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:inherit}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width: 576px){.container{max-width:540px}}@media (min-width: 768px){.container{max-width:720px}}@media (min-width: 992px){.container{max-width:960px}}@media (min-width: 1200px){.container{max-width:1140px}}.container-fluid,.container-sm,.container-md,.container-lg,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width: 576px){.container,.container-sm{max-width:540px}}@media (min-width: 768px){.container,.container-sm,.container-md{max-width:720px}}@media (min-width: 992px){.container,.container-sm,.container-md,.container-lg{max-width:960px}}@media (min-width: 1200px){.container,.container-sm,.container-md,.container-lg,.container-xl{max-width:1140px}}.row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*="col-"]{padding-right:0;padding-left:0}.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col,.col-auto,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm,.col-sm-auto,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md,.col-md-auto,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg,.col-lg-auto,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-1>*{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-4>*{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-webkit-box-flex:0;-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-1{margin-left:8.3333333333%}.offset-2{margin-left:16.6666666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.3333333333%}.offset-5{margin-left:41.6666666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.3333333333%}.offset-8{margin-left:66.6666666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.3333333333%}.offset-11{margin-left:91.6666666667%}@media (min-width: 576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-sm-1>*{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-sm-4>*{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-webkit-box-flex:0;-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-sm-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-sm-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-sm-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-sm-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-sm-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-sm-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-sm-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-sm-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-sm-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-sm-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-sm-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-sm-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-sm-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-sm-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-sm-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-sm-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-sm-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-sm-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-sm-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-sm-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-sm-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-sm-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-sm-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.3333333333%}.offset-sm-2{margin-left:16.6666666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.3333333333%}.offset-sm-5{margin-left:41.6666666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.3333333333%}.offset-sm-8{margin-left:66.6666666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.3333333333%}.offset-sm-11{margin-left:91.6666666667%}}@media (min-width: 768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-md-1>*{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-md-4>*{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-webkit-box-flex:0;-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-md-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-md-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-md-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-md-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-md-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-md-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-md-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-md-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-md-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-md-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-md-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-md-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-md-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-md-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-md-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-md-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-md-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-md-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-md-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-md-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-md-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-md-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-md-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.3333333333%}.offset-md-2{margin-left:16.6666666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.3333333333%}.offset-md-5{margin-left:41.6666666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.3333333333%}.offset-md-8{margin-left:66.6666666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.3333333333%}.offset-md-11{margin-left:91.6666666667%}}@media (min-width: 992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-lg-1>*{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-lg-4>*{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-webkit-box-flex:0;-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-lg-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-lg-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-lg-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-lg-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-lg-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-lg-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-lg-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-lg-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-lg-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-lg-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-lg-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-lg-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-lg-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-lg-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-lg-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-lg-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-lg-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-lg-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-lg-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-lg-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-lg-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-lg-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-lg-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.3333333333%}.offset-lg-2{margin-left:16.6666666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.3333333333%}.offset-lg-5{margin-left:41.6666666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.3333333333%}.offset-lg-8{margin-left:66.6666666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.3333333333%}.offset-lg-11{margin-left:91.6666666667%}}@media (min-width: 1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-xl-1>*{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-xl-4>*{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-webkit-box-flex:0;-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-xl-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-xl-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-xl-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-xl-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-xl-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-xl-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-xl-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-xl-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-xl-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-xl-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-xl-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-xl-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-xl-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-xl-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-xl-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-xl-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-xl-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-xl-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-xl-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-xl-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-xl-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-xl-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-xl-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.3333333333%}.offset-xl-2{margin-left:16.6666666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.3333333333%}.offset-xl-5{margin-left:41.6666666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.3333333333%}.offset-xl-8{margin-left:66.6666666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.3333333333%}.offset-xl-11{margin-left:91.6666666667%}}.table{width:100%;margin-bottom:1rem;color:#fff}.table th,.table td{padding:0.75rem;vertical-align:top;border-top:1px solid #444}.table thead th{vertical-align:bottom;border-bottom:2px solid #444}.table tbody+tbody{border-top:2px solid #444}.table-sm th,.table-sm td{padding:0.3rem}.table-bordered{border:1px solid #444}.table-bordered th,.table-bordered td{border:1px solid #444}.table-bordered thead th,.table-bordered thead td{border-bottom-width:2px}.table-borderless th,.table-borderless td,.table-borderless thead th,.table-borderless tbody+tbody{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:#303030}.table-hover tbody tr:hover{color:#fff;background-color:rgba(0,0,0,0.075)}.table-primary,.table-primary>th,.table-primary>td{background-color:#c7d1db}.table-primary th,.table-primary td,.table-primary thead th,.table-primary tbody+tbody{border-color:#97a9bc}.table-hover .table-primary:hover{background-color:#b7c4d1}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#b7c4d1}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#cbcbcb}.table-secondary th,.table-secondary td,.table-secondary thead th,.table-secondary tbody+tbody{border-color:#9e9e9e}.table-hover .table-secondary:hover{background-color:#bebebe}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#bebebe}.table-success,.table-success>th,.table-success>td{background-color:#b8ecdf}.table-success th,.table-success td,.table-success thead th,.table-success tbody+tbody{border-color:#7adcc3}.table-hover .table-success:hover{background-color:#a4e7d6}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#a4e7d6}.table-info,.table-info>th,.table-info>td{background-color:#c6e2f5}.table-info th,.table-info td,.table-info thead th,.table-info tbody+tbody{border-color:#95c9ec}.table-hover .table-info:hover{background-color:#b0d7f1}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#b0d7f1}.table-warning,.table-warning>th,.table-warning>td{background-color:#fce3bd}.table-warning th,.table-warning td,.table-warning thead th,.table-warning tbody+tbody{border-color:#f9cc84}.table-hover .table-warning:hover{background-color:#fbd9a5}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#fbd9a5}.table-danger,.table-danger>th,.table-danger>td{background-color:#f8cdc8}.table-danger th,.table-danger td,.table-danger thead th,.table-danger tbody+tbody{border-color:#f3a29a}.table-hover .table-danger:hover{background-color:#f5b8b1}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f5b8b1}.table-light,.table-light>th,.table-light>td{background-color:#e8eaed}.table-light th,.table-light td,.table-light thead th,.table-light tbody+tbody{border-color:#d4d9dd}.table-hover .table-light:hover{background-color:#dadde2}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#dadde2}.table-dark,.table-dark>th,.table-dark>td{background-color:#c5c5c5}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:#939393}.table-hover .table-dark:hover{background-color:#b8b8b8}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b8b8b8}.table-active,.table-active>th,.table-active>td{background-color:rgba(0,0,0,0.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,0.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,0.075)}.table .thead-dark th{color:#fff;background-color:#303030;border-color:#434343}.table .thead-light th{color:#444;background-color:#ebebeb;border-color:#444}.table-dark{color:#fff;background-color:#303030}.table-dark th,.table-dark td,.table-dark thead th{border-color:#434343}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,0.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,0.075)}@media (max-width: 575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width: 767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width: 991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width: 1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + 0.75rem + 2px);padding:0.375rem 0.75rem;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#444;background-color:#fff;background-clip:padding-box;border:1px solid #222;border-radius:0.25rem;-webkit-transition:border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.form-control{-webkit-transition:none;transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #444}.form-control:focus{color:#444;background-color:#fff;border-color:#739ac2;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25);box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25)}.form-control::-webkit-input-placeholder{color:#888;opacity:1}.form-control::-ms-input-placeholder{color:#888;opacity:1}.form-control::placeholder{color:#888;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#ebebeb;opacity:1}input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:focus::-ms-value{color:#444;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(0.375rem + 1px);padding-bottom:calc(0.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(0.5rem + 1px);padding-bottom:calc(0.5rem + 1px);font-size:1.171875rem;line-height:1.5}.col-form-label-sm{padding-top:calc(0.25rem + 1px);padding-bottom:calc(0.25rem + 1px);font-size:0.8203125rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:0.375rem 0;margin-bottom:0;font-size:0.9375rem;line-height:1.5;color:#fff;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + 0.5rem + 2px);padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}select.form-control[size],select.form-control[multiple]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:0.25rem}.form-row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*="col-"]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:0.3rem;margin-left:-1.25rem}.form-check-input[disabled] ~ .form-check-label,.form-check-input:disabled ~ .form-check-label{color:#888}.form-check-label{margin-bottom:0}.form-check-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:0.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:0.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:0.25rem;font-size:80%;color:#00bc8c}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:0.25rem 0.5rem;margin-top:.1rem;font-size:0.8203125rem;line-height:1.5;color:#fff;background-color:rgba(0,188,140,0.9);border-radius:0.25rem}.was-validated :valid ~ .valid-feedback,.was-validated :valid ~ .valid-tooltip,.is-valid ~ .valid-feedback,.is-valid ~ .valid-tooltip{display:block}.was-validated .form-control:valid,.form-control.is-valid{border-color:#00bc8c;padding-right:calc(1.5em + 0.75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2300bc8c' d='M2.3 6.73L.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)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:#00bc8c;-webkit-box-shadow:0 0 0 0.2rem rgba(0,188,140,0.25);box-shadow:0 0 0 0.2rem rgba(0,188,140,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)}.was-validated .custom-select:valid,.custom-select.is-valid{border-color:#00bc8c;padding-right:calc(0.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23303030' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2300bc8c' d='M2.3 6.73L.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") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .custom-select:valid:focus,.custom-select.is-valid:focus{border-color:#00bc8c;-webkit-box-shadow:0 0 0 0.2rem rgba(0,188,140,0.25);box-shadow:0 0 0 0.2rem rgba(0,188,140,0.25)}.was-validated .form-check-input:valid ~ .form-check-label,.form-check-input.is-valid ~ .form-check-label{color:#00bc8c}.was-validated .form-check-input:valid ~ .valid-feedback,.was-validated .form-check-input:valid ~ .valid-tooltip,.form-check-input.is-valid ~ .valid-feedback,.form-check-input.is-valid ~ .valid-tooltip{display:block}.was-validated .custom-control-input:valid ~ .custom-control-label,.custom-control-input.is-valid ~ .custom-control-label{color:#00bc8c}.was-validated .custom-control-input:valid ~ .custom-control-label::before,.custom-control-input.is-valid ~ .custom-control-label::before{border-color:#00bc8c}.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before,.custom-control-input.is-valid:checked ~ .custom-control-label::before{border-color:#00efb2;background-color:#00efb2}.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before,.custom-control-input.is-valid:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(0,188,140,0.25);box-shadow:0 0 0 0.2rem rgba(0,188,140,0.25)}.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before,.custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before{border-color:#00bc8c}.was-validated .custom-file-input:valid ~ .custom-file-label,.custom-file-input.is-valid ~ .custom-file-label{border-color:#00bc8c}.was-validated .custom-file-input:valid:focus ~ .custom-file-label,.custom-file-input.is-valid:focus ~ .custom-file-label{border-color:#00bc8c;-webkit-box-shadow:0 0 0 0.2rem rgba(0,188,140,0.25);box-shadow:0 0 0 0.2rem rgba(0,188,140,0.25)}.invalid-feedback{display:none;width:100%;margin-top:0.25rem;font-size:80%;color:#E74C3C}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:0.25rem 0.5rem;margin-top:.1rem;font-size:0.8203125rem;line-height:1.5;color:#fff;background-color:rgba(231,76,60,0.9);border-radius:0.25rem}.was-validated :invalid ~ .invalid-feedback,.was-validated :invalid ~ .invalid-tooltip,.is-invalid ~ .invalid-feedback,.is-invalid ~ .invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:#E74C3C;padding-right:calc(1.5em + 0.75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23E74C3C' viewBox='0 0 12 12'%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='%23E74C3C' 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)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:#E74C3C;-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25);box-shadow:0 0 0 0.2rem rgba(231,76,60,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)}.was-validated .custom-select:invalid,.custom-select.is-invalid{border-color:#E74C3C;padding-right:calc(0.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23303030' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23E74C3C' viewBox='0 0 12 12'%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='%23E74C3C' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .custom-select:invalid:focus,.custom-select.is-invalid:focus{border-color:#E74C3C;-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25)}.was-validated .form-check-input:invalid ~ .form-check-label,.form-check-input.is-invalid ~ .form-check-label{color:#E74C3C}.was-validated .form-check-input:invalid ~ .invalid-feedback,.was-validated .form-check-input:invalid ~ .invalid-tooltip,.form-check-input.is-invalid ~ .invalid-feedback,.form-check-input.is-invalid ~ .invalid-tooltip{display:block}.was-validated .custom-control-input:invalid ~ .custom-control-label,.custom-control-input.is-invalid ~ .custom-control-label{color:#E74C3C}.was-validated .custom-control-input:invalid ~ .custom-control-label::before,.custom-control-input.is-invalid ~ .custom-control-label::before{border-color:#E74C3C}.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before,.custom-control-input.is-invalid:checked ~ .custom-control-label::before{border-color:#ed7669;background-color:#ed7669}.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before,.custom-control-input.is-invalid:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25)}.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before,.custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before{border-color:#E74C3C}.was-validated .custom-file-input:invalid ~ .custom-file-label,.custom-file-input.is-invalid ~ .custom-file-label{border-color:#E74C3C}.was-validated .custom-file-input:invalid:focus ~ .custom-file-label,.custom-file-input.is-invalid:focus ~ .custom-file-label{border-color:#E74C3C;-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.25)}.form-inline{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width: 576px){.form-inline label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .input-group,.form-inline .custom-select{width:auto}.form-inline .form-check{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:0.25rem;margin-left:0}.form-inline .custom-control{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#fff;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:0.375rem 0.75rem;font-size:0.9375rem;line-height:1.5;border-radius:0.25rem;-webkit-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-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, -webkit-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;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, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.btn{-webkit-transition:none;transition:none}}.btn:hover{color:#fff;text-decoration:none}.btn:focus,.btn.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25);box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25)}.btn.disabled,.btn:disabled{opacity:0.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#375a7f;border-color:#375a7f}.btn-primary:hover{color:#fff;background-color:#2b4764;border-color:#28415b}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#2b4764;border-color:#28415b;-webkit-box-shadow:0 0 0 0.2rem rgba(85,115,146,0.5);box-shadow:0 0 0 0.2rem rgba(85,115,146,0.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#375a7f;border-color:#375a7f}.btn-primary:not(:disabled):not(.disabled):active,.btn-primary:not(:disabled):not(.disabled).active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#28415b;border-color:#243a53}.btn-primary:not(:disabled):not(.disabled):active:focus,.btn-primary:not(:disabled):not(.disabled).active:focus,.show>.btn-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(85,115,146,0.5);box-shadow:0 0 0 0.2rem rgba(85,115,146,0.5)}.btn-secondary{color:#fff;background-color:#444;border-color:#444}.btn-secondary:hover{color:#fff;background-color:#313131;border-color:#2b2a2a}.btn-secondary:focus,.btn-secondary.focus{color:#fff;background-color:#313131;border-color:#2b2a2a;-webkit-box-shadow:0 0 0 0.2rem rgba(96,96,96,0.5);box-shadow:0 0 0 0.2rem rgba(96,96,96,0.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#444;border-color:#444}.btn-secondary:not(:disabled):not(.disabled):active,.btn-secondary:not(:disabled):not(.disabled).active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#2b2a2a;border-color:#242424}.btn-secondary:not(:disabled):not(.disabled):active:focus,.btn-secondary:not(:disabled):not(.disabled).active:focus,.show>.btn-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(96,96,96,0.5);box-shadow:0 0 0 0.2rem rgba(96,96,96,0.5)}.btn-success{color:#fff;background-color:#00bc8c;border-color:#00bc8c}.btn-success:hover{color:#fff;background-color:#009670;border-color:#008966}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#009670;border-color:#008966;-webkit-box-shadow:0 0 0 0.2rem rgba(38,198,157,0.5);box-shadow:0 0 0 0.2rem rgba(38,198,157,0.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#00bc8c;border-color:#00bc8c}.btn-success:not(:disabled):not(.disabled):active,.btn-success:not(:disabled):not(.disabled).active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#008966;border-color:#007c5d}.btn-success:not(:disabled):not(.disabled):active:focus,.btn-success:not(:disabled):not(.disabled).active:focus,.show>.btn-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(38,198,157,0.5);box-shadow:0 0 0 0.2rem rgba(38,198,157,0.5)}.btn-info{color:#fff;background-color:#3498DB;border-color:#3498DB}.btn-info:hover{color:#fff;background-color:#2384c6;border-color:#217dbb}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#2384c6;border-color:#217dbb;-webkit-box-shadow:0 0 0 0.2rem rgba(82,167,224,0.5);box-shadow:0 0 0 0.2rem rgba(82,167,224,0.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#3498DB;border-color:#3498DB}.btn-info:not(:disabled):not(.disabled):active,.btn-info:not(:disabled):not(.disabled).active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#217dbb;border-color:#1f76b0}.btn-info:not(:disabled):not(.disabled):active:focus,.btn-info:not(:disabled):not(.disabled).active:focus,.show>.btn-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(82,167,224,0.5);box-shadow:0 0 0 0.2rem rgba(82,167,224,0.5)}.btn-warning{color:#fff;background-color:#F39C12;border-color:#F39C12}.btn-warning:hover{color:#fff;background-color:#d4860b;border-color:#c87f0a}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#d4860b;border-color:#c87f0a;-webkit-box-shadow:0 0 0 0.2rem rgba(245,171,54,0.5);box-shadow:0 0 0 0.2rem rgba(245,171,54,0.5)}.btn-warning.disabled,.btn-warning:disabled{color:#fff;background-color:#F39C12;border-color:#F39C12}.btn-warning:not(:disabled):not(.disabled):active,.btn-warning:not(:disabled):not(.disabled).active,.show>.btn-warning.dropdown-toggle{color:#fff;background-color:#c87f0a;border-color:#bc770a}.btn-warning:not(:disabled):not(.disabled):active:focus,.btn-warning:not(:disabled):not(.disabled).active:focus,.show>.btn-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(245,171,54,0.5);box-shadow:0 0 0 0.2rem rgba(245,171,54,0.5)}.btn-danger{color:#fff;background-color:#E74C3C;border-color:#E74C3C}.btn-danger:hover{color:#fff;background-color:#e12e1c;border-color:#d62c1a}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#e12e1c;border-color:#d62c1a;-webkit-box-shadow:0 0 0 0.2rem rgba(235,103,89,0.5);box-shadow:0 0 0 0.2rem rgba(235,103,89,0.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#E74C3C;border-color:#E74C3C}.btn-danger:not(:disabled):not(.disabled):active,.btn-danger:not(:disabled):not(.disabled).active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#d62c1a;border-color:#ca2a19}.btn-danger:not(:disabled):not(.disabled):active:focus,.btn-danger:not(:disabled):not(.disabled).active:focus,.show>.btn-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(235,103,89,0.5);box-shadow:0 0 0 0.2rem rgba(235,103,89,0.5)}.btn-light{color:#222;background-color:#adb5bd;border-color:#adb5bd}.btn-light:hover{color:#fff;background-color:#98a2ac;border-color:#919ca6}.btn-light:focus,.btn-light.focus{color:#fff;background-color:#98a2ac;border-color:#919ca6;-webkit-box-shadow:0 0 0 0.2rem rgba(152,159,166,0.5);box-shadow:0 0 0 0.2rem rgba(152,159,166,0.5)}.btn-light.disabled,.btn-light:disabled{color:#222;background-color:#adb5bd;border-color:#adb5bd}.btn-light:not(:disabled):not(.disabled):active,.btn-light:not(:disabled):not(.disabled).active,.show>.btn-light.dropdown-toggle{color:#fff;background-color:#919ca6;border-color:#8a95a1}.btn-light:not(:disabled):not(.disabled):active:focus,.btn-light:not(:disabled):not(.disabled).active:focus,.show>.btn-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(152,159,166,0.5);box-shadow:0 0 0 0.2rem rgba(152,159,166,0.5)}.btn-dark{color:#fff;background-color:#303030;border-color:#303030}.btn-dark:hover{color:#fff;background-color:#1d1d1d;border-color:#171616}.btn-dark:focus,.btn-dark.focus{color:#fff;background-color:#1d1d1d;border-color:#171616;-webkit-box-shadow:0 0 0 0.2rem rgba(79,79,79,0.5);box-shadow:0 0 0 0.2rem rgba(79,79,79,0.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#303030;border-color:#303030}.btn-dark:not(:disabled):not(.disabled):active,.btn-dark:not(:disabled):not(.disabled).active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#171616;border-color:#101010}.btn-dark:not(:disabled):not(.disabled):active:focus,.btn-dark:not(:disabled):not(.disabled).active:focus,.show>.btn-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(79,79,79,0.5);box-shadow:0 0 0 0.2rem rgba(79,79,79,0.5)}.btn-outline-primary{color:#375a7f;border-color:#375a7f}.btn-outline-primary:hover{color:#fff;background-color:#375a7f;border-color:#375a7f}.btn-outline-primary:focus,.btn-outline-primary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(55,90,127,0.5);box-shadow:0 0 0 0.2rem rgba(55,90,127,0.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#375a7f;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled):active,.btn-outline-primary:not(:disabled):not(.disabled).active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#375a7f;border-color:#375a7f}.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(55,90,127,0.5);box-shadow:0 0 0 0.2rem rgba(55,90,127,0.5)}.btn-outline-secondary{color:#444;border-color:#444}.btn-outline-secondary:hover{color:#fff;background-color:#444;border-color:#444}.btn-outline-secondary:focus,.btn-outline-secondary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(68,68,68,0.5);box-shadow:0 0 0 0.2rem rgba(68,68,68,0.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#444;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled):active,.btn-outline-secondary:not(:disabled):not(.disabled).active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#444;border-color:#444}.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(68,68,68,0.5);box-shadow:0 0 0 0.2rem rgba(68,68,68,0.5)}.btn-outline-success{color:#00bc8c;border-color:#00bc8c}.btn-outline-success:hover{color:#fff;background-color:#00bc8c;border-color:#00bc8c}.btn-outline-success:focus,.btn-outline-success.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(0,188,140,0.5);box-shadow:0 0 0 0.2rem rgba(0,188,140,0.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#00bc8c;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled):active,.btn-outline-success:not(:disabled):not(.disabled).active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#00bc8c;border-color:#00bc8c}.btn-outline-success:not(:disabled):not(.disabled):active:focus,.btn-outline-success:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(0,188,140,0.5);box-shadow:0 0 0 0.2rem rgba(0,188,140,0.5)}.btn-outline-info{color:#3498DB;border-color:#3498DB}.btn-outline-info:hover{color:#fff;background-color:#3498DB;border-color:#3498DB}.btn-outline-info:focus,.btn-outline-info.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5);box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#3498DB;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled):active,.btn-outline-info:not(:disabled):not(.disabled).active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#3498DB;border-color:#3498DB}.btn-outline-info:not(:disabled):not(.disabled):active:focus,.btn-outline-info:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5);box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5)}.btn-outline-warning{color:#F39C12;border-color:#F39C12}.btn-outline-warning:hover{color:#fff;background-color:#F39C12;border-color:#F39C12}.btn-outline-warning:focus,.btn-outline-warning.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5);box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#F39C12;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled):active,.btn-outline-warning:not(:disabled):not(.disabled).active,.show>.btn-outline-warning.dropdown-toggle{color:#fff;background-color:#F39C12;border-color:#F39C12}.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5);box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5)}.btn-outline-danger{color:#E74C3C;border-color:#E74C3C}.btn-outline-danger:hover{color:#fff;background-color:#E74C3C;border-color:#E74C3C}.btn-outline-danger:focus,.btn-outline-danger.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#E74C3C;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled):active,.btn-outline-danger:not(:disabled):not(.disabled).active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#E74C3C;border-color:#E74C3C}.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5)}.btn-outline-light{color:#adb5bd;border-color:#adb5bd}.btn-outline-light:hover{color:#222;background-color:#adb5bd;border-color:#adb5bd}.btn-outline-light:focus,.btn-outline-light.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(173,181,189,0.5);box-shadow:0 0 0 0.2rem rgba(173,181,189,0.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#adb5bd;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled):active,.btn-outline-light:not(:disabled):not(.disabled).active,.show>.btn-outline-light.dropdown-toggle{color:#222;background-color:#adb5bd;border-color:#adb5bd}.btn-outline-light:not(:disabled):not(.disabled):active:focus,.btn-outline-light:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(173,181,189,0.5);box-shadow:0 0 0 0.2rem rgba(173,181,189,0.5)}.btn-outline-dark{color:#303030;border-color:#303030}.btn-outline-dark:hover{color:#fff;background-color:#303030;border-color:#303030}.btn-outline-dark:focus,.btn-outline-dark.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(48,48,48,0.5);box-shadow:0 0 0 0.2rem rgba(48,48,48,0.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#303030;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled):active,.btn-outline-dark:not(:disabled):not(.disabled).active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#303030;border-color:#303030}.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(48,48,48,0.5);box-shadow:0 0 0 0.2rem rgba(48,48,48,0.5)}.btn-link{font-weight:400;color:#00bc8c;text-decoration:none}.btn-link:hover{color:#007053;text-decoration:underline}.btn-link:focus,.btn-link.focus{text-decoration:underline}.btn-link:disabled,.btn-link.disabled{color:#888;pointer-events:none}.btn-lg,.btn-group-lg>.btn{padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}.btn-sm,.btn-group-sm>.btn{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:0.5rem}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{-webkit-transition:opacity 0.15s linear;transition:opacity 0.15s linear}@media (prefers-reduced-motion: reduce){.fade{-webkit-transition:none;transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;transition:height 0.35s ease}@media (prefers-reduced-motion: reduce){.collapsing{-webkit-transition:none;transition:none}}.dropup,.dropright,.dropdown,.dropleft{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{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:0.5rem 0;margin:0.125rem 0 0;font-size:0.9375rem;color:#fff;text-align:left;list-style:none;background-color:#222;background-clip:padding-box;border:1px solid #444;border-radius:0.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width: 576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width: 768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width: 992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width: 1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:0.125rem}.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}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:0.125rem}.dropright .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}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:0.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .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}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^="top"],.dropdown-menu[x-placement^="right"],.dropdown-menu[x-placement^="bottom"],.dropdown-menu[x-placement^="left"]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:0.5rem 0;overflow:hidden;border-top:1px solid #444}.dropdown-item{display:block;width:100%;padding:0.25rem 1.5rem;clear:both;font-weight:400;color:#fff;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:hover,.dropdown-item:focus{color:#fff;text-decoration:none;background-color:#375a7f}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#375a7f}.dropdown-item.disabled,.dropdown-item:disabled{color:#888;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:0.5rem 1.5rem;margin-bottom:0;font-size:0.8203125rem;color:#888;white-space:nowrap}.dropdown-item-text{display:block;padding:0.25rem 1.5rem;color:#fff}.btn-group,.btn-group-vertical{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover{z-index:1}.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-toolbar{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child){margin-left:-1px}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child)>.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,.dropup .dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:0.375rem;padding-left:0.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:0.75rem;padding-left:0.75rem}.btn-group-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type="radio"],.btn-group-toggle>.btn input[type="checkbox"],.btn-group-toggle>.btn-group>.btn input[type="radio"],.btn-group-toggle>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-control-plaintext,.input-group>.custom-select,.input-group>.custom-file{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.form-control+.form-control,.input-group>.form-control+.custom-select,.input-group>.form-control+.custom-file,.input-group>.form-control-plaintext+.form-control,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.custom-file,.input-group>.custom-select+.form-control,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.custom-file,.input-group>.custom-file+.form-control,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.custom-file{margin-left:-1px}.input-group>.form-control:focus,.input-group>.custom-select:focus,.input-group>.custom-file .custom-file-input:focus ~ .custom-file-label{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.form-control:not(:last-child),.input-group>.custom-select:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.form-control:not(:first-child),.input-group>.custom-select:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-prepend,.input-group-append{display:-webkit-box;display:-ms-flexbox;display:flex}.input-group-prepend .btn,.input-group-append .btn{position:relative;z-index:2}.input-group-prepend .btn:focus,.input-group-append .btn:focus{z-index:3}.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.input-group-text,.input-group-append .input-group-text+.btn{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0.375rem 0.75rem;margin-bottom:0;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#adb5bd;text-align:center;white-space:nowrap;background-color:#444;border:1px solid #222;border-radius:0.25rem}.input-group-text input[type="radio"],.input-group-text input[type="checkbox"]{margin-top:0}.input-group-lg>.form-control:not(textarea),.input-group-lg>.custom-select{height:calc(1.5em + 1rem + 2px)}.input-group-lg>.form-control,.input-group-lg>.custom-select,.input-group-lg>.input-group-prepend>.input-group-text,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-append>.btn{padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}.input-group-sm>.form-control:not(textarea),.input-group-sm>.custom-select{height:calc(1.5em + 0.5rem + 2px)}.input-group-sm>.form-control,.input-group-sm>.custom-select,.input-group-sm>.input-group-prepend>.input-group-text,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-append>.btn{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text,.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.40625rem;padding-left:1.5rem}.custom-control-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.203125rem;opacity:0}.custom-control-input:checked ~ .custom-control-label::before{color:#fff;border-color:#375a7f;background-color:#375a7f}.custom-control-input:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25);box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25)}.custom-control-input:focus:not(:checked) ~ .custom-control-label::before{border-color:#739ac2}.custom-control-input:not(:disabled):active ~ .custom-control-label::before{color:#fff;background-color:#97b3d2;border-color:#97b3d2}.custom-control-input[disabled] ~ .custom-control-label,.custom-control-input:disabled ~ .custom-control-label{color:#888}.custom-control-input[disabled] ~ .custom-control-label::before,.custom-control-input:disabled ~ .custom-control-label::before{background-color:#ebebeb}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:0.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:0.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50% / 50% 50%}.custom-checkbox .custom-control-label::before{border-radius:0.25rem}.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before{border-color:#375a7f;background-color:#375a7f}.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before{background-color:rgba(55,90,127,0.5)}.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before{background-color:rgba(55,90,127,0.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked ~ .custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before{background-color:rgba(55,90,127,0.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:0.5rem}.custom-switch .custom-control-label::after{top:calc(0.203125rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:0.5rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:transform 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:transform 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, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.custom-switch .custom-control-label::after{-webkit-transition:none;transition:none}}.custom-switch .custom-control-input:checked ~ .custom-control-label::after{background-color:#fff;-webkit-transform:translateX(0.75rem);transform:translateX(0.75rem)}.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before{background-color:rgba(55,90,127,0.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + 0.75rem + 2px);padding:0.375rem 1.75rem 0.375rem 0.75rem;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#444;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23303030' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px;border:1px solid #222;border-radius:0.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#739ac2;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25);box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25)}.custom-select:focus::-ms-value{color:#444;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:0.75rem;background-image:none}.custom-select:disabled{color:#888;background-color:#ebebeb}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #444}.custom-select-sm{height:calc(1.5em + 0.5rem + 2px);padding-top:0.25rem;padding-bottom:0.25rem;padding-left:0.5rem;font-size:0.8203125rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:0.5rem;padding-bottom:0.5rem;padding-left:1rem;font-size:1.171875rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + 0.75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + 0.75rem + 2px);margin:0;opacity:0}.custom-file-input:focus ~ .custom-file-label{border-color:#739ac2;-webkit-box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25);box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25)}.custom-file-input[disabled] ~ .custom-file-label,.custom-file-input:disabled ~ .custom-file-label{background-color:#ebebeb}.custom-file-input:lang(en) ~ .custom-file-label::after{content:"Browse"}.custom-file-input ~ .custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + 0.75rem + 2px);padding:0.375rem 0.75rem;font-weight:400;line-height:1.5;color:#adb5bd;background-color:#fff;border:1px solid #222;border-radius:0.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + 0.75rem);padding:0.375rem 0.75rem;line-height:1.5;color:#adb5bd;content:"Browse";background-color:#444;border-left:inherit;border-radius:0 0.25rem 0.25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:none}.custom-range:focus::-webkit-slider-thumb{-webkit-box-shadow:0 0 0 1px #222,0 0 0 0.2rem rgba(55,90,127,0.25);box-shadow:0 0 0 1px #222,0 0 0 0.2rem rgba(55,90,127,0.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #222,0 0 0 0.2rem rgba(55,90,127,0.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #222,0 0 0 0.2rem rgba(55,90,127,0.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-0.25rem;background-color:#375a7f;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-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;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion: reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#97b3d2}.custom-range::-webkit-slider-runnable-track{width:100%;height:0.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#375a7f;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-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;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion: reduce){.custom-range::-moz-range-thumb{-webkit-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#97b3d2}.custom-range::-moz-range-track{width:100%;height:0.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:0.2rem;margin-left:0.2rem;background-color:#375a7f;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-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;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;appearance:none}@media (prefers-reduced-motion: reduce){.custom-range::-ms-thumb{-webkit-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#97b3d2}.custom-range::-ms-track{width:100%;height:0.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:0.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-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;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:none;transition:none}}.nav{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:0.5rem 2rem}.nav-link:hover,.nav-link:focus{text-decoration:none}.nav-link.disabled{color:#adb5bd;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #444}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{border-color:#444 #444 transparent}.nav-tabs .nav-link.disabled{color:#adb5bd;background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:#fff;background-color:#222;border-color:#444 #444 transparent}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:0.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#375a7f}.nav-fill .nav-item{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-sm,.navbar .container-md,.navbar .container-lg,.navbar .container-xl{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:0.32421875rem;padding-bottom:0.32421875rem;margin-right:1rem;font-size:1.171875rem;line-height:inherit;white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-nav{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:0.5rem;padding-bottom:0.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:0.25rem 0.75rem;font-size:1.171875rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:0.25rem}.navbar-toggler:hover,.navbar-toggler:focus{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width: 575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 576px){.navbar-expand-sm{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width: 767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-md,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 768px){.navbar-expand-md{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-md,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width: 991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 992px){.navbar-expand-lg{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width: 1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 1200px){.navbar-expand-xl{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-sm,.navbar-expand>.container-md,.navbar-expand>.container-lg,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-sm,.navbar-expand>.container-md,.navbar-expand>.container-lg,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:#222}.navbar-light .navbar-brand:hover,.navbar-light .navbar-brand:focus{color:#222}.navbar-light .navbar-nav .nav-link{color:rgba(34,34,34,0.7)}.navbar-light .navbar-nav .nav-link:hover,.navbar-light .navbar-nav .nav-link:focus{color:#222}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,0.3)}.navbar-light .navbar-nav .show>.nav-link,.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .nav-link.active{color:#222}.navbar-light .navbar-toggler{color:rgba(34,34,34,0.7);border-color:rgba(34,34,34,0.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2834, 34, 34, 0.7%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(34,34,34,0.7)}.navbar-light .navbar-text a{color:#222}.navbar-light .navbar-text a:hover,.navbar-light .navbar-text a:focus{color:#222}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:hover,.navbar-dark .navbar-brand:focus{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,0.6)}.navbar-dark .navbar-nav .nav-link:hover,.navbar-dark .navbar-nav .nav-link:focus{color:#fff}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,0.25)}.navbar-dark .navbar-nav .show>.nav-link,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .nav-link.active{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,0.6);border-color:rgba(255,255,255,0.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.6%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,0.6)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:hover,.navbar-dark .navbar-text a:focus{color:#fff}.card{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#303030;background-clip:border-box;border:1px solid rgba(0,0,0,0.125);border-radius:0.25rem}.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:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(0.25rem - 1px);border-bottom-left-radius:calc(0.25rem - 1px)}.card-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:0.75rem}.card-subtitle{margin-top:-0.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:0.75rem 1.25rem;margin-bottom:0;background-color:#444;border-bottom:1px solid rgba(0,0,0,0.125)}.card-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:0.75rem 1.25rem;background-color:#444;border-top:1px solid rgba(0,0,0,0.125)}.card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.card-header-tabs{margin-right:-0.625rem;margin-bottom:-0.75rem;margin-left:-0.625rem;border-bottom:0}.card-header-pills{margin-right:-0.625rem;margin-left:-0.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-top,.card-img-bottom{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(0.25rem - 1px);border-bottom-left-radius:calc(0.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width: 576px){.card-deck{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width: 576px){.card-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-img-top,.card-group>.card:not(:last-child) .card-header{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-img-bottom,.card-group>.card:not(:last-child) .card-footer{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-img-top,.card-group>.card:not(:first-child) .card-header{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-img-bottom,.card-group>.card:not(:first-child) .card-footer{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:0.75rem}@media (min-width: 576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#444;border-radius:0.25rem}.breadcrumb-item{display:-webkit-box;display:-ms-flexbox;display:flex}.breadcrumb-item+.breadcrumb-item{padding-left:0.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:0.5rem;color:#888;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#888}.pagination{display:-webkit-box;display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:0.25rem}.page-link{position:relative;display:block;padding:0.5rem 0.75rem;margin-left:0;line-height:1.25;color:#fff;background-color:#00bc8c;border:0 solid transparent}.page-link:hover{z-index:2;color:#fff;text-decoration:none;background-color:#00efb2;border-color:transparent}.page-link:focus{z-index:3;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25);box-shadow:0 0 0 0.2rem rgba(55,90,127,0.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:0.25rem;border-bottom-left-radius:0.25rem}.page-item:last-child .page-link{border-top-right-radius:0.25rem;border-bottom-right-radius:0.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#00efb2;border-color:transparent}.page-item.disabled .page-link{color:#fff;pointer-events:none;cursor:auto;background-color:#007053;border-color:transparent}.pagination-lg .page-link{padding:0.75rem 1.5rem;font-size:1.171875rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:0.3rem;border-bottom-left-radius:0.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:0.3rem;border-bottom-right-radius:0.3rem}.pagination-sm .page-link{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:0.2rem;border-bottom-left-radius:0.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:0.2rem;border-bottom-right-radius:0.2rem}.badge{display:inline-block;padding:0.25em 0.4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:0.25rem;-webkit-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-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, -webkit-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;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, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.badge{-webkit-transition:none;transition:none}}a.badge:hover,a.badge:focus{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:0.6em;padding-left:0.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#375a7f}a.badge-primary:hover,a.badge-primary:focus{color:#fff;background-color:#28415b}a.badge-primary:focus,a.badge-primary.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(55,90,127,0.5);box-shadow:0 0 0 0.2rem rgba(55,90,127,0.5)}.badge-secondary{color:#fff;background-color:#444}a.badge-secondary:hover,a.badge-secondary:focus{color:#fff;background-color:#2b2a2a}a.badge-secondary:focus,a.badge-secondary.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(68,68,68,0.5);box-shadow:0 0 0 0.2rem rgba(68,68,68,0.5)}.badge-success{color:#fff;background-color:#00bc8c}a.badge-success:hover,a.badge-success:focus{color:#fff;background-color:#008966}a.badge-success:focus,a.badge-success.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(0,188,140,0.5);box-shadow:0 0 0 0.2rem rgba(0,188,140,0.5)}.badge-info{color:#fff;background-color:#3498DB}a.badge-info:hover,a.badge-info:focus{color:#fff;background-color:#217dbb}a.badge-info:focus,a.badge-info.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5);box-shadow:0 0 0 0.2rem rgba(52,152,219,0.5)}.badge-warning{color:#fff;background-color:#F39C12}a.badge-warning:hover,a.badge-warning:focus{color:#fff;background-color:#c87f0a}a.badge-warning:focus,a.badge-warning.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5);box-shadow:0 0 0 0.2rem rgba(243,156,18,0.5)}.badge-danger{color:#fff;background-color:#E74C3C}a.badge-danger:hover,a.badge-danger:focus{color:#fff;background-color:#d62c1a}a.badge-danger:focus,a.badge-danger.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5);box-shadow:0 0 0 0.2rem rgba(231,76,60,0.5)}.badge-light{color:#222;background-color:#adb5bd}a.badge-light:hover,a.badge-light:focus{color:#222;background-color:#919ca6}a.badge-light:focus,a.badge-light.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(173,181,189,0.5);box-shadow:0 0 0 0.2rem rgba(173,181,189,0.5)}.badge-dark{color:#fff;background-color:#303030}a.badge-dark:hover,a.badge-dark:focus{color:#fff;background-color:#171616}a.badge-dark:focus,a.badge-dark.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(48,48,48,0.5);box-shadow:0 0 0 0.2rem rgba(48,48,48,0.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#303030;border-radius:0.3rem}@media (min-width: 576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:0.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:0.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3.90625rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:0.75rem 1.25rem;color:inherit}.alert-primary{color:#1d2f42;background-color:#d7dee5;border-color:#c7d1db}.alert-primary hr{border-top-color:#b7c4d1}.alert-primary .alert-link{color:#0d161f}.alert-secondary{color:#232323;background-color:#dadada;border-color:#cbcbcb}.alert-secondary hr{border-top-color:#bebebe}.alert-secondary .alert-link{color:#0a0909}.alert-success{color:#006249;background-color:#ccf2e8;border-color:#b8ecdf}.alert-success hr{border-top-color:#a4e7d6}.alert-success .alert-link{color:#002f23}.alert-info{color:#1b4f72;background-color:#d6eaf8;border-color:#c6e2f5}.alert-info hr{border-top-color:#b0d7f1}.alert-info .alert-link{color:#113249}.alert-warning{color:#7e5109;background-color:#fdebd0;border-color:#fce3bd}.alert-warning hr{border-top-color:#fbd9a5}.alert-warning .alert-link{color:#4e3206}.alert-danger{color:#78281f;background-color:#fadbd8;border-color:#f8cdc8}.alert-danger hr{border-top-color:#f5b8b1}.alert-danger .alert-link{color:#4f1a15}.alert-light{color:#5a5e62;background-color:#eff0f2;border-color:#e8eaed}.alert-light hr{border-top-color:#dadde2}.alert-light .alert-link{color:#424547}.alert-dark{color:#191919;background-color:#d6d6d6;border-color:#c5c5c5}.alert-dark hr{border-top-color:#b8b8b8}.alert-dark .alert-link{color:black}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-webkit-box;display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:0.703125rem;background-color:#444;border-radius:0.25rem}.progress-bar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#375a7f;-webkit-transition:width 0.6s ease;transition:width 0.6s ease}@media (prefers-reduced-motion: reduce){.progress-bar{-webkit-transition:none;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:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion: reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.media-body{-webkit-box-flex:1;-ms-flex:1;flex:1}.list-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:0.25rem}.list-group-item-action{width:100%;color:#444;text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:#444;text-decoration:none;background-color:#444}.list-group-item-action:active{color:#fff;background-color:#ebebeb}.list-group-item{position:relative;display:block;padding:0.75rem 1.25rem;background-color:#303030;border:1px solid #444}.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:#888;pointer-events:none;background-color:#303030}.list-group-item.active{z-index:2;color:#fff;background-color:#375a7f;border-color:#375a7f}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:0.25rem;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:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width: 576px){.list-group-horizontal-sm{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:0.25rem;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:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width: 768px){.list-group-horizontal-md{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:0.25rem;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:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width: 992px){.list-group-horizontal-lg{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:0.25rem;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:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width: 1200px){.list-group-horizontal-xl{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:0.25rem;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:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#1d2f42;background-color:#c7d1db}.list-group-item-primary.list-group-item-action:hover,.list-group-item-primary.list-group-item-action:focus{color:#1d2f42;background-color:#b7c4d1}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#1d2f42;border-color:#1d2f42}.list-group-item-secondary{color:#232323;background-color:#cbcbcb}.list-group-item-secondary.list-group-item-action:hover,.list-group-item-secondary.list-group-item-action:focus{color:#232323;background-color:#bebebe}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#232323;border-color:#232323}.list-group-item-success{color:#006249;background-color:#b8ecdf}.list-group-item-success.list-group-item-action:hover,.list-group-item-success.list-group-item-action:focus{color:#006249;background-color:#a4e7d6}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#006249;border-color:#006249}.list-group-item-info{color:#1b4f72;background-color:#c6e2f5}.list-group-item-info.list-group-item-action:hover,.list-group-item-info.list-group-item-action:focus{color:#1b4f72;background-color:#b0d7f1}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#1b4f72;border-color:#1b4f72}.list-group-item-warning{color:#7e5109;background-color:#fce3bd}.list-group-item-warning.list-group-item-action:hover,.list-group-item-warning.list-group-item-action:focus{color:#7e5109;background-color:#fbd9a5}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#7e5109;border-color:#7e5109}.list-group-item-danger{color:#78281f;background-color:#f8cdc8}.list-group-item-danger.list-group-item-action:hover,.list-group-item-danger.list-group-item-action:focus{color:#78281f;background-color:#f5b8b1}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#78281f;border-color:#78281f}.list-group-item-light{color:#5a5e62;background-color:#e8eaed}.list-group-item-light.list-group-item-action:hover,.list-group-item-light.list-group-item-action:focus{color:#5a5e62;background-color:#dadde2}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#5a5e62;border-color:#5a5e62}.list-group-item-dark{color:#191919;background-color:#c5c5c5}.list-group-item-dark.list-group-item-action:hover,.list-group-item-dark.list-group-item-action:focus{color:#191919;background-color:#b8b8b8}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#191919;border-color:#191919}.close{float:right;font-size:1.40625rem;font-weight:700;line-height:1;color:#fff;text-shadow:none;opacity:.5}.close:hover{color:#fff;text-decoration:none}.close:not(:disabled):not(.disabled):hover,.close:not(:disabled):not(.disabled):focus{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:0.875rem;background-color:#444;background-clip:padding-box;border:1px solid rgba(0,0,0,0.1);-webkit-box-shadow:0 0.25rem 0.75rem rgba(0,0,0,0.1);box-shadow:0 0.25rem 0.75rem rgba(0,0,0,0.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:0.25rem}.toast:not(:last-child){margin-bottom:0.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0.25rem 0.75rem;color:#888;background-color:#303030;background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,0.05)}.toast-body{padding:0.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:0.5rem;pointer-events:none}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform 0.3s ease-out;transition:-webkit-transform 0.3s ease-out;transition:transform 0.3s ease-out;transition:transform 0.3s ease-out, -webkit-transform 0.3s ease-out;-webkit-transform:translate(0, -50px);transform:translate(0, -50px)}@media (prefers-reduced-motion: reduce){.modal.fade .modal-dialog{-webkit-transition:none;transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-webkit-box;display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-header,.modal-dialog-scrollable .modal-footer{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#303030;background-clip:padding-box;border:1px solid #444;border-radius:0.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:0.5}.modal-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #444;border-top-left-radius:calc(0.3rem - 1px);border-top-right-radius:calc(0.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding:0.75rem;border-top:1px solid #444;border-bottom-right-radius:calc(0.3rem - 1px);border-bottom-left-radius:calc(0.3rem - 1px)}.modal-footer>*{margin:0.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width: 576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width: 992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width: 1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:"Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";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;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.8203125rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:0.9}.tooltip .arrow{position:absolute;display:block;width:0.8rem;height:0.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top,.bs-tooltip-auto[x-placement^="top"]{padding:0.4rem 0}.bs-tooltip-top .arrow,.bs-tooltip-auto[x-placement^="top"] .arrow{bottom:0}.bs-tooltip-top .arrow::before,.bs-tooltip-auto[x-placement^="top"] .arrow::before{top:0;border-width:0.4rem 0.4rem 0;border-top-color:#000}.bs-tooltip-right,.bs-tooltip-auto[x-placement^="right"]{padding:0 0.4rem}.bs-tooltip-right .arrow,.bs-tooltip-auto[x-placement^="right"] .arrow{left:0;width:0.4rem;height:0.8rem}.bs-tooltip-right .arrow::before,.bs-tooltip-auto[x-placement^="right"] .arrow::before{right:0;border-width:0.4rem 0.4rem 0.4rem 0;border-right-color:#000}.bs-tooltip-bottom,.bs-tooltip-auto[x-placement^="bottom"]{padding:0.4rem 0}.bs-tooltip-bottom .arrow,.bs-tooltip-auto[x-placement^="bottom"] .arrow{top:0}.bs-tooltip-bottom .arrow::before,.bs-tooltip-auto[x-placement^="bottom"] .arrow::before{bottom:0;border-width:0 0.4rem 0.4rem;border-bottom-color:#000}.bs-tooltip-left,.bs-tooltip-auto[x-placement^="left"]{padding:0 0.4rem}.bs-tooltip-left .arrow,.bs-tooltip-auto[x-placement^="left"] .arrow{right:0;width:0.4rem;height:0.8rem}.bs-tooltip-left .arrow::before,.bs-tooltip-auto[x-placement^="left"] .arrow::before{left:0;border-width:0.4rem 0 0.4rem 0.4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:0.25rem 0.5rem;color:#fff;text-align:center;background-color:#000;border-radius:0.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:"Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";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;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.8203125rem;word-wrap:break-word;background-color:#303030;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:0.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:0.5rem;margin:0 0.3rem}.popover .arrow::before,.popover .arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-top,.bs-popover-auto[x-placement^="top"]{margin-bottom:0.5rem}.bs-popover-top>.arrow,.bs-popover-auto[x-placement^="top"]>.arrow{bottom:calc(-0.5rem - 1px)}.bs-popover-top>.arrow::before,.bs-popover-auto[x-placement^="top"]>.arrow::before{bottom:0;border-width:0.5rem 0.5rem 0;border-top-color:rgba(0,0,0,0.25)}.bs-popover-top>.arrow::after,.bs-popover-auto[x-placement^="top"]>.arrow::after{bottom:1px;border-width:0.5rem 0.5rem 0;border-top-color:#303030}.bs-popover-right,.bs-popover-auto[x-placement^="right"]{margin-left:0.5rem}.bs-popover-right>.arrow,.bs-popover-auto[x-placement^="right"]>.arrow{left:calc(-0.5rem - 1px);width:0.5rem;height:1rem;margin:0.3rem 0}.bs-popover-right>.arrow::before,.bs-popover-auto[x-placement^="right"]>.arrow::before{left:0;border-width:0.5rem 0.5rem 0.5rem 0;border-right-color:rgba(0,0,0,0.25)}.bs-popover-right>.arrow::after,.bs-popover-auto[x-placement^="right"]>.arrow::after{left:1px;border-width:0.5rem 0.5rem 0.5rem 0;border-right-color:#303030}.bs-popover-bottom,.bs-popover-auto[x-placement^="bottom"]{margin-top:0.5rem}.bs-popover-bottom>.arrow,.bs-popover-auto[x-placement^="bottom"]>.arrow{top:calc(-0.5rem - 1px)}.bs-popover-bottom>.arrow::before,.bs-popover-auto[x-placement^="bottom"]>.arrow::before{top:0;border-width:0 0.5rem 0.5rem 0.5rem;border-bottom-color:rgba(0,0,0,0.25)}.bs-popover-bottom>.arrow::after,.bs-popover-auto[x-placement^="bottom"]>.arrow::after{top:1px;border-width:0 0.5rem 0.5rem 0.5rem;border-bottom-color:#303030}.bs-popover-bottom .popover-header::before,.bs-popover-auto[x-placement^="bottom"] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-0.5rem;content:"";border-bottom:1px solid #444}.bs-popover-left,.bs-popover-auto[x-placement^="left"]{margin-right:0.5rem}.bs-popover-left>.arrow,.bs-popover-auto[x-placement^="left"]>.arrow{right:calc(-0.5rem - 1px);width:0.5rem;height:1rem;margin:0.3rem 0}.bs-popover-left>.arrow::before,.bs-popover-auto[x-placement^="left"]>.arrow::before{right:0;border-width:0.5rem 0 0.5rem 0.5rem;border-left-color:rgba(0,0,0,0.25)}.bs-popover-left>.arrow::after,.bs-popover-auto[x-placement^="left"]>.arrow::after{right:1px;border-width:0.5rem 0 0.5rem 0.5rem;border-left-color:#303030}.popover-header{padding:0.5rem 0.75rem;margin-bottom:0;font-size:0.9375rem;background-color:#444;border-bottom:1px solid #373737;border-top-left-radius:calc(0.3rem - 1px);border-top-right-radius:calc(0.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:0.5rem 0.75rem;color:#fff}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;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;-webkit-transition:-webkit-transform 0.6s ease-in-out;transition:-webkit-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out}@media (prefers-reduced-motion: reduce){.carousel-item{-webkit-transition:none;transition:none}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block}.carousel-item-next:not(.carousel-item-left),.active.carousel-item-right{-webkit-transform:translateX(100%);transform:translateX(100%)}.carousel-item-prev:not(.carousel-item-right),.active.carousel-item-left{-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;-webkit-transition-property:opacity;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;-webkit-transition:opacity 0s 0.6s;transition:opacity 0s 0.6s}@media (prefers-reduced-motion: reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{-webkit-transition:none;transition:none}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:0.5;-webkit-transition:opacity 0.15s ease;transition:opacity 0.15s ease}@media (prefers-reduced-motion: reduce){.carousel-control-prev,.carousel-control-next{-webkit-transition:none;transition:none}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:0.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50% / 100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{-webkit-box-sizing:content-box;box-sizing:content-box;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;-webkit-transition:opacity 0.6s ease;transition:opacity 0.6s ease}@media (prefers-reduced-motion: reduce){.carousel-indicators li{-webkit-transition:none;transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:0.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:0.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.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}.bg-primary{background-color:#375a7f !important}a.bg-primary:hover,a.bg-primary:focus,button.bg-primary:hover,button.bg-primary:focus{background-color:#28415b !important}.bg-secondary{background-color:#444 !important}a.bg-secondary:hover,a.bg-secondary:focus,button.bg-secondary:hover,button.bg-secondary:focus{background-color:#2b2a2a !important}.bg-success{background-color:#00bc8c !important}a.bg-success:hover,a.bg-success:focus,button.bg-success:hover,button.bg-success:focus{background-color:#008966 !important}.bg-info{background-color:#3498DB !important}a.bg-info:hover,a.bg-info:focus,button.bg-info:hover,button.bg-info:focus{background-color:#217dbb !important}.bg-warning{background-color:#F39C12 !important}a.bg-warning:hover,a.bg-warning:focus,button.bg-warning:hover,button.bg-warning:focus{background-color:#c87f0a !important}.bg-danger{background-color:#E74C3C !important}a.bg-danger:hover,a.bg-danger:focus,button.bg-danger:hover,button.bg-danger:focus{background-color:#d62c1a !important}.bg-light{background-color:#adb5bd !important}a.bg-light:hover,a.bg-light:focus,button.bg-light:hover,button.bg-light:focus{background-color:#919ca6 !important}.bg-dark{background-color:#303030 !important}a.bg-dark:hover,a.bg-dark:focus,button.bg-dark:hover,button.bg-dark:focus{background-color:#171616 !important}.bg-white{background-color:#fff !important}.bg-transparent{background-color:transparent !important}.border{border:1px solid #dee2e6 !important}.border-top{border-top:1px solid #dee2e6 !important}.border-right{border-right:1px solid #dee2e6 !important}.border-bottom{border-bottom:1px solid #dee2e6 !important}.border-left{border-left:1px solid #dee2e6 !important}.border-0{border:0 !important}.border-top-0{border-top:0 !important}.border-right-0{border-right:0 !important}.border-bottom-0{border-bottom:0 !important}.border-left-0{border-left:0 !important}.border-primary{border-color:#375a7f !important}.border-secondary{border-color:#444 !important}.border-success{border-color:#00bc8c !important}.border-info{border-color:#3498DB !important}.border-warning{border-color:#F39C12 !important}.border-danger{border-color:#E74C3C !important}.border-light{border-color:#adb5bd !important}.border-dark{border-color:#303030 !important}.border-white{border-color:#fff !important}.rounded-sm{border-radius:0.2rem !important}.rounded{border-radius:0.25rem !important}.rounded-top{border-top-left-radius:0.25rem !important;border-top-right-radius:0.25rem !important}.rounded-right{border-top-right-radius:0.25rem !important;border-bottom-right-radius:0.25rem !important}.rounded-bottom{border-bottom-right-radius:0.25rem !important;border-bottom-left-radius:0.25rem !important}.rounded-left{border-top-left-radius:0.25rem !important;border-bottom-left-radius:0.25rem !important}.rounded-lg{border-radius:0.3rem !important}.rounded-circle{border-radius:50% !important}.rounded-pill{border-radius:50rem !important}.rounded-0{border-radius:0 !important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none !important}.d-inline{display:inline !important}.d-inline-block{display:inline-block !important}.d-block{display:block !important}.d-table{display:table !important}.d-table-row{display:table-row !important}.d-table-cell{display:table-cell !important}.d-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}@media (min-width: 576px){.d-sm-none{display:none !important}.d-sm-inline{display:inline !important}.d-sm-inline-block{display:inline-block !important}.d-sm-block{display:block !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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-sm-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media (min-width: 768px){.d-md-none{display:none !important}.d-md-inline{display:inline !important}.d-md-inline-block{display:inline-block !important}.d-md-block{display:block !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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-md-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media (min-width: 992px){.d-lg-none{display:none !important}.d-lg-inline{display:inline !important}.d-lg-inline-block{display:inline-block !important}.d-lg-block{display:block !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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-lg-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media (min-width: 1200px){.d-xl-none{display:none !important}.d-xl-inline{display:inline !important}.d-xl-inline-block{display:inline-block !important}.d-xl-block{display:block !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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-xl-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media print{.d-print-none{display:none !important}.d-print-inline{display:inline !important}.d-print-inline-block{display:inline-block !important}.d-print-block{display:block !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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-print-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.8571428571%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}@media (min-width: 576px){.flex-sm-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-sm-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-sm-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-sm-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-sm-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-sm-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-sm-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-sm-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-sm-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-sm-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-sm-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-sm-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-sm-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-sm-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-sm-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-sm-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-sm-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-sm-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-sm-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-sm-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-sm-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-sm-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-sm-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-sm-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-sm-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-sm-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-sm-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-sm-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-sm-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-sm-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-sm-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-sm-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-sm-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}@media (min-width: 768px){.flex-md-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-md-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-md-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-md-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-md-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-md-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-md-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-md-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-md-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-md-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-md-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-md-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-md-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-md-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-md-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-md-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-md-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-md-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-md-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-md-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-md-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-md-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-md-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-md-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-md-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-md-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-md-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-md-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-md-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-md-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-md-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-md-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-md-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}@media (min-width: 992px){.flex-lg-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-lg-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-lg-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-lg-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-lg-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-lg-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-lg-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-lg-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-lg-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-lg-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-lg-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-lg-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-lg-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-lg-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-lg-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-lg-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-lg-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-lg-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-lg-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-lg-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-lg-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-lg-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-lg-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-lg-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-lg-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-lg-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-lg-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-lg-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-lg-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-lg-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-lg-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-lg-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-lg-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}@media (min-width: 1200px){.flex-xl-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-xl-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-xl-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-xl-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-xl-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-xl-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-xl-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-xl-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-xl-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-xl-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-xl-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-xl-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-xl-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-xl-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-xl-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-xl-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-xl-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-xl-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-xl-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-xl-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-xl-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-xl-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-xl-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-xl-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-xl-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-xl-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-xl-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-xl-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-xl-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-xl-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-xl-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-xl-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-xl-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}.float-left{float:left !important}.float-right{float:right !important}.float-none{float:none !important}@media (min-width: 576px){.float-sm-left{float:left !important}.float-sm-right{float:right !important}.float-sm-none{float:none !important}}@media (min-width: 768px){.float-md-left{float:left !important}.float-md-right{float:right !important}.float-md-none{float:none !important}}@media (min-width: 992px){.float-lg-left{float:left !important}.float-lg-right{float:right !important}.float-lg-none{float:none !important}}@media (min-width: 1200px){.float-xl-left{float:left !important}.float-xl-right{float:right !important}.float-xl-none{float:none !important}}.user-select-all{-webkit-user-select:all !important;-moz-user-select:all !important;-ms-user-select:all !important;user-select:all !important}.user-select-auto{-webkit-user-select:auto !important;-moz-user-select:auto !important;-ms-user-select:auto !important;user-select:auto !important}.user-select-none{-webkit-user-select:none !important;-moz-user-select:none !important;-ms-user-select:none !important;user-select:none !important}.overflow-auto{overflow:auto !important}.overflow-hidden{overflow:hidden !important}.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}.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}@supports (position: -webkit-sticky) or (position: sticky){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{-webkit-box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.075) !important;box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.075) !important}.shadow{-webkit-box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important;box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important}.shadow-lg{-webkit-box-shadow:0 1rem 3rem rgba(0,0,0,0.175) !important;box-shadow:0 1rem 3rem rgba(0,0,0,0.175) !important}.shadow-none{-webkit-box-shadow:none !important;box-shadow:none !important}.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}.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}.mw-100{max-width:100% !important}.mh-100{max-height:100% !important}.min-vw-100{min-width:100vw !important}.min-vh-100{min-height:100vh !important}.vw-100{width:100vw !important}.vh-100{height:100vh !important}.m-0{margin:0 !important}.mt-0,.my-0{margin-top:0 !important}.mr-0,.mx-0{margin-right:0 !important}.mb-0,.my-0{margin-bottom:0 !important}.ml-0,.mx-0{margin-left:0 !important}.m-1{margin:0.25rem !important}.mt-1,.my-1{margin-top:0.25rem !important}.mr-1,.mx-1{margin-right:0.25rem !important}.mb-1,.my-1{margin-bottom:0.25rem !important}.ml-1,.mx-1{margin-left:0.25rem !important}.m-2{margin:0.5rem !important}.mt-2,.my-2{margin-top:0.5rem !important}.mr-2,.mx-2{margin-right:0.5rem !important}.mb-2,.my-2{margin-bottom:0.5rem !important}.ml-2,.mx-2{margin-left:0.5rem !important}.m-3{margin:1rem !important}.mt-3,.my-3{margin-top:1rem !important}.mr-3,.mx-3{margin-right:1rem !important}.mb-3,.my-3{margin-bottom:1rem !important}.ml-3,.mx-3{margin-left:1rem !important}.m-4{margin:1.5rem !important}.mt-4,.my-4{margin-top:1.5rem !important}.mr-4,.mx-4{margin-right:1.5rem !important}.mb-4,.my-4{margin-bottom:1.5rem !important}.ml-4,.mx-4{margin-left:1.5rem !important}.m-5{margin:3rem !important}.mt-5,.my-5{margin-top:3rem !important}.mr-5,.mx-5{margin-right:3rem !important}.mb-5,.my-5{margin-bottom:3rem !important}.ml-5,.mx-5{margin-left:3rem !important}.p-0{padding:0 !important}.pt-0,.py-0{padding-top:0 !important}.pr-0,.px-0{padding-right:0 !important}.pb-0,.py-0{padding-bottom:0 !important}.pl-0,.px-0{padding-left:0 !important}.p-1{padding:0.25rem !important}.pt-1,.py-1{padding-top:0.25rem !important}.pr-1,.px-1{padding-right:0.25rem !important}.pb-1,.py-1{padding-bottom:0.25rem !important}.pl-1,.px-1{padding-left:0.25rem !important}.p-2{padding:0.5rem !important}.pt-2,.py-2{padding-top:0.5rem !important}.pr-2,.px-2{padding-right:0.5rem !important}.pb-2,.py-2{padding-bottom:0.5rem !important}.pl-2,.px-2{padding-left:0.5rem !important}.p-3{padding:1rem !important}.pt-3,.py-3{padding-top:1rem !important}.pr-3,.px-3{padding-right:1rem !important}.pb-3,.py-3{padding-bottom:1rem !important}.pl-3,.px-3{padding-left:1rem !important}.p-4{padding:1.5rem !important}.pt-4,.py-4{padding-top:1.5rem !important}.pr-4,.px-4{padding-right:1.5rem !important}.pb-4,.py-4{padding-bottom:1.5rem !important}.pl-4,.px-4{padding-left:1.5rem !important}.p-5{padding:3rem !important}.pt-5,.py-5{padding-top:3rem !important}.pr-5,.px-5{padding-right:3rem !important}.pb-5,.py-5{padding-bottom:3rem !important}.pl-5,.px-5{padding-left:3rem !important}.m-n1{margin:-0.25rem !important}.mt-n1,.my-n1{margin-top:-0.25rem !important}.mr-n1,.mx-n1{margin-right:-0.25rem !important}.mb-n1,.my-n1{margin-bottom:-0.25rem !important}.ml-n1,.mx-n1{margin-left:-0.25rem !important}.m-n2{margin:-0.5rem !important}.mt-n2,.my-n2{margin-top:-0.5rem !important}.mr-n2,.mx-n2{margin-right:-0.5rem !important}.mb-n2,.my-n2{margin-bottom:-0.5rem !important}.ml-n2,.mx-n2{margin-left:-0.5rem !important}.m-n3{margin:-1rem !important}.mt-n3,.my-n3{margin-top:-1rem !important}.mr-n3,.mx-n3{margin-right:-1rem !important}.mb-n3,.my-n3{margin-bottom:-1rem !important}.ml-n3,.mx-n3{margin-left:-1rem !important}.m-n4{margin:-1.5rem !important}.mt-n4,.my-n4{margin-top:-1.5rem !important}.mr-n4,.mx-n4{margin-right:-1.5rem !important}.mb-n4,.my-n4{margin-bottom:-1.5rem !important}.ml-n4,.mx-n4{margin-left:-1.5rem !important}.m-n5{margin:-3rem !important}.mt-n5,.my-n5{margin-top:-3rem !important}.mr-n5,.mx-n5{margin-right:-3rem !important}.mb-n5,.my-n5{margin-bottom:-3rem !important}.ml-n5,.mx-n5{margin-left:-3rem !important}.m-auto{margin:auto !important}.mt-auto,.my-auto{margin-top:auto !important}.mr-auto,.mx-auto{margin-right:auto !important}.mb-auto,.my-auto{margin-bottom:auto !important}.ml-auto,.mx-auto{margin-left:auto !important}@media (min-width: 576px){.m-sm-0{margin:0 !important}.mt-sm-0,.my-sm-0{margin-top:0 !important}.mr-sm-0,.mx-sm-0{margin-right:0 !important}.mb-sm-0,.my-sm-0{margin-bottom:0 !important}.ml-sm-0,.mx-sm-0{margin-left:0 !important}.m-sm-1{margin:0.25rem !important}.mt-sm-1,.my-sm-1{margin-top:0.25rem !important}.mr-sm-1,.mx-sm-1{margin-right:0.25rem !important}.mb-sm-1,.my-sm-1{margin-bottom:0.25rem !important}.ml-sm-1,.mx-sm-1{margin-left:0.25rem !important}.m-sm-2{margin:0.5rem !important}.mt-sm-2,.my-sm-2{margin-top:0.5rem !important}.mr-sm-2,.mx-sm-2{margin-right:0.5rem !important}.mb-sm-2,.my-sm-2{margin-bottom:0.5rem !important}.ml-sm-2,.mx-sm-2{margin-left:0.5rem !important}.m-sm-3{margin:1rem !important}.mt-sm-3,.my-sm-3{margin-top:1rem !important}.mr-sm-3,.mx-sm-3{margin-right:1rem !important}.mb-sm-3,.my-sm-3{margin-bottom:1rem !important}.ml-sm-3,.mx-sm-3{margin-left:1rem !important}.m-sm-4{margin:1.5rem !important}.mt-sm-4,.my-sm-4{margin-top:1.5rem !important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem !important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem !important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem !important}.m-sm-5{margin:3rem !important}.mt-sm-5,.my-sm-5{margin-top:3rem !important}.mr-sm-5,.mx-sm-5{margin-right:3rem !important}.mb-sm-5,.my-sm-5{margin-bottom:3rem !important}.ml-sm-5,.mx-sm-5{margin-left:3rem !important}.p-sm-0{padding:0 !important}.pt-sm-0,.py-sm-0{padding-top:0 !important}.pr-sm-0,.px-sm-0{padding-right:0 !important}.pb-sm-0,.py-sm-0{padding-bottom:0 !important}.pl-sm-0,.px-sm-0{padding-left:0 !important}.p-sm-1{padding:0.25rem !important}.pt-sm-1,.py-sm-1{padding-top:0.25rem !important}.pr-sm-1,.px-sm-1{padding-right:0.25rem !important}.pb-sm-1,.py-sm-1{padding-bottom:0.25rem !important}.pl-sm-1,.px-sm-1{padding-left:0.25rem !important}.p-sm-2{padding:0.5rem !important}.pt-sm-2,.py-sm-2{padding-top:0.5rem !important}.pr-sm-2,.px-sm-2{padding-right:0.5rem !important}.pb-sm-2,.py-sm-2{padding-bottom:0.5rem !important}.pl-sm-2,.px-sm-2{padding-left:0.5rem !important}.p-sm-3{padding:1rem !important}.pt-sm-3,.py-sm-3{padding-top:1rem !important}.pr-sm-3,.px-sm-3{padding-right:1rem !important}.pb-sm-3,.py-sm-3{padding-bottom:1rem !important}.pl-sm-3,.px-sm-3{padding-left:1rem !important}.p-sm-4{padding:1.5rem !important}.pt-sm-4,.py-sm-4{padding-top:1.5rem !important}.pr-sm-4,.px-sm-4{padding-right:1.5rem !important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem !important}.pl-sm-4,.px-sm-4{padding-left:1.5rem !important}.p-sm-5{padding:3rem !important}.pt-sm-5,.py-sm-5{padding-top:3rem !important}.pr-sm-5,.px-sm-5{padding-right:3rem !important}.pb-sm-5,.py-sm-5{padding-bottom:3rem !important}.pl-sm-5,.px-sm-5{padding-left:3rem !important}.m-sm-n1{margin:-0.25rem !important}.mt-sm-n1,.my-sm-n1{margin-top:-0.25rem !important}.mr-sm-n1,.mx-sm-n1{margin-right:-0.25rem !important}.mb-sm-n1,.my-sm-n1{margin-bottom:-0.25rem !important}.ml-sm-n1,.mx-sm-n1{margin-left:-0.25rem !important}.m-sm-n2{margin:-0.5rem !important}.mt-sm-n2,.my-sm-n2{margin-top:-0.5rem !important}.mr-sm-n2,.mx-sm-n2{margin-right:-0.5rem !important}.mb-sm-n2,.my-sm-n2{margin-bottom:-0.5rem !important}.ml-sm-n2,.mx-sm-n2{margin-left:-0.5rem !important}.m-sm-n3{margin:-1rem !important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem !important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem !important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem !important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem !important}.m-sm-n4{margin:-1.5rem !important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem !important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem !important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem !important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem !important}.m-sm-n5{margin:-3rem !important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem !important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem !important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem !important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem !important}.m-sm-auto{margin:auto !important}.mt-sm-auto,.my-sm-auto{margin-top:auto !important}.mr-sm-auto,.mx-sm-auto{margin-right:auto !important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto !important}.ml-sm-auto,.mx-sm-auto{margin-left:auto !important}}@media (min-width: 768px){.m-md-0{margin:0 !important}.mt-md-0,.my-md-0{margin-top:0 !important}.mr-md-0,.mx-md-0{margin-right:0 !important}.mb-md-0,.my-md-0{margin-bottom:0 !important}.ml-md-0,.mx-md-0{margin-left:0 !important}.m-md-1{margin:0.25rem !important}.mt-md-1,.my-md-1{margin-top:0.25rem !important}.mr-md-1,.mx-md-1{margin-right:0.25rem !important}.mb-md-1,.my-md-1{margin-bottom:0.25rem !important}.ml-md-1,.mx-md-1{margin-left:0.25rem !important}.m-md-2{margin:0.5rem !important}.mt-md-2,.my-md-2{margin-top:0.5rem !important}.mr-md-2,.mx-md-2{margin-right:0.5rem !important}.mb-md-2,.my-md-2{margin-bottom:0.5rem !important}.ml-md-2,.mx-md-2{margin-left:0.5rem !important}.m-md-3{margin:1rem !important}.mt-md-3,.my-md-3{margin-top:1rem !important}.mr-md-3,.mx-md-3{margin-right:1rem !important}.mb-md-3,.my-md-3{margin-bottom:1rem !important}.ml-md-3,.mx-md-3{margin-left:1rem !important}.m-md-4{margin:1.5rem !important}.mt-md-4,.my-md-4{margin-top:1.5rem !important}.mr-md-4,.mx-md-4{margin-right:1.5rem !important}.mb-md-4,.my-md-4{margin-bottom:1.5rem !important}.ml-md-4,.mx-md-4{margin-left:1.5rem !important}.m-md-5{margin:3rem !important}.mt-md-5,.my-md-5{margin-top:3rem !important}.mr-md-5,.mx-md-5{margin-right:3rem !important}.mb-md-5,.my-md-5{margin-bottom:3rem !important}.ml-md-5,.mx-md-5{margin-left:3rem !important}.p-md-0{padding:0 !important}.pt-md-0,.py-md-0{padding-top:0 !important}.pr-md-0,.px-md-0{padding-right:0 !important}.pb-md-0,.py-md-0{padding-bottom:0 !important}.pl-md-0,.px-md-0{padding-left:0 !important}.p-md-1{padding:0.25rem !important}.pt-md-1,.py-md-1{padding-top:0.25rem !important}.pr-md-1,.px-md-1{padding-right:0.25rem !important}.pb-md-1,.py-md-1{padding-bottom:0.25rem !important}.pl-md-1,.px-md-1{padding-left:0.25rem !important}.p-md-2{padding:0.5rem !important}.pt-md-2,.py-md-2{padding-top:0.5rem !important}.pr-md-2,.px-md-2{padding-right:0.5rem !important}.pb-md-2,.py-md-2{padding-bottom:0.5rem !important}.pl-md-2,.px-md-2{padding-left:0.5rem !important}.p-md-3{padding:1rem !important}.pt-md-3,.py-md-3{padding-top:1rem !important}.pr-md-3,.px-md-3{padding-right:1rem !important}.pb-md-3,.py-md-3{padding-bottom:1rem !important}.pl-md-3,.px-md-3{padding-left:1rem !important}.p-md-4{padding:1.5rem !important}.pt-md-4,.py-md-4{padding-top:1.5rem !important}.pr-md-4,.px-md-4{padding-right:1.5rem !important}.pb-md-4,.py-md-4{padding-bottom:1.5rem !important}.pl-md-4,.px-md-4{padding-left:1.5rem !important}.p-md-5{padding:3rem !important}.pt-md-5,.py-md-5{padding-top:3rem !important}.pr-md-5,.px-md-5{padding-right:3rem !important}.pb-md-5,.py-md-5{padding-bottom:3rem !important}.pl-md-5,.px-md-5{padding-left:3rem !important}.m-md-n1{margin:-0.25rem !important}.mt-md-n1,.my-md-n1{margin-top:-0.25rem !important}.mr-md-n1,.mx-md-n1{margin-right:-0.25rem !important}.mb-md-n1,.my-md-n1{margin-bottom:-0.25rem !important}.ml-md-n1,.mx-md-n1{margin-left:-0.25rem !important}.m-md-n2{margin:-0.5rem !important}.mt-md-n2,.my-md-n2{margin-top:-0.5rem !important}.mr-md-n2,.mx-md-n2{margin-right:-0.5rem !important}.mb-md-n2,.my-md-n2{margin-bottom:-0.5rem !important}.ml-md-n2,.mx-md-n2{margin-left:-0.5rem !important}.m-md-n3{margin:-1rem !important}.mt-md-n3,.my-md-n3{margin-top:-1rem !important}.mr-md-n3,.mx-md-n3{margin-right:-1rem !important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem !important}.ml-md-n3,.mx-md-n3{margin-left:-1rem !important}.m-md-n4{margin:-1.5rem !important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem !important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem !important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem !important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem !important}.m-md-n5{margin:-3rem !important}.mt-md-n5,.my-md-n5{margin-top:-3rem !important}.mr-md-n5,.mx-md-n5{margin-right:-3rem !important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem !important}.ml-md-n5,.mx-md-n5{margin-left:-3rem !important}.m-md-auto{margin:auto !important}.mt-md-auto,.my-md-auto{margin-top:auto !important}.mr-md-auto,.mx-md-auto{margin-right:auto !important}.mb-md-auto,.my-md-auto{margin-bottom:auto !important}.ml-md-auto,.mx-md-auto{margin-left:auto !important}}@media (min-width: 992px){.m-lg-0{margin:0 !important}.mt-lg-0,.my-lg-0{margin-top:0 !important}.mr-lg-0,.mx-lg-0{margin-right:0 !important}.mb-lg-0,.my-lg-0{margin-bottom:0 !important}.ml-lg-0,.mx-lg-0{margin-left:0 !important}.m-lg-1{margin:0.25rem !important}.mt-lg-1,.my-lg-1{margin-top:0.25rem !important}.mr-lg-1,.mx-lg-1{margin-right:0.25rem !important}.mb-lg-1,.my-lg-1{margin-bottom:0.25rem !important}.ml-lg-1,.mx-lg-1{margin-left:0.25rem !important}.m-lg-2{margin:0.5rem !important}.mt-lg-2,.my-lg-2{margin-top:0.5rem !important}.mr-lg-2,.mx-lg-2{margin-right:0.5rem !important}.mb-lg-2,.my-lg-2{margin-bottom:0.5rem !important}.ml-lg-2,.mx-lg-2{margin-left:0.5rem !important}.m-lg-3{margin:1rem !important}.mt-lg-3,.my-lg-3{margin-top:1rem !important}.mr-lg-3,.mx-lg-3{margin-right:1rem !important}.mb-lg-3,.my-lg-3{margin-bottom:1rem !important}.ml-lg-3,.mx-lg-3{margin-left:1rem !important}.m-lg-4{margin:1.5rem !important}.mt-lg-4,.my-lg-4{margin-top:1.5rem !important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem !important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem !important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem !important}.m-lg-5{margin:3rem !important}.mt-lg-5,.my-lg-5{margin-top:3rem !important}.mr-lg-5,.mx-lg-5{margin-right:3rem !important}.mb-lg-5,.my-lg-5{margin-bottom:3rem !important}.ml-lg-5,.mx-lg-5{margin-left:3rem !important}.p-lg-0{padding:0 !important}.pt-lg-0,.py-lg-0{padding-top:0 !important}.pr-lg-0,.px-lg-0{padding-right:0 !important}.pb-lg-0,.py-lg-0{padding-bottom:0 !important}.pl-lg-0,.px-lg-0{padding-left:0 !important}.p-lg-1{padding:0.25rem !important}.pt-lg-1,.py-lg-1{padding-top:0.25rem !important}.pr-lg-1,.px-lg-1{padding-right:0.25rem !important}.pb-lg-1,.py-lg-1{padding-bottom:0.25rem !important}.pl-lg-1,.px-lg-1{padding-left:0.25rem !important}.p-lg-2{padding:0.5rem !important}.pt-lg-2,.py-lg-2{padding-top:0.5rem !important}.pr-lg-2,.px-lg-2{padding-right:0.5rem !important}.pb-lg-2,.py-lg-2{padding-bottom:0.5rem !important}.pl-lg-2,.px-lg-2{padding-left:0.5rem !important}.p-lg-3{padding:1rem !important}.pt-lg-3,.py-lg-3{padding-top:1rem !important}.pr-lg-3,.px-lg-3{padding-right:1rem !important}.pb-lg-3,.py-lg-3{padding-bottom:1rem !important}.pl-lg-3,.px-lg-3{padding-left:1rem !important}.p-lg-4{padding:1.5rem !important}.pt-lg-4,.py-lg-4{padding-top:1.5rem !important}.pr-lg-4,.px-lg-4{padding-right:1.5rem !important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem !important}.pl-lg-4,.px-lg-4{padding-left:1.5rem !important}.p-lg-5{padding:3rem !important}.pt-lg-5,.py-lg-5{padding-top:3rem !important}.pr-lg-5,.px-lg-5{padding-right:3rem !important}.pb-lg-5,.py-lg-5{padding-bottom:3rem !important}.pl-lg-5,.px-lg-5{padding-left:3rem !important}.m-lg-n1{margin:-0.25rem !important}.mt-lg-n1,.my-lg-n1{margin-top:-0.25rem !important}.mr-lg-n1,.mx-lg-n1{margin-right:-0.25rem !important}.mb-lg-n1,.my-lg-n1{margin-bottom:-0.25rem !important}.ml-lg-n1,.mx-lg-n1{margin-left:-0.25rem !important}.m-lg-n2{margin:-0.5rem !important}.mt-lg-n2,.my-lg-n2{margin-top:-0.5rem !important}.mr-lg-n2,.mx-lg-n2{margin-right:-0.5rem !important}.mb-lg-n2,.my-lg-n2{margin-bottom:-0.5rem !important}.ml-lg-n2,.mx-lg-n2{margin-left:-0.5rem !important}.m-lg-n3{margin:-1rem !important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem !important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem !important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem !important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem !important}.m-lg-n4{margin:-1.5rem !important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem !important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem !important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem !important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem !important}.m-lg-n5{margin:-3rem !important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem !important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem !important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem !important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem !important}.m-lg-auto{margin:auto !important}.mt-lg-auto,.my-lg-auto{margin-top:auto !important}.mr-lg-auto,.mx-lg-auto{margin-right:auto !important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto !important}.ml-lg-auto,.mx-lg-auto{margin-left:auto !important}}@media (min-width: 1200px){.m-xl-0{margin:0 !important}.mt-xl-0,.my-xl-0{margin-top:0 !important}.mr-xl-0,.mx-xl-0{margin-right:0 !important}.mb-xl-0,.my-xl-0{margin-bottom:0 !important}.ml-xl-0,.mx-xl-0{margin-left:0 !important}.m-xl-1{margin:0.25rem !important}.mt-xl-1,.my-xl-1{margin-top:0.25rem !important}.mr-xl-1,.mx-xl-1{margin-right:0.25rem !important}.mb-xl-1,.my-xl-1{margin-bottom:0.25rem !important}.ml-xl-1,.mx-xl-1{margin-left:0.25rem !important}.m-xl-2{margin:0.5rem !important}.mt-xl-2,.my-xl-2{margin-top:0.5rem !important}.mr-xl-2,.mx-xl-2{margin-right:0.5rem !important}.mb-xl-2,.my-xl-2{margin-bottom:0.5rem !important}.ml-xl-2,.mx-xl-2{margin-left:0.5rem !important}.m-xl-3{margin:1rem !important}.mt-xl-3,.my-xl-3{margin-top:1rem !important}.mr-xl-3,.mx-xl-3{margin-right:1rem !important}.mb-xl-3,.my-xl-3{margin-bottom:1rem !important}.ml-xl-3,.mx-xl-3{margin-left:1rem !important}.m-xl-4{margin:1.5rem !important}.mt-xl-4,.my-xl-4{margin-top:1.5rem !important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem !important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem !important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem !important}.m-xl-5{margin:3rem !important}.mt-xl-5,.my-xl-5{margin-top:3rem !important}.mr-xl-5,.mx-xl-5{margin-right:3rem !important}.mb-xl-5,.my-xl-5{margin-bottom:3rem !important}.ml-xl-5,.mx-xl-5{margin-left:3rem !important}.p-xl-0{padding:0 !important}.pt-xl-0,.py-xl-0{padding-top:0 !important}.pr-xl-0,.px-xl-0{padding-right:0 !important}.pb-xl-0,.py-xl-0{padding-bottom:0 !important}.pl-xl-0,.px-xl-0{padding-left:0 !important}.p-xl-1{padding:0.25rem !important}.pt-xl-1,.py-xl-1{padding-top:0.25rem !important}.pr-xl-1,.px-xl-1{padding-right:0.25rem !important}.pb-xl-1,.py-xl-1{padding-bottom:0.25rem !important}.pl-xl-1,.px-xl-1{padding-left:0.25rem !important}.p-xl-2{padding:0.5rem !important}.pt-xl-2,.py-xl-2{padding-top:0.5rem !important}.pr-xl-2,.px-xl-2{padding-right:0.5rem !important}.pb-xl-2,.py-xl-2{padding-bottom:0.5rem !important}.pl-xl-2,.px-xl-2{padding-left:0.5rem !important}.p-xl-3{padding:1rem !important}.pt-xl-3,.py-xl-3{padding-top:1rem !important}.pr-xl-3,.px-xl-3{padding-right:1rem !important}.pb-xl-3,.py-xl-3{padding-bottom:1rem !important}.pl-xl-3,.px-xl-3{padding-left:1rem !important}.p-xl-4{padding:1.5rem !important}.pt-xl-4,.py-xl-4{padding-top:1.5rem !important}.pr-xl-4,.px-xl-4{padding-right:1.5rem !important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem !important}.pl-xl-4,.px-xl-4{padding-left:1.5rem !important}.p-xl-5{padding:3rem !important}.pt-xl-5,.py-xl-5{padding-top:3rem !important}.pr-xl-5,.px-xl-5{padding-right:3rem !important}.pb-xl-5,.py-xl-5{padding-bottom:3rem !important}.pl-xl-5,.px-xl-5{padding-left:3rem !important}.m-xl-n1{margin:-0.25rem !important}.mt-xl-n1,.my-xl-n1{margin-top:-0.25rem !important}.mr-xl-n1,.mx-xl-n1{margin-right:-0.25rem !important}.mb-xl-n1,.my-xl-n1{margin-bottom:-0.25rem !important}.ml-xl-n1,.mx-xl-n1{margin-left:-0.25rem !important}.m-xl-n2{margin:-0.5rem !important}.mt-xl-n2,.my-xl-n2{margin-top:-0.5rem !important}.mr-xl-n2,.mx-xl-n2{margin-right:-0.5rem !important}.mb-xl-n2,.my-xl-n2{margin-bottom:-0.5rem !important}.ml-xl-n2,.mx-xl-n2{margin-left:-0.5rem !important}.m-xl-n3{margin:-1rem !important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem !important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem !important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem !important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem !important}.m-xl-n4{margin:-1.5rem !important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem !important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem !important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem !important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem !important}.m-xl-n5{margin:-3rem !important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem !important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem !important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem !important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem !important}.m-xl-auto{margin:auto !important}.mt-xl-auto,.my-xl-auto{margin-top:auto !important}.mr-xl-auto,.mx-xl-auto{margin-right:auto !important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto !important}.ml-xl-auto,.mx-xl-auto{margin-left:auto !important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important}.text-justify{text-align:justify !important}.text-wrap{white-space:normal !important}.text-nowrap{white-space:nowrap !important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left !important}.text-right{text-align:right !important}.text-center{text-align:center !important}@media (min-width: 576px){.text-sm-left{text-align:left !important}.text-sm-right{text-align:right !important}.text-sm-center{text-align:center !important}}@media (min-width: 768px){.text-md-left{text-align:left !important}.text-md-right{text-align:right !important}.text-md-center{text-align:center !important}}@media (min-width: 992px){.text-lg-left{text-align:left !important}.text-lg-right{text-align:right !important}.text-lg-center{text-align:center !important}}@media (min-width: 1200px){.text-xl-left{text-align:left !important}.text-xl-right{text-align:right !important}.text-xl-center{text-align:center !important}}.text-lowercase{text-transform:lowercase !important}.text-uppercase{text-transform:uppercase !important}.text-capitalize{text-transform:capitalize !important}.font-weight-light{font-weight:300 !important}.font-weight-lighter{font-weight:lighter !important}.font-weight-normal{font-weight:400 !important}.font-weight-bold{font-weight:700 !important}.font-weight-bolder{font-weight:bolder !important}.font-italic{font-style:italic !important}.text-white{color:#fff !important}.text-primary{color:#375a7f !important}a.text-primary:hover,a.text-primary:focus{color:#20344a !important}.text-secondary{color:#444 !important}a.text-secondary:hover,a.text-secondary:focus{color:#1e1e1e !important}.text-success{color:#00bc8c !important}a.text-success:hover,a.text-success:focus{color:#007053 !important}.text-info{color:#3498DB !important}a.text-info:hover,a.text-info:focus{color:#1d6fa5 !important}.text-warning{color:#F39C12 !important}a.text-warning:hover,a.text-warning:focus{color:#b06f09 !important}.text-danger{color:#E74C3C !important}a.text-danger:hover,a.text-danger:focus{color:#bf2718 !important}.text-light{color:#adb5bd !important}a.text-light:hover,a.text-light:focus{color:#838f9b !important}.text-dark{color:#303030 !important}a.text-dark:hover,a.text-dark:focus{color:#0a0a0a !important}.text-body{color:#fff !important}.text-muted{color:#888 !important}.text-black-50{color:rgba(0,0,0,0.5) !important}.text-white-50{color:rgba(255,255,255,0.5) !important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none !important}.text-break{word-wrap:break-word !important}.text-reset{color:inherit !important}.visible{visibility:visible !important}.invisible{visibility:hidden !important}@media print{*,*::before,*::after{text-shadow:none !important;-webkit-box-shadow:none !important;box-shadow:none !important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap !important}pre,blockquote{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px !important}.container{min-width:992px !important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #dee2e6 !important}.table-dark{color:inherit}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:#444}.table .thead-dark th{color:inherit;border-color:#444}}.blockquote-footer{color:#888}.table-primary,.table-primary>th,.table-primary>td{background-color:#375a7f}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#444}.table-light,.table-light>th,.table-light>td{background-color:#adb5bd}.table-dark,.table-dark>th,.table-dark>td{background-color:#303030}.table-success,.table-success>th,.table-success>td{background-color:#00bc8c}.table-info,.table-info>th,.table-info>td{background-color:#3498DB}.table-danger,.table-danger>th,.table-danger>td{background-color:#E74C3C}.table-warning,.table-warning>th,.table-warning>td{background-color:#F39C12}.table-active,.table-active>th,.table-active>td{background-color:rgba(0,0,0,0.075)}.table-hover .table-primary:hover,.table-hover .table-primary:hover>th,.table-hover .table-primary:hover>td{background-color:#2f4d6d}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>th,.table-hover .table-secondary:hover>td{background-color:#373737}.table-hover .table-light:hover,.table-hover .table-light:hover>th,.table-hover .table-light:hover>td{background-color:#9fa8b2}.table-hover .table-dark:hover,.table-hover .table-dark:hover>th,.table-hover .table-dark:hover>td{background-color:#232323}.table-hover .table-success:hover,.table-hover .table-success:hover>th,.table-hover .table-success:hover>td{background-color:#00a379}.table-hover .table-info:hover,.table-hover .table-info:hover>th,.table-hover .table-info:hover>td{background-color:#258cd1}.table-hover .table-danger:hover,.table-hover .table-danger:hover>th,.table-hover .table-danger:hover>td{background-color:#e43725}.table-hover .table-warning:hover,.table-hover .table-warning:hover>th,.table-hover .table-warning:hover>td{background-color:#e08e0b}.table-hover .table-active:hover,.table-hover .table-active:hover>th,.table-hover .table-active:hover>td{background-color:rgba(0,0,0,0.075)}.input-group-addon{color:#fff}.nav-tabs .nav-link,.nav-tabs .nav-link.active,.nav-tabs .nav-link.active:focus,.nav-tabs .nav-link.active:hover,.nav-tabs .nav-item.open .nav-link,.nav-tabs .nav-item.open .nav-link:focus,.nav-tabs .nav-item.open .nav-link:hover,.nav-pills .nav-link,.nav-pills .nav-link.active,.nav-pills .nav-link.active:focus,.nav-pills .nav-link.active:hover,.nav-pills .nav-item.open .nav-link,.nav-pills .nav-item.open .nav-link:focus,.nav-pills .nav-item.open .nav-link:hover{color:#fff}.breadcrumb a{color:#fff}.pagination a:hover{text-decoration:none}.close{opacity:0.4}.close:hover,.close:focus{opacity:1}.alert{border:none;color:#fff}.alert a,.alert .alert-link{color:#fff;text-decoration:underline}.alert-primary{background-color:#375a7f}.alert-secondary{background-color:#444}.alert-success{background-color:#00bc8c}.alert-info{background-color:#3498DB}.alert-warning{background-color:#F39C12}.alert-danger{background-color:#E74C3C}.alert-light{background-color:#adb5bd}.alert-dark{background-color:#303030}.list-group-item-action{color:#fff}.list-group-item-action:hover,.list-group-item-action:focus{background-color:#444;color:#fff}.list-group-item-action .list-group-item-heading{color:#fff}
diff --git a/openwebrx/htdocs/css/login.css b/openwebrx/htdocs/css/login.css
new file mode 100644
index 0000000..ccd6c02
--- /dev/null
+++ b/openwebrx/htdocs/css/login.css
@@ -0,0 +1,34 @@
+@import url("openwebrx-header.css");
+@import url("openwebrx-globals.css");
+
+body {
+ display: flex;
+ flex-direction: column;
+}
+
+.login-container {
+ flex: 1;
+ position: relative;
+}
+
+.login {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+
+ width: 500px;
+
+ padding: 20px;
+ border-radius: 10px;
+ border: 1px solid #575757;
+ box-shadow: 0 0 20px #000;
+}
+
+.login .btn {
+ width: 100%;
+}
+
+.btn-login {
+ height: 50px;
+}
\ No newline at end of file
diff --git a/openwebrx/htdocs/css/map.css b/openwebrx/htdocs/css/map.css
new file mode 100644
index 0000000..70702b9
--- /dev/null
+++ b/openwebrx/htdocs/css/map.css
@@ -0,0 +1,65 @@
+@import url("openwebrx-header.css");
+@import url("openwebrx-globals.css");
+
+body {
+ display: flex;
+ flex-direction: column;
+}
+
+.openwebrx-map {
+ flex: 1 1 auto;
+}
+
+h3 {
+ margin: 10px 0;
+ text-align: center;
+}
+
+ul {
+ margin-block-start: 5px;
+ margin-block-end: 5px;
+ padding-inline-start: 25px;
+}
+
+/* don't show the filter in it's initial position */
+.openwebrx-map-legend {
+ display: none;
+ background-color: #fff;
+ padding: 10px;
+ margin: 10px;
+ user-select: none;
+}
+
+/* show it as soon as google maps has moved it to its container */
+.openwebrx-map .openwebrx-map-legend {
+ display: block;
+}
+
+.openwebrx-map-legend ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.openwebrx-map-legend ul li {
+ cursor: pointer;
+}
+
+.openwebrx-map-legend ul li.disabled {
+ opacity: .3;
+ filter: grayscale(70%);
+}
+
+.openwebrx-map-legend li.square .illustration {
+ display: inline-block;
+ width: 30px;
+ height: 20px;
+ margin-right: 10px;
+ border-width: 2px;
+ border-style: solid;
+}
+
+.openwebrx-map-legend select {
+ background-color: #FFF;
+ border-color: #DDD;
+ padding: 5px;
+}
diff --git a/openwebrx/htdocs/css/openwebrx-globals.css b/openwebrx/htdocs/css/openwebrx-globals.css
new file mode 100644
index 0000000..5759847
--- /dev/null
+++ b/openwebrx/htdocs/css/openwebrx-globals.css
@@ -0,0 +1,7 @@
+html, body
+{
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ font-family: "DejaVu Sans", Verdana, Geneva, sans-serif;
+}
diff --git a/openwebrx/htdocs/css/openwebrx-header.css b/openwebrx/htdocs/css/openwebrx-header.css
new file mode 100644
index 0000000..4e8601b
--- /dev/null
+++ b/openwebrx/htdocs/css/openwebrx-header.css
@@ -0,0 +1,227 @@
+.webrx-top-container {
+ position: relative;
+ z-index:1000;
+ background-color: #575757;
+
+ background-image: url(../gfx/openwebrx-top-photo.jpg);
+ background-position-x: center;
+ background-position-y: top;
+ background-repeat: no-repeat;
+ background-size: cover;
+
+ overflow: hidden;
+}
+
+.openwebrx-description-container {
+ transition-property: height, opacity;
+ transition-duration: 1s;
+ transition-timing-function: ease-out;
+ opacity: 0;
+ height: 0;
+ /* originally, top-bar + description was 350px */
+ max-height: 283px;
+ overflow: hidden;
+}
+
+.openwebrx-description-container.expanded {
+ opacity: 1;
+ height: 283px;
+}
+
+.webrx-top-bar {
+ height:67px;
+
+ background: rgba(128, 128, 128, 0.15);
+ margin:0;
+ padding:0;
+ user-select: none;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ overflow: hidden;
+
+ display: flex;
+ flex-direction: row;
+}
+
+.webrx-top-bar > * {
+ flex: 0;
+}
+
+.webrx-top-container, .webrx-top-container * {
+ line-height: initial;
+ box-sizing: initial;
+}
+
+.webrx-top-logo {
+ width: 261px;
+ padding: 12px;
+ filter: drop-shadow(0 0 2.5px rgba(0, 0, 0, .9));
+ /* overwritten by media queries */
+ display: none;
+}
+
+.webrx-rx-avatar {
+ background-color: rgba(154, 154, 154, .5);
+ margin: 7px;
+
+ width: 46px;
+ height: 46px;
+ padding: 4px;
+ border-radius: 8px;
+ box-sizing: content-box;
+}
+
+.webrx-rx-texts {
+ /* minimum layout width */
+ width: 0;
+ /* will be getting wider with flex */
+ flex: 1;
+ overflow: hidden;
+ margin: auto 0;
+}
+
+.webrx-rx-texts div, .webrx-rx-texts h1 {
+ margin: 0 10px;
+ padding: 3px;
+ white-space:nowrap;
+ overflow: hidden;
+ color: #909090;
+ text-align: left;
+}
+
+.webrx-rx-title {
+ font-family: "DejaVu Sans", Verdana, Geneva, sans-serif;
+ font-size: 11pt;
+ font-weight: bold;
+}
+
+.webrx-rx-desc {
+ font-size: 10pt;
+}
+
+.openwebrx-main-buttons .button {
+ display: block;
+ width: 55px;
+ cursor:pointer;
+}
+
+.openwebrx-main-buttons .button[data-toggle-panel] {
+ /* will be enabled by javascript if the panel is present in the DOM */
+ display: none;
+}
+
+.openwebrx-main-buttons .button img,
+.openwebrx-main-buttons .button svg {
+ height: 38px;
+ filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.5));
+}
+
+.openwebrx-main-buttons a {
+ color: inherit;
+ text-decoration: inherit;
+}
+
+.openwebrx-main-buttons .button:hover {
+ background-color: rgba(255, 255, 255, 0.3);
+}
+
+.openwebrx-main-buttons .button:active {
+ background-color: rgba(255, 255, 255, 0.55);
+}
+
+
+.openwebrx-main-buttons {
+ padding: 5px 15px;
+ display: flex;
+ list-style: none;
+ margin:0;
+ color: white;
+ text-shadow: 0px 0px 4px #000000;
+ text-align: center;
+ font-size: 9pt;
+ font-weight: bold;
+}
+
+.webrx-rx-photo-title {
+ margin: 10px 15px;
+ color: white;
+ font-size: 16pt;
+ text-shadow: 1px 1px 4px #444;
+ opacity: 1;
+}
+
+.webrx-rx-photo-desc {
+ margin: 10px 15px;
+ color: white;
+ font-size: 10pt;
+ font-weight: bold;
+ text-shadow: 0px 0px 6px #444;
+ opacity: 1;
+ line-height: 1.5em;
+}
+
+.webrx-rx-photo-desc a {
+ color: #5ca8ff;
+ text-shadow: none;
+}
+
+.openwebrx-photo-trigger {
+ cursor: pointer;
+}
+
+/*
+ * Responsive stuff
+ */
+
+@media (min-width: 576px) {
+ .webrx-rx-texts {
+ display: initial;
+ }
+}
+
+@media (min-width: 768px) {
+}
+
+@media (min-width: 992px) {
+ .webrx-top-logo {
+ display: initial;
+ }
+}
+
+@media (min-width: 1200px) {
+}
+
+/*
+ * RX details arrow up/down switching
+ */
+
+.openwebrx-rx-details-arrow {
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ transform: translate(-50%, 0);
+
+ margin: 0;
+ padding: 0;
+ line-height: 0;
+ display: block;
+}
+
+.openwebrx-rx-details-arrow svg {
+ height: 12px;
+}
+
+.openwebrx-rx-details-arrow .up {
+ display: none;
+}
+
+.openwebrx-rx-details-arrow--up .down {
+ display: none;
+}
+
+.openwebrx-rx-details-arrow--up .up {
+ display: initial;
+}
\ No newline at end of file
diff --git a/openwebrx/htdocs/css/openwebrx.css b/openwebrx/htdocs/css/openwebrx.css
new file mode 100644
index 0000000..cb1e954
--- /dev/null
+++ b/openwebrx/htdocs/css/openwebrx.css
@@ -0,0 +1,1364 @@
+/*
+
+ This file is part of OpenWebRX,
+ an open-source SDR receiver software with a web UI.
+ Copyright (c) 2013-2015 by Andras Retzler
+ Copyright (c) 2019-2021 by Jakob Ketterl
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+*/
+@import url("openwebrx-header.css");
+@import url("openwebrx-globals.css");
+
+html, body {
+ overflow: hidden;
+}
+
+select
+{
+ font-family: "DejaVu Sans", Verdana, Geneva, sans-serif;
+}
+
+input
+{
+ vertical-align:middle;
+}
+
+input[type=range] {
+ -webkit-appearance: none;
+ margin: 0 0;
+ background: transparent !important;
+ --track-background: #B6B6B6;
+}
+
+input[type=range]:focus {
+ outline: none;
+}
+
+input[type=range]::-webkit-slider-runnable-track
+{
+ height: 5px;
+ cursor: pointer;
+ animate: 0.2s;
+ box-shadow: 0px 0px 0px #000000;
+ background: #B6B6B6;
+ /*border-radius: 11px;*/
+ border: 1px solid #8A8A8A;
+ background: var(--track-background);
+}
+
+input[type=range]::-webkit-slider-thumb
+{
+ box-shadow: 1px 1px 1px #828282;
+ border: 1px solid #8A8A8A;
+ height: 15px;
+ width: 15px;
+ border-radius: 10px;
+ background: #FFFFFF;
+ cursor: pointer;
+ -webkit-appearance: none;
+ margin-top: -7px;
+}
+
+input[type=range]:focus::-webkit-slider-runnable-track
+{
+ background: #B6B6B6;
+ background: var(--track-background);
+}
+
+input[type=range]::-moz-range-track
+{
+ height: 3px;
+ cursor: pointer;
+ animate: 0.2s;
+ box-shadow: 0px 0px 0px #000000;
+ background: #B6B6B6;
+ background: var(--track-background);
+ border-radius: 11px;
+ border: 1px solid #8A8A8A;
+}
+
+input[type=range]::-moz-range-thumb
+{
+ box-shadow: 1px 1px 1px #828282;
+ border: 1px solid #8A8A8A;
+ height: 12px;
+ width: 12px;
+ border-radius: 10px;
+ background: #FFFFFF;
+ cursor: pointer;
+}
+
+input[type=range]::-ms-track
+{
+ width: 100%;
+ height: 7px;
+ cursor: pointer;
+ animate: 0.2s;
+ background: transparent;
+ border-color: transparent;
+ color: transparent;
+}
+
+input[type=range]::-ms-fill-lower
+ {
+ background: #B6B6B6;
+ border: 1px solid #8A8A8A;
+ border-radius: 22px;
+ box-shadow: 0px 0px 0px #000000;
+}
+
+input[type=range]::-ms-fill-upper
+{
+ background: #B6B6B6;
+ border: 1px solid #8A8A8A;
+ border-radius: 22px;
+ box-shadow: 0px 0px 0px #000000;
+}
+
+input[type=range]::-ms-thumb
+{
+ box-shadow: 1px 1px 1px #828282;
+ border: 1px solid #8A8A8A;
+ height: 24px;
+ width: 7px;
+ border-radius: 0px;
+ background: #FFFFFF;
+ cursor: pointer;
+}
+
+input[type=range]:focus::-ms-fill-lower
+{
+ background: #B6B6B6;
+}
+
+input[type=range]:focus::-ms-fill-upper
+{
+ background: #B6B6B6;
+}
+
+input[type=range]:disabled {
+ opacity: 0.5;
+}
+
+#webrx-page-container
+{
+ height: 100%;
+ position: relative;
+ display: flex;
+ flex-direction: column;
+}
+
+#openwebrx-scale-container
+{
+ height: 47px;
+ overflow: hidden;
+ z-index:1000;
+ position: relative;
+}
+
+#openwebrx-frequency-container {
+ background-image: url("../gfx/openwebrx-scale-background.png");
+ background-repeat: repeat-x;
+ background-size: cover;
+ background-color: #444;
+ z-index: 1001;
+}
+
+#openwebrx-bookmarks-container
+{
+ height: 25px;
+ position: relative;
+ z-index: 1000;
+}
+
+#openwebrx-bookmarks-container .bookmark {
+ font-size: 12px;
+ background-color: #FFFF00;
+ border: 1px solid #000;
+ border-radius: 5px;
+ padding: 2px 5px;
+ cursor: pointer;
+ white-space: nowrap;
+ max-height: 14px;
+ max-width: 50px;
+
+ position: absolute;
+ bottom: 5px;
+ transform: translate(-50%, 0);
+}
+
+#openwebrx-bookmarks-container .bookmark .bookmark-content {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+#openwebrx-bookmarks-container .bookmark .bookmark-actions {
+ display: none;
+ text-align: right;
+}
+
+.bookmark-actions .action {
+ line-height: 0;
+}
+
+.bookmark-actions .action img {
+ width: 14px;
+}
+
+#openwebrx-bookmarks-container .bookmark.selected {
+ z-index: 1010;
+}
+
+#openwebrx-bookmarks-container .bookmark:hover {
+ z-index: 1011;
+ max-height: none;
+ max-width: none;
+}
+
+#openwebrx-bookmarks-container .bookmark[editable]:hover .bookmark-actions {
+ display: block;
+ margin-bottom: 5px;
+}
+
+#openwebrx-bookmarks-container .bookmark:after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ width: 0;
+ height: 0;
+ border: 5px solid transparent;
+ border-top-color: #FFFF00;
+ border-bottom: 0;
+ margin-left: -5px;
+ margin-bottom: -5px;
+}
+
+#openwebrx-bookmarks-container .bookmark[data-source=local] {
+ background-color: #0FF;
+}
+
+#openwebrx-bookmarks-container .bookmark[data-source=local]:after {
+ border-top-color: #0FF;
+}
+
+#openwebrx-bookmarks-container .bookmark[data-source=dial_frequencies] {
+ background-color: #0F0;
+}
+
+#openwebrx-bookmarks-container .bookmark[data-source=dial_frequencies]:after {
+ border-top-color: #0F0;
+}
+
+#webrx-canvas-background {
+ flex-grow: 1;
+ background-image: url('../gfx/openwebrx-background-cool-blue.png');
+ background-repeat: no-repeat;
+ background-color: #1e5f7f;
+ background-size: cover;
+ display: flex;
+ flex-direction: column;
+}
+
+@supports(background-image: -webkit-image-set(url('../gfx/openwebrx-background-cool-blue.webp') 1x)) {
+ #webrx-canvas-background {
+ background-image: -webkit-image-set(url('../gfx/openwebrx-background-cool-blue.webp') 1x);
+ }
+}
+
+@supports(background-image: image-set(url('../gfx/openwebrx-background-cool-blue.webp') 1x)) {
+ #webrx-canvas-background {
+ background-image: image-set(url('../gfx/openwebrx-background-cool-blue.webp') 1x);
+ }
+}
+
+#webrx-canvas-container
+{
+ position: relative;
+ overflow: visible;
+ cursor: crosshair;
+ flex-grow: 1;
+}
+
+#webrx-canvas-container canvas
+{
+ position: absolute;
+ top: 0;
+ border-style: none;
+ image-rendering: crisp-edges;
+ image-rendering: -webkit-optimize-contrast;
+ width: 100%;
+ height: 200px;
+ will-change: transform;
+}
+
+#openwebrx-log-scroll
+{
+ /*overflow-y:auto;*/
+ height: 125px;
+ width: 619px
+}
+
+.nano .nano-pane { background: #444; }
+.nano .nano-slider { background: #eee !important; }
+
+.webrx-error
+{
+ font-weight: bold;
+ color: #ff6262;
+}
+
+@font-face {
+ font-family: 'roboto-mono';
+ src: url('../fonts/RobotoMono-Regular.woff2') format('woff2'),
+ url('../fonts/RobotoMono-Regular.woff') format('woff'),
+ url('../fonts/RobotoMono-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+
+.webrx-actual-freq {
+ width: 100%;
+ text-align: left;
+ padding: 0;
+ margin: 0;
+ display: flex;
+ flex-direction: row;
+ cursor: pointer;
+}
+
+.webrx-actual-freq > * {
+ flex: 1;
+}
+
+.webrx-actual-freq .input-group {
+ display: flex;
+ flex-direction: row;
+}
+
+.webrx-actual-freq .input-group > * {
+ flex: 0 0 auto;
+}
+
+.webrx-actual-freq .input-group input {
+ flex: 1 0 auto;
+ margin-right: 0;
+ border-right: 1px solid #373737;
+ -moz-appearance: textfield;
+}
+
+.webrx-actual-freq .input-group input::-webkit-outer-spin-button,
+.webrx-actual-freq .input-group input::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+}
+
+.input-group > :not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.input-group > :not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.input-group :first-child {
+ padding-left: 5px;
+}
+
+.input-group :last-child {
+ padding-right: 5px
+}
+
+.webrx-actual-freq .input-group input, .webrx-actual-freq .input-group select {
+ outline: none;
+ font-size: 16pt;
+}
+
+.webrx-actual-freq input {
+ font-family: 'roboto-mono';
+ width: 0;
+ box-sizing: border-box;
+ border: 0;
+ padding: 0;
+ background-color: inherit;
+ color: inherit;
+}
+
+.webrx-actual-freq, .webrx-actual-freq input {
+ font-size: 16pt;
+ font-family: 'roboto-mono';
+}
+
+.webrx-actual-freq .digit {
+ cursor: ns-resize;
+}
+
+.webrx-actual-freq .digit:hover {
+ color: #FFFF50;
+ border-radius: 5px;
+ background: -webkit-gradient( linear, left top, left bottom, color-stop(0.0 , #373737), color-stop(1, #4F4F4F) );
+ background: -moz-linear-gradient( center top, #373737 0%, #4F4F4F 100% );
+}
+
+.webrx-mouse-freq {
+ width: 100%;
+ text-align: left;
+ font-size: 10pt;
+ color: #AAA;
+ font-family: 'roboto-mono';
+ margin-bottom: 5px;
+}
+
+#openwebrx-panels-container-left,
+#openwebrx-panels-container-right {
+ position: absolute;
+ bottom: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ height: 0;
+ overflow: visible;
+}
+
+#openwebrx-panels-container-left {
+ left: 0;
+ align-items: flex-start;
+}
+
+#openwebrx-panels-container-right {
+ right: 0;
+ align-items: flex-end;
+}
+
+.openwebrx-panel
+{
+ transform: perspective( 600px ) rotateX( 90deg );
+ background-color: #575757;
+ padding: 10px;
+ color: white;
+ font-size: 10pt;
+ border-radius: 15px;
+ -moz-border-radius: 15px;
+ margin: 5.9px;
+ box-sizing: content-box;
+}
+
+.openwebrx-panel a
+{
+ color: #5ca8ff;
+ text-shadow: none;
+}
+
+.openwebrx-panel-inner
+{
+ overflow-y: auto;
+ overflow-x: hidden;
+ height: 100%;
+}
+
+.openwebrx-button
+{
+ background-color: #373737;
+ padding: 4.2px;
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ color: White;
+ font-weight: bold;
+ margin-right: 1px;
+ cursor: pointer;
+ background:-webkit-gradient( linear, left top, left bottom, color-stop(0.0 , #373737), color-stop(1, #4F4F4F) );
+ background:-moz-linear-gradient( center top, #373737 0%, #4F4F4F 100% );
+ user-select: none;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ display: inline-block;
+}
+
+.openwebrx-button:hover, .openwebrx-demodulator-button.highlighted, .openwebrx-button.highlighted
+{
+ /*background:-webkit-gradient( linear, left top, left bottom, color-stop(0.0 , #3F3F3F), color-stop(1, #777777) );
+ background:-moz-linear-gradient( center top, #373737 5%, #4F4F4F 100% );*/
+ background: #474747;
+ color: #FFFF50;
+}
+
+.openwebrx-button:active
+{
+ background: #777777;
+ color: #FFFF50;
+}
+
+.openwebrx-button:last-child {
+ margin-right: 0;
+}
+
+.openwebrx-button.disabled {
+ opacity: 0.5;
+}
+
+.openwebrx-demodulator-button
+{
+ height: 19px;
+ font-size: 12pt;
+ text-align: center;
+ flex: 1;
+ margin-right: 5px;
+}
+
+.openwebrx-demodulator-button.same-mod {
+ color: #FFC;
+}
+
+.openwebrx-square-button img
+{
+ height: 27px;
+}
+
+.openwebrx-round-button
+{
+ margin-right: -2px;
+ width: 35px;
+ height: 35px;
+ border-radius: 25px;
+}
+
+.openwebrx-round-button img
+{
+ height: 30px;
+}
+
+.openwebrx-round-button-small
+{
+ margin-right: -3px;
+ width: 20px;
+ height: 20px;
+ border-radius: 25px;
+}
+
+.openwebrx-round-button-small img
+{
+ height: 20px;
+}
+
+img.openwebrx-mirror-img
+{
+ transform: scale(-1, 1);
+}
+
+
+.openwebrx-round-rightarrow img
+{
+ position: relative;
+ left: 12px;
+ top: 3px;
+}
+
+.openwebrx-round-leftarrow img
+{
+ position: relative;
+ left: 7px;
+ top: 3px;
+}
+
+#openwebrx-client-log-title
+{
+ margin-bottom: 5px;
+ font-weight: bold;
+}
+
+.openwebrx-progressbar
+{
+ position: relative;
+ border-radius: 5px;
+ background-color: #003850; /*#006235;*/
+ display: inline-block;
+ text-align: center;
+ font-size: 8pt;
+ font-weight: bold;
+ text-shadow: 0px 0px 4px #000000;
+ cursor: default;
+ user-select: none;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ overflow: hidden;
+ z-index: 1
+}
+
+.openwebrx-progressbar-bar {
+ background-color: #00aba6;
+ border-radius: 5px;
+ height: 100%;
+ width: 100%;
+ transition-property: transform, background-color;
+ transition-duration: 1s;
+ transition-timing-function: ease-in-out;
+ transform: translate(-100%) translateZ(0);
+ will-change: transform, background-color;
+ z-index: 0;
+}
+
+.openwebrx-progressbar--over .openwebrx-progressbar-bar {
+ background-color: #ff6262;
+}
+
+.openwebrx-progressbar-text
+{
+ position: absolute;
+ left:50%;
+ top:50%;
+ transform: translate(-50%, -50%);
+ white-space: nowrap;
+ z-index: 2;
+}
+
+#openwebrx-panel-status
+{
+ margin: 0 0 0 5.9px;
+ padding: 0px;
+ background-color:rgba(0, 0, 0, 0);
+}
+
+#openwebrx-panel-status div.openwebrx-progressbar
+{
+ width: 200px;
+ height: 20px;
+}
+
+#openwebrx-panel-receiver
+{
+ width:110px;
+}
+
+
+#openwebrx-panel-receiver .frequencies-container {
+ display: flex;
+ flex-direction: row;
+ gap: 5px;
+}
+
+#openwebrx-panel-receiver .frequencies {
+ flex-grow: 1;
+}
+
+#openwebrx-panel-receiver .openwebrx-bookmark-button {
+ width: 27px;
+ height: 27px;
+ text-align: center;
+}
+
+.openwebrx-panel-slider
+{
+ position: relative;
+ top: -2px;
+ width: 95px;
+}
+
+.openwebrx-panel-line
+{
+ padding-top: 5px;
+}
+
+.openwebrx-panel-flex-line {
+ display: flex;
+ flex-direction: row;
+}
+
+.openwebrx-panel-line:first-child {
+ padding-top: 0;
+}
+
+.openwebrx-modes-grid {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ margin: -5px -5px 0 0;
+}
+
+.openwebrx-modes-grid .openwebrx-demodulator-button {
+ margin: 0;
+ white-space: nowrap;
+ flex: 1 0 38px;
+ margin: 5px 5px 0 0;
+}
+
+@supports(gap: 5px) {
+ .openwebrx-modes-grid {
+ margin: 0;
+ gap: 5px;
+ }
+
+ .openwebrx-modes-grid .openwebrx-demodulator-button {
+ margin: 0;
+ }
+}
+
+#openwebrx-smeter {
+ border-color: #888;
+ border-style: solid;
+ border-width: 0px;
+ width: 255px;
+ height: 7px;
+ background-color: #373737;
+ border-radius: 3px;
+ overflow: hidden;
+}
+
+.openwebrx-smeter-bar {
+ transition-property: transform;
+ transition-duration: 0.2s;
+ transition-timing-function: linear;
+ will-change: transform;
+ transform: translate(-100%) translateZ(0);
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(to top, #ff5939 , #961700);
+ margin: 0;
+ padding: 0;
+ border-radius: 3px;
+}
+
+#openwebrx-smeter-db
+{
+ color: #aaa;
+ display: inline-block;
+ font-size: 10pt;
+ float: right;
+ margin-right: 5px;
+ margin-top: 24px;
+ font-family: 'roboto-mono';
+}
+
+.openwebrx-overlay {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ opacity: 0.8;
+ background-color: #777;
+ left: 0;
+ top: 0;
+ z-index: 1001;
+ color: white;
+ font-weight: bold;
+ font-size: 20pt;
+}
+
+#openwebrx-autoplay-overlay
+{
+ cursor: pointer;
+ transition: opacity 0.3s linear;
+}
+
+#openwebrx-autoplay-overlay svg {
+ width: 150px;
+}
+
+.openwebrx-overlay .overlay-content {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+}
+
+#openwebrx-error-overlay .overlay-content {
+ background-color: #000;
+ padding: 50px;
+ border-radius: 20px;
+}
+
+#openwebrx-digimode-canvas-container
+{
+ /*margin: -10px -10px 10px -10px;*/
+ margin: -10px -10px 0px -10px;
+ border-radius: 15px;
+ height: 150px;
+ background-color: #333;
+ position: relative;
+ overflow: hidden;
+}
+
+#openwebrx-digimode-canvas-container canvas
+{
+ position: absolute;
+ top: 0;
+ pointer-events: none;
+ transition: width 500ms, left 500ms;
+ will-change: transform;
+}
+
+.openwebrx-panel select,
+.openwebrx-panel input,
+.openwebrx-dialog select,
+.openwebrx-dialog input {
+ border-radius: 5px;
+ background-color: #373737;
+ color: White;
+ font-weight: normal;
+ font-size: 13pt;
+ margin-right: 1px;
+ background:linear-gradient(#373737, #4F4F4F);
+ border-color: transparent;
+ border-width: 0px;
+}
+
+@supports(-moz-appearance: none) {
+ .openwebrx-panel select,
+ .openwebrx-dialog select {
+ -moz-appearance: none;
+ background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%20%20xmlns%3Av%3D%22https%3A%2F%2Fvecta.io%2Fnano%22%3E%3Cpath%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8s-1.9-9.2-5.5-12.8z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E'),
+ linear-gradient(#373737, #4F4F4F);
+ background-repeat: no-repeat, repeat;
+ background-position: right .3em top 50%, 0 0;
+ background-size: .65em auto, 100%;
+ }
+
+ .openwebrx-panel .input-group select,
+ .openwebrx-dialog .input-group select {
+ padding-right: 1em;
+ }
+}
+
+.openwebrx-panel select option,
+.openwebrx-dialog select option {
+ border-width: 0px;
+ background-color: #373737;
+ color: White;
+}
+
+.openwebrx-secondary-demod-listbox {
+ width: 173px;
+ height: 27px;
+ padding-left:3px;
+ flex: 4;
+}
+
+#openwebrx-sdr-profiles-listbox {
+ width: 100%;
+ font-size: 10pt;
+ height: 27px;
+}
+
+#openwebrx-cursor-blink
+{
+ animation: cursor-blink 1s infinite;
+ /*animation: cursor-3d 2s infinite;*/
+ animation-timing-function: linear;
+ animation-direction: alternate;
+ height: 1em;
+ width: 8px;
+ background-color: White;
+ display: inline-block;
+ position: relative;
+ top: 1px;
+ /*perspective: 60px;*/
+
+}
+
+@keyframes cursor-blink
+{
+ 0%{ opacity: 0; }
+ 50% { opacity: 1; }
+ 100%{ opacity: 0; }
+}
+
+@keyframes cursor-3d
+{
+ 0%{ transform: rotateX(0deg) rotateX(Ydeg); }
+ 50% { transform: rotateX(180deg) rotateY(360deg); opacity: 0.1; }
+ 100%{ transform: rotateX(360deg) rotateY(720deg); }
+}
+
+#openwebrx-digimode-content
+{
+ word-wrap: break-word;
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+}
+
+#openwebrx-digimode-content-container
+{
+ overflow-y: hidden;
+ display: block;
+ height: 50px;
+ position: relative;
+}
+
+#openwebrx-digimode-content-container .gradient
+{
+ width: 100%;
+ height: 20px;
+ background: linear-gradient(to top, rgba(87,87,87,0) 0%,rgba(87,87,87,1) 100%);
+ position: absolute;
+ top: 0;
+ z-index: 10;
+}
+
+
+#openwebrx-digimode-content .part
+{
+ perspective: 700px;
+}
+
+#openwebrx-digimode-content .part
+{
+ animation: new-digimode-data-3d 100ms;
+ animation-timing-function: linear;
+ display: inline-block;
+ perspective-origin: 50% 50%;
+ transform-origin: 0% 50%;
+}
+
+@keyframes new-digimode-data
+{
+ 0%{ opacity: 0; }
+ 100%{ opacity: 1; }
+}
+
+@keyframes new-digimode-data-3d
+{
+ 0%{ transform: rotateX(0deg) rotateY(-90deg) translateX(-5px) scale(1.3); }
+ 100%{ transform: rotateX(0deg) rotateY(0deg) translateX(0) scale(1); }
+}
+
+#openwebrx-digimode-select-channel
+{
+ transition: all 500ms;
+ background-color: Yellow;
+ display: block;
+ position: absolute;
+ pointer-events: none;
+ height: 100%;
+ width: 0px;
+ top: 0px;
+ left: 0px;
+ opacity: 0.7;
+ border-style: solid;
+ border-width: 0px;
+ border-color: Red;
+}
+
+.openwebrx-meta-panel {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ /* compatibility with iOS 14.2 */
+ flex: 0 0 auto;
+}
+
+.openwebrx-meta-slot {
+ flex: 1;
+ width: 145px;
+ height: 196px;
+
+ background-color: #676767;
+ padding: 2px 0;
+ color: #333;
+
+ text-align: center;
+
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ overflow: hidden;
+}
+
+.openwebrx-meta-slot > * {
+ flex: 1 0 0;
+ line-height: 1.2em;
+}
+
+.openwebrx-meta-slot, .openwebrx-meta-slot .mute {
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+.openwebrx-meta-slot .mute {
+ display: none;
+ cursor: pointer;
+
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ background-color: rgba(0,0,0,.3);
+}
+
+.openwebrx-meta-slot .mute svg {
+ position: absolute;
+ top: 50%;
+ left: 0;
+ transform: translate(0, -50%);
+}
+
+.openwebrx-meta-slot.muted .mute {
+ display: block;
+}
+
+.openwebrx-meta-slot.active {
+ background-color: #95bbdf;
+}
+
+.openwebrx-meta-slot.sync .openwebrx-dmr-slot:before {
+ content:"";
+ display: inline-block;
+ margin: 0 5px;
+ width: 12px;
+ height: 12px;
+ background-color: #ABFF00;
+ border-radius: 50%;
+ box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #89FF00 0 2px 12px;
+}
+
+.openwebrx-meta-slot .openwebrx-meta-user-image {
+ flex: 0 1 100%;
+ background-position: center;
+ background-repeat: no-repeat;
+ line-height: 0;
+ overflow: hidden;
+}
+
+.openwebrx-meta-slot .openwebrx-meta-user-image img {
+ max-width: 100%;
+ max-height: 100%;
+ display: none;
+}
+
+.openwebrx-meta-slot.active.direct .openwebrx-meta-user-image .directcall,
+.openwebrx-meta-slot.active.individual .openwebrx-meta-user-image .directcall,
+#openwebrx-panel-metadata-ysf .openwebrx-meta-slot.active .openwebrx-meta-user-image .directcall,
+#openwebrx-panel-metadata-dstar .openwebrx-meta-slot.active .openwebrx-meta-user-image .directcall {
+ display: initial;
+}
+
+.openwebrx-meta-slot.active.group .openwebrx-meta-user-image .groupcall,
+.openwebrx-meta-slot.active.conference .openwebrx-meta-user-image .groupcall {
+ display: initial;
+}
+
+.openwebrx-meta-slot.group .openwebrx-dmr-target:not(:empty):before {
+ content: "Talkgroup: ";
+}
+
+.openwebrx-meta-slot.direct .openwebrx-dmr-target:not(:empty):before {
+ content: "Direct: ";
+}
+
+.openwebrx-dmr-timeslot-panel * {
+ cursor: pointer;
+ user-select: none;
+}
+
+.openwebrx-ysf-mode:not(:empty):before {
+ content: "Mode: ";
+}
+
+.openwebrx-ysf-up:not(:empty):before {
+ content: "Up: ";
+}
+
+.openwebrx-ysf-down:not(:empty):before {
+ content: "Down: ";
+}
+
+.openwebrx-dstar-yourcall:not(:empty):before {
+ content: "UR: ";
+}
+
+.openwebrx-dstar-departure:not(:empty):before {
+ content: "RPT1: ";
+}
+
+.openwebrx-dstar-destination:not(:empty):before {
+ content: "RPT2: ";
+}
+
+.openwebrx-meta-slot.individual .openwebrx-nxdn-destination:not(:empty):before {
+ content: "Direct: ";
+}
+
+.openwebrx-meta-slot.conference .openwebrx-nxdn-destination:not(:empty):before {
+ content: "Conference: ";
+}
+
+.openwebrx-maps-pin svg {
+ width: 15px;
+ height: 15px;
+ vertical-align: middle;
+}
+
+.openwebrx-message-panel {
+ height: 180px;
+ position: relative;
+}
+
+.openwebrx-message-panel tbody {
+ display: block;
+ overflow: auto;
+ height: 150px;
+ width: 100%;
+}
+
+.openwebrx-message-panel thead tr {
+ display: block;
+}
+
+.openwebrx-message-panel th,
+.openwebrx-message-panel td {
+ width: 50px;
+ text-align: left;
+ padding: 1px 3px;
+}
+
+#openwebrx-panel-wsjt-message .message {
+ width: 380px;
+}
+
+#openwebrx-panel-wsjt-message .decimal {
+ text-align: right;
+ width: 35px;
+}
+
+#openwebrx-panel-wsjt-message .decimal.freq {
+ width: 70px;
+}
+
+#openwebrx-panel-js8-message .message {
+ width: 465px;
+ max-width: 465px;
+}
+
+#openwebrx-panel-js8-message td.message {
+ white-space: nowrap;
+ overflow: hidden;
+ display: flex;
+ flex-direction: row-reverse;
+}
+
+#openwebrx-panel-js8-message .message div {
+ flex: 1;
+}
+
+#openwebrx-panel-js8-message .decimal {
+ text-align: right;
+ width: 35px;
+}
+
+#openwebrx-panel-js8-message .decimal.freq {
+ width: 70px;
+}
+
+#openwebrx-panel-packet-message .message {
+ width: 410px;
+ max-width: 410px;
+}
+
+#openwebrx-panel-packet-message .callsign {
+ width: 80px;
+}
+
+#openwebrx-panel-packet-message .coord {
+ width: 40px;
+ text-align: center;
+}
+
+#openwebrx-panel-pocsag-message .address {
+ width: 100px;
+}
+
+#openwebrx-panel-pocsag-message .message {
+ width: 486px;
+ max-width: 486px;
+ white-space: pre;
+}
+
+.aprs-symbol {
+ display: inline-block;
+ width: 15px;
+ height: 15px;
+ background-size: 240px 90px;
+}
+
+.aprs-symboltable-normal {
+ background-image: url(../../aprs-symbols/aprs-symbols-24-0.png)
+}
+
+.aprs-symboltable-alternate {
+ background-image: url(../../aprs-symbols/aprs-symbols-24-1.png)
+}
+
+.aprs-symboltable-overlay {
+ background-image: url(../../aprs-symbols/aprs-symbols-24-2.png)
+}
+
+.openwebrx-dialog {
+ background-color: #575757;
+ padding: 10px;
+ color: white;
+ position: fixed;
+ font-size: 10pt;
+ border-radius: 15px;
+ -moz-border-radius: 15px;
+ position: fixed;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, 0);
+}
+
+.openwebrx-dialog .form-field {
+ padding: 5px;
+ display: flex;
+ flex-direction: row;
+}
+
+.openwebrx-dialog .form-field:first-child {
+ padding-top: 0;
+}
+
+.openwebrx-dialog label {
+ display: inline-block;
+ flex-grow: 0;
+ width: 70px;
+ padding-right: 20px;
+ margin-top: auto;
+ margin-bottom: auto;
+}
+
+.openwebrx-dialog .form-field input,
+.openwebrx-dialog .form-field select {
+ flex-grow: 1;
+ height: 27px;
+}
+
+.openwebrx-dialog .form-field input {
+ padding: 0 5px;
+}
+
+.openwebrx-dialog .buttons {
+ text-align: right;
+ padding: 5px 5px 0;
+ border-top: 1px solid #666;
+}
+
+.openwebrx-dialog .buttons .openwebrx-button {
+ font-size: 12pt;
+ min-width: 50px;
+ text-align: center;
+ padding: 5px 10px;
+}
+
+#openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-content-container,
+#openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-content-container,
+#openwebrx-panel-digimodes[data-mode="jt65"] #openwebrx-digimode-content-container,
+#openwebrx-panel-digimodes[data-mode="jt9"] #openwebrx-digimode-content-container,
+#openwebrx-panel-digimodes[data-mode="ft4"] #openwebrx-digimode-content-container,
+#openwebrx-panel-digimodes[data-mode="packet"] #openwebrx-digimode-content-container,
+#openwebrx-panel-digimodes[data-mode="pocsag"] #openwebrx-digimode-content-container,
+#openwebrx-panel-digimodes[data-mode="js8"] #openwebrx-digimode-content-container,
+#openwebrx-panel-digimodes[data-mode="fst4"] #openwebrx-digimode-content-container,
+#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-content-container,
+#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-content-container,
+#openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-select-channel,
+#openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-select-channel,
+#openwebrx-panel-digimodes[data-mode="jt65"] #openwebrx-digimode-select-channel,
+#openwebrx-panel-digimodes[data-mode="jt9"] #openwebrx-digimode-select-channel,
+#openwebrx-panel-digimodes[data-mode="ft4"] #openwebrx-digimode-select-channel,
+#openwebrx-panel-digimodes[data-mode="packet"] #openwebrx-digimode-select-channel,
+#openwebrx-panel-digimodes[data-mode="pocsag"] #openwebrx-digimode-select-channel,
+#openwebrx-panel-digimodes[data-mode="js8"] #openwebrx-digimode-select-channel,
+#openwebrx-panel-digimodes[data-mode="fst4"] #openwebrx-digimode-select-channel,
+#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-select-channel,
+#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-select-channel
+{
+ display: none;
+}
+
+#openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-canvas-container,
+#openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-canvas-container,
+#openwebrx-panel-digimodes[data-mode="jt65"] #openwebrx-digimode-canvas-container,
+#openwebrx-panel-digimodes[data-mode="jt9"] #openwebrx-digimode-canvas-container,
+#openwebrx-panel-digimodes[data-mode="ft4"] #openwebrx-digimode-canvas-container,
+#openwebrx-panel-digimodes[data-mode="packet"] #openwebrx-digimode-canvas-container,
+#openwebrx-panel-digimodes[data-mode="pocsag"] #openwebrx-digimode-canvas-container,
+#openwebrx-panel-digimodes[data-mode="js8"] #openwebrx-digimode-canvas-container,
+#openwebrx-panel-digimodes[data-mode="fst4"] #openwebrx-digimode-canvas-container,
+#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-canvas-container,
+#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-canvas-container
+{
+ height: 200px;
+ margin: -10px;
+}
+
+.openwebrx-zoom-button svg {
+ height: 27px;
+}
+
+.openwebrx-slider-button svg {
+ position:relative;
+ top: 1px;
+ height: 14px;
+}
+
+.openwebrx-mute-button svg.muted {
+ display: none;
+}
+
+.openwebrx-mute-button.muted svg.muted {
+ display: initial;
+}
+
+.openwebrx-mute-button.muted svg.unmuted {
+ display: none;
+}
+
+.bookmark .bookmark-actions .openwebrx-button svg {
+ height: 14px;
+}
+
+#openwebrx-waterfall-colors-auto .continuous {
+ display: none;
+}
+
+#openwebrx-waterfall-colors-auto.highlighted .continuous {
+ display: initial;
+}
+
+#openwebrx-waterfall-colors-auto.highlighted .auto {
+ display: none;
+}
+
+.openwebrx-waterfall-container {
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+}
+
+.openwebrx-waterfall-container > * {
+ flex: 0 0 auto;
+}
\ No newline at end of file
diff --git a/openwebrx/htdocs/favicon.ico b/openwebrx/htdocs/favicon.ico
new file mode 100644
index 0000000..6a07f1b
Binary files /dev/null and b/openwebrx/htdocs/favicon.ico differ
diff --git a/openwebrx/htdocs/features.html b/openwebrx/htdocs/features.html
new file mode 100644
index 0000000..53099b6
--- /dev/null
+++ b/openwebrx/htdocs/features.html
@@ -0,0 +1,25 @@
+
+ OpenWebRX Feature report
+
+
+
+
+
+
+
+
+ ${header}
+
+ ${breadcrumb}
+
OpenWebRX Feature Report
+
+
+
Feature
+
Requirement
+
Description
+
Available
+
+
+ ${breadcrumb}
+
+
\ No newline at end of file
diff --git a/openwebrx/htdocs/features.js b/openwebrx/htdocs/features.js
new file mode 100644
index 0000000..fef2817
--- /dev/null
+++ b/openwebrx/htdocs/features.js
@@ -0,0 +1,23 @@
+$(function(){
+ var converter = new showdown.Converter();
+ $.ajax('api/features').done(function(data){
+ var $table = $('table.features');
+ $.each(data, function(name, details) {
+ var requirements = $.map(details.requirements, function(r, name){
+ return '
' +
+ '
' +
+ '
' + name + '
' +
+ '
' + converter.makeHtml(r.description) + '
' +
+ '
' + (r.available ? 'YES' : 'NO') + '
' +
+ '
';
+ });
+ $table.append(
+ '
' +
+ '
' + name + '
' +
+ '
' + (details.available ? 'YES' : 'NO') + '
' +
+ '
' +
+ requirements.join("")
+ );
+ })
+ });
+});
diff --git a/openwebrx/htdocs/fonts/RobotoMono-Regular.ttf b/openwebrx/htdocs/fonts/RobotoMono-Regular.ttf
new file mode 100644
index 0000000..7c4ce36
Binary files /dev/null and b/openwebrx/htdocs/fonts/RobotoMono-Regular.ttf differ
diff --git a/openwebrx/htdocs/fonts/RobotoMono-Regular.woff b/openwebrx/htdocs/fonts/RobotoMono-Regular.woff
new file mode 100644
index 0000000..1b805ea
Binary files /dev/null and b/openwebrx/htdocs/fonts/RobotoMono-Regular.woff differ
diff --git a/openwebrx/htdocs/fonts/RobotoMono-Regular.woff2 b/openwebrx/htdocs/fonts/RobotoMono-Regular.woff2
new file mode 100644
index 0000000..dab2585
Binary files /dev/null and b/openwebrx/htdocs/fonts/RobotoMono-Regular.woff2 differ
diff --git a/openwebrx/htdocs/gfx/favicon128.png b/openwebrx/htdocs/gfx/favicon128.png
new file mode 100644
index 0000000..ad42441
Binary files /dev/null and b/openwebrx/htdocs/gfx/favicon128.png differ
diff --git a/openwebrx/htdocs/gfx/favicon32.png b/openwebrx/htdocs/gfx/favicon32.png
new file mode 100644
index 0000000..2c534af
Binary files /dev/null and b/openwebrx/htdocs/gfx/favicon32.png differ
diff --git a/openwebrx/htdocs/gfx/favicon44.png b/openwebrx/htdocs/gfx/favicon44.png
new file mode 100644
index 0000000..d21f326
Binary files /dev/null and b/openwebrx/htdocs/gfx/favicon44.png differ
diff --git a/openwebrx/htdocs/gfx/favicon64.png b/openwebrx/htdocs/gfx/favicon64.png
new file mode 100644
index 0000000..b08e03f
Binary files /dev/null and b/openwebrx/htdocs/gfx/favicon64.png differ
diff --git a/openwebrx/htdocs/gfx/favicon96.png b/openwebrx/htdocs/gfx/favicon96.png
new file mode 100644
index 0000000..ee94129
Binary files /dev/null and b/openwebrx/htdocs/gfx/favicon96.png differ
diff --git a/openwebrx/htdocs/gfx/openwebrx-avatar.png b/openwebrx/htdocs/gfx/openwebrx-avatar.png
new file mode 100644
index 0000000..fc20529
Binary files /dev/null and b/openwebrx/htdocs/gfx/openwebrx-avatar.png differ
diff --git a/openwebrx/htdocs/gfx/openwebrx-background-cool-blue.png b/openwebrx/htdocs/gfx/openwebrx-background-cool-blue.png
new file mode 100644
index 0000000..236b366
Binary files /dev/null and b/openwebrx/htdocs/gfx/openwebrx-background-cool-blue.png differ
diff --git a/openwebrx/htdocs/gfx/openwebrx-background-cool-blue.webp b/openwebrx/htdocs/gfx/openwebrx-background-cool-blue.webp
new file mode 100644
index 0000000..51f7852
Binary files /dev/null and b/openwebrx/htdocs/gfx/openwebrx-background-cool-blue.webp differ
diff --git a/openwebrx/htdocs/gfx/openwebrx-directcall.svg b/openwebrx/htdocs/gfx/openwebrx-directcall.svg
new file mode 100644
index 0000000..3440112
--- /dev/null
+++ b/openwebrx/htdocs/gfx/openwebrx-directcall.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/openwebrx/htdocs/gfx/openwebrx-groupcall.svg b/openwebrx/htdocs/gfx/openwebrx-groupcall.svg
new file mode 100644
index 0000000..5083a57
--- /dev/null
+++ b/openwebrx/htdocs/gfx/openwebrx-groupcall.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/openwebrx/htdocs/gfx/openwebrx-scale-background.png b/openwebrx/htdocs/gfx/openwebrx-scale-background.png
new file mode 100644
index 0000000..91453c5
Binary files /dev/null and b/openwebrx/htdocs/gfx/openwebrx-scale-background.png differ
diff --git a/openwebrx/htdocs/gfx/openwebrx-top-photo.jpg b/openwebrx/htdocs/gfx/openwebrx-top-photo.jpg
new file mode 100644
index 0000000..afc8e7e
Binary files /dev/null and b/openwebrx/htdocs/gfx/openwebrx-top-photo.jpg differ
diff --git a/openwebrx/htdocs/gfx/svg-defs.svg b/openwebrx/htdocs/gfx/svg-defs.svg
new file mode 100644
index 0000000..251b051
--- /dev/null
+++ b/openwebrx/htdocs/gfx/svg-defs.svg
@@ -0,0 +1,28 @@
+
\ No newline at end of file
diff --git a/openwebrx/htdocs/include/header.include.html b/openwebrx/htdocs/include/header.include.html
new file mode 100644
index 0000000..8ae0811
--- /dev/null
+++ b/openwebrx/htdocs/include/header.include.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
${receiver_name}
+
${receiver_location} | Loc: ${locator}, ASL: ${receiver_asl} m