`s.
+
+.nav {
+ display: flex;
+ flex-wrap: wrap;
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+}
+
+.nav-link {
+ display: block;
+ padding: @nav-link-padding-y @nav-link-padding-x;
+
+ #hover-focus({
+ text-decoration: none;
+ });
+
+ // Disabled state lightens text
+ &.disabled {
+ color: @nav-link-disabled-color;
+ }
+}
+
+//
+// Tabs
+//
+
+.nav-tabs {
+ border-bottom: @nav-tabs-border-width solid @nav-tabs-border-color;
+
+ .nav-item {
+ margin-bottom: -@nav-tabs-border-width;
+ }
+
+ .nav-link {
+ border: @nav-tabs-border-width solid transparent;
+ #border-top-radius(@nav-tabs-border-radius);
+
+ #hover-focus({
+ border-color: @nav-tabs-link-hover-border-color;
+ });
+
+ &.disabled {
+ color: @nav-link-disabled-color;
+ background-color: transparent;
+ border-color: transparent;
+ }
+ }
+
+ .nav-link.active,
+ .nav-item.show .nav-link {
+ color: @nav-tabs-link-active-color;
+ background-color: @nav-tabs-link-active-bg;
+ border-color: @nav-tabs-link-active-border-color;
+ }
+
+ .dropdown-menu {
+ // Make dropdown border overlap tab border
+ margin-top: -@nav-tabs-border-width;
+ // Remove the top rounded corners here since there is a hard edge above the menu
+ #border-top-radius(0);
+ }
+}
+
+
+//
+// Pills
+//
+
+.nav-pills {
+ .nav-link {
+ #border-radius(@nav-pills-border-radius);
+ }
+
+ .nav-link.active,
+ .show > .nav-link {
+ color: @nav-pills-link-active-color;
+ background-color: @nav-pills-link-active-bg;
+ }
+}
+
+
+//
+// Justified variants
+//
+
+.nav-fill {
+ .nav-item {
+ flex: 1 1 auto;
+ text-align: center;
+ }
+}
+
+.nav-justified {
+ .nav-item {
+ flex-basis: 0;
+ flex-grow: 1;
+ text-align: center;
+ }
+}
+
+
+// Tabbable tabs
+//
+// Hide tabbable panes to start, show them when `.active`
+
+.tab-content {
+ > .tab-pane {
+ display: none;
+ }
+ > .active {
+ display: block;
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/_navbar.less b/include/thirdparty/Bootstrap4/less/_navbar.less
new file mode 100644
index 0000000..1978c79
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_navbar.less
@@ -0,0 +1,351 @@
+// Contents
+//
+// Navbar
+// Navbar brand
+// Navbar nav
+// Navbar text
+// Navbar divider
+// Responsive navbar
+// Navbar position
+// Navbar themes
+
+
+// Navbar
+//
+// Provide a static navbar from which we expand to create full-width, fixed, and
+// other navbar variations.
+
+.navbar {
+ position: relative;
+ display: flex;
+ flex-wrap: wrap; // allow us to do the line break for collapsing content
+ align-items: center;
+ justify-content: space-between; // space out brand from logo
+ padding: @navbar-padding-y @navbar-padding-x;
+
+ // Because flex properties aren't inherited, we need to redeclare these first
+ // few properties so that content nested within behave properly.
+ > .container,
+ > .container-fluid {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-between;
+ }
+}
+
+
+// Navbar brand
+//
+// Used for brand, project, or site names.
+
+.navbar-brand {
+ display: inline-block;
+ padding-top: @navbar-brand-padding-y;
+ padding-bottom: @navbar-brand-padding-y;
+ margin-right: @navbar-padding-x;
+ font-size: @navbar-brand-font-size;
+ line-height: inherit;
+ white-space: nowrap;
+
+ #hover-focus({
+ text-decoration: none;
+ });
+}
+
+
+// Navbar nav
+//
+// Custom navbar navigation (doesn't require `.nav`, but does make use of `.nav-link`).
+
+.navbar-nav {
+ display: flex;
+ flex-direction: column; // cannot use `inherit` to get the `.navbar`s value
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+
+ .nav-link {
+ padding-right: 0;
+ padding-left: 0;
+ }
+
+ .dropdown-menu {
+ position: static;
+ float: none;
+ }
+}
+
+
+// Navbar text
+//
+//
+
+.navbar-text {
+ display: inline-block;
+ padding-top: @nav-link-padding-y;
+ padding-bottom: @nav-link-padding-y;
+}
+
+
+// Responsive navbar
+//
+// Custom styles for responsive collapsing and toggling of navbar contents.
+// Powered by the collapse Bootstrap JavaScript plugin.
+
+// When collapsed, prevent the toggleable navbar contents from appearing in
+// the default flexbox row orientation. Requires the use of `flex-wrap: wrap`
+// on the `.navbar` parent.
+.navbar-collapse {
+ flex-basis: 100%;
+ flex-grow: 1;
+ // For always expanded or extra full navbars, ensure content aligns itself
+ // properly vertically. Can be easily overridden with flex utilities.
+ align-items: center;
+}
+
+// Button for toggling the navbar when in its collapsed state
+.navbar-toggler {
+ padding: @navbar-toggler-padding-y @navbar-toggler-padding-x;
+ font-size: @navbar-toggler-font-size;
+ line-height: 1;
+ background-color: transparent; // remove default button style
+ border: @border-width solid transparent; // remove default button style
+ #border-radius(@navbar-toggler-border-radius);
+
+ #hover-focus({
+ text-decoration: none;
+ });
+
+ // Opinionated: add "hand" cursor to non-disabled .navbar-toggler elements
+ &:not(:disabled):not(.disabled) {
+ cursor: pointer;
+ }
+}
+
+// Keep as a separate element so folks can easily override it with another icon
+// or image file as needed.
+.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%;
+}
+
+// Generate series of `.navbar-expand-*` responsive classes for configuring
+// where your navbar collapses.
+.navbar-expand {
+ //@each @breakpoint in map-keys(@grid-breakpoints) {
+ // @next: breakpoint-next(@breakpoint, @grid-breakpoints);
+ // @infix: breakpoint-infix(@next, @grid-breakpoints);
+ //
+ // &@{infix} {
+ // @include media-breakpoint-down(@breakpoint) {
+ // > .container,
+ // > .container-fluid {
+ // padding-right: 0;
+ // padding-left: 0;
+ // }
+ // }
+ //
+ // @include media-breakpoint-up(@next) {
+ // flex-flow: row nowrap;
+ // justify-content: flex-start;
+ //
+ // .navbar-nav {
+ // flex-direction: row;
+ //
+ // .dropdown-menu {
+ // position: absolute;
+ // }
+ //
+ // .nav-link {
+ // padding-right: $navbar-nav-link-padding-x;
+ // padding-left: $navbar-nav-link-padding-x;
+ // }
+ // }
+ //
+ // // For nesting containers, have to redeclare for alignment purposes
+ // > .container,
+ // > .container-fluid {
+ // flex-wrap: nowrap;
+ // }
+ //
+ // .navbar-collapse {
+ // display: flex !important; // stylelint-disable-line declaration-no-important
+ //
+ // // Changes flex-bases to auto because of an IE10 bug
+ // flex-basis: auto;
+ // }
+ //
+ // .navbar-toggler {
+ // display: none;
+ // }
+ // }
+ // }
+ //}
+ #each-breakpoint-navbar(@i: 1) when (@i =< length(@grid-breakpoints)) {
+ @breakpoint: extract(extract(@grid-breakpoints, @i), 1);
+ @next: breakpoint-next(@breakpoint, @grid-breakpoints);
+ @infix: breakpoint-infix(@next, @grid-breakpoints);
+
+ &@{infix} {
+ #media-breakpoint-down(@breakpoint, {
+ > .container,
+ > .container-fluid {
+ padding-right: 0;
+ padding-left: 0;
+ }
+ });
+
+ #media-breakpoint-up(@next, {
+ flex-flow: row nowrap;
+ justify-content: flex-start;
+
+ .navbar-nav {
+ flex-direction: row;
+
+ .dropdown-menu {
+ position: absolute;
+ }
+
+ .nav-link {
+ padding-right: @navbar-nav-link-padding-x;
+ padding-left: @navbar-nav-link-padding-x;
+ }
+ }
+
+ // For nesting containers, have to redeclare for alignment purposes
+ > .container,
+ > .container-fluid {
+ flex-wrap: nowrap;
+ }
+
+ .navbar-collapse {
+ display: flex !important;
+
+ // Changes flex-bases to auto because of an IE10 bug
+ flex-basis: auto;
+ }
+
+ .navbar-toggler {
+ display: none;
+ }
+ });
+ }
+
+ #each-breakpoint-navbar((@i + 1));
+ } #each-breakpoint-navbar();
+}
+
+
+// Navbar themes
+//
+// Styles for switching between navbars with light or dark background.
+
+// Dark links against a light background
+.navbar-light {
+ .navbar-brand {
+ color: @navbar-light-active-color;
+
+ #hover-focus({
+ color: @navbar-light-active-color;
+ });
+ }
+
+ .navbar-nav {
+ .nav-link {
+ color: @navbar-light-color;
+
+ #hover-focus({
+ color: @navbar-light-hover-color;
+ });
+
+ &.disabled {
+ color: @navbar-light-disabled-color;
+ }
+ }
+
+ .show > .nav-link,
+ .active > .nav-link,
+ .nav-link.show,
+ .nav-link.active {
+ color: @navbar-light-active-color;
+ }
+ }
+
+ .navbar-toggler {
+ color: @navbar-light-color;
+ border-color: @navbar-light-toggler-border-color;
+ }
+
+ .navbar-toggler-icon {
+ background-image: @navbar-light-toggler-icon-bg;
+ }
+
+ .navbar-text {
+ color: @navbar-light-color;
+ a {
+ color: @navbar-light-active-color;
+
+ #hover-focus({
+ color: @navbar-light-active-color;
+ });
+ }
+ }
+}
+
+// White links against a dark background
+.navbar-dark {
+ .navbar-brand {
+ color: @navbar-dark-active-color;
+
+ #hover-focus({
+ color: @navbar-dark-active-color;
+ });
+ }
+
+ .navbar-nav {
+ .nav-link {
+ color: @navbar-dark-color;
+
+ #hover-focus({
+ color: @navbar-dark-hover-color;
+ });
+
+ &.disabled {
+ color: @navbar-dark-disabled-color;
+ }
+ }
+
+ .show > .nav-link,
+ .active > .nav-link,
+ .nav-link.show,
+ .nav-link.active {
+ color: @navbar-dark-active-color;
+ }
+ }
+
+ .navbar-toggler {
+ color: @navbar-dark-color;
+ border-color: @navbar-dark-toggler-border-color;
+ }
+
+ .navbar-toggler-icon {
+ background-image: @navbar-dark-toggler-icon-bg;
+ }
+
+ .navbar-text {
+ color: @navbar-dark-color;
+ a {
+ color: @navbar-dark-active-color;
+
+ #hover-focus({
+ color: @navbar-dark-active-color;
+ });
+ }
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/_pagination.less b/include/thirdparty/Bootstrap4/less/_pagination.less
new file mode 100644
index 0000000..658c07e
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_pagination.less
@@ -0,0 +1,78 @@
+.pagination {
+ display: flex;
+ #list-unstyled();
+ #border-radius();
+}
+
+.page-link {
+ position: relative;
+ display: block;
+ padding: @pagination-padding-y @pagination-padding-x;
+ margin-left: -@pagination-border-width;
+ line-height: @pagination-line-height;
+ color: @pagination-color;
+ background-color: @pagination-bg;
+ border: @pagination-border-width solid @pagination-border-color;
+
+ &:hover {
+ z-index: 2;
+ color: @pagination-hover-color;
+ text-decoration: none;
+ background-color: @pagination-hover-bg;
+ border-color: @pagination-hover-border-color;
+ }
+
+ &:focus {
+ z-index: 2;
+ outline: @pagination-focus-outline;
+ box-shadow: @pagination-focus-box-shadow;
+ }
+
+ // Opinionated: add "hand" cursor to non-disabled .page-link elements
+ &:not(:disabled):not(.disabled) {
+ cursor: pointer;
+ }
+}
+
+.page-item {
+ &:first-child {
+ .page-link {
+ margin-left: 0;
+ #border-left-radius(@border-radius);
+ }
+ }
+ &:last-child {
+ .page-link {
+ #border-right-radius(@border-radius);
+ }
+ }
+
+ &.active .page-link {
+ z-index: 1;
+ color: @pagination-active-color;
+ background-color: @pagination-active-bg;
+ border-color: @pagination-active-border-color;
+ }
+
+ &.disabled .page-link {
+ color: @pagination-disabled-color;
+ pointer-events: none;
+ // Opinionated: remove the "hand" cursor set previously for .page-link
+ cursor: auto;
+ background-color: @pagination-disabled-bg;
+ border-color: @pagination-disabled-border-color;
+ }
+}
+
+
+//
+// Sizing
+//
+
+.pagination-lg {
+ #pagination-size(@pagination-padding-y-lg, @pagination-padding-x-lg, @font-size-lg, @line-height-lg, @border-radius-lg);
+}
+
+.pagination-sm {
+ #pagination-size(@pagination-padding-y-sm, @pagination-padding-x-sm, @font-size-sm, @line-height-sm, @border-radius-sm);
+}
diff --git a/include/thirdparty/Bootstrap4/less/_popover.less b/include/thirdparty/Bootstrap4/less/_popover.less
new file mode 100644
index 0000000..b7afcbe
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_popover.less
@@ -0,0 +1,183 @@
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: @zindex-popover;
+ display: block;
+ max-width: @popover-max-width;
+ // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
+ // So reset our font and text properties to avoid inheriting weird values.
+ #reset-text();
+ font-size: @popover-font-size;
+ // Allow breaking very long words so they don't overflow the popover's bounds
+ word-wrap: break-word;
+ background-color: @popover-bg;
+ background-clip: padding-box;
+ border: @popover-border-width solid @popover-border-color;
+ #border-radius(@popover-border-radius);
+ #box-shadow(@popover-box-shadow);
+
+ .arrow {
+ position: absolute;
+ display: block;
+ width: @popover-arrow-width;
+ height: @popover-arrow-height;
+ margin: 0 @border-radius-lg;
+
+ &::before,
+ &::after {
+ position: absolute;
+ display: block;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+ }
+ }
+}
+
+.bs-popover-top {
+ margin-bottom: @popover-arrow-height;
+
+ .arrow {
+ bottom: ~"calc((@{popover-arrow-height} + @{popover-border-width}) * -1)";
+ }
+
+ .arrow::before,
+ .arrow::after {
+ border-width: @popover-arrow-height (@popover-arrow-width / 2) 0;
+ }
+
+ .arrow::before {
+ bottom: 0;
+ border-top-color: @popover-arrow-outer-color;
+ }
+
+ .arrow::after {
+ bottom: @popover-border-width;
+ border-top-color: @popover-arrow-color;
+ }
+}
+
+.bs-popover-right {
+ margin-left: @popover-arrow-height;
+
+ .arrow {
+ left: ~"calc((@{popover-arrow-height} + @{popover-border-width}) * -1)";
+ width: @popover-arrow-height;
+ height: @popover-arrow-width;
+ margin: @border-radius-lg 0; // make sure the arrow does not touch the popover's rounded corners
+ }
+
+ .arrow::before,
+ .arrow::after {
+ border-width: (@popover-arrow-width / 2) @popover-arrow-height (@popover-arrow-width / 2) 0;
+ }
+
+ .arrow::before {
+ left: 0;
+ border-right-color: @popover-arrow-outer-color;
+ }
+
+ .arrow::after {
+ left: @popover-border-width;
+ border-right-color: @popover-arrow-color;
+ }
+}
+
+.bs-popover-bottom {
+ margin-top: @popover-arrow-height;
+
+ .arrow {
+ top: ~"calc((@{popover-arrow-height} + @{popover-border-width}) * -1)";
+ }
+
+ .arrow::before,
+ .arrow::after {
+ border-width: 0 (@popover-arrow-width / 2) @popover-arrow-height (@popover-arrow-width / 2);
+ }
+
+ .arrow::before {
+ top: 0;
+ border-bottom-color: @popover-arrow-outer-color;
+ }
+
+ .arrow::after {
+ top: @popover-border-width;
+ border-bottom-color: @popover-arrow-color;
+ }
+
+ // This will remove the popover-header's border just below the arrow
+ .popover-header::before {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ display: block;
+ width: @popover-arrow-width;
+ margin-left: (@popover-arrow-width / -2);
+ content: "";
+ border-bottom: @popover-border-width solid @popover-header-bg;
+ }
+}
+
+.bs-popover-left {
+ margin-right: @popover-arrow-height;
+
+ .arrow {
+ right: ~"calc((@{popover-arrow-height} + @{popover-border-width}) * -1)";
+ width: @popover-arrow-height;
+ height: @popover-arrow-width;
+ margin: @border-radius-lg 0; // make sure the arrow does not touch the popover's rounded corners
+ }
+
+ .arrow::before,
+ .arrow::after {
+ border-width: (@popover-arrow-width / 2) 0 (@popover-arrow-width / 2) @popover-arrow-height;
+ }
+
+ .arrow::before {
+ right: 0;
+ border-left-color: @popover-arrow-outer-color;
+ }
+
+ .arrow::after {
+ right: @popover-border-width;
+ border-left-color: @popover-arrow-color;
+ }
+}
+
+.bs-popover-auto {
+ &[x-placement^="top"] {
+ &:extend(.bs-popover-top all);
+ }
+ &[x-placement^="right"] {
+ &:extend(.bs-popover-right all);
+ }
+ &[x-placement^="bottom"] {
+ &:extend(.bs-popover-bottom all);
+ }
+ &[x-placement^="left"] {
+ &:extend(.bs-popover-left all);
+ }
+}
+
+
+// Offset the popover to account for the popover arrow
+.popover-header {
+ padding: @popover-header-padding-y @popover-header-padding-x;
+ margin-bottom: 0; // Reset the default from Reboot
+ font-size: @font-size-base;
+ color: @popover-header-color;
+ background-color: @popover-header-bg;
+ border-bottom: @popover-border-width solid darken(@popover-header-bg, 5%);
+ @offset-border-width: ~"calc(@{border-radius-lg} - @{popover-border-width})";
+ #border-top-radius(@offset-border-width);
+
+ &:empty {
+ display: none;
+ }
+}
+
+.popover-body {
+ padding: @popover-body-padding-y @popover-body-padding-x;
+ color: @popover-body-color;
+}
diff --git a/include/thirdparty/Bootstrap4/less/_print.less b/include/thirdparty/Bootstrap4/less/_print.less
new file mode 100644
index 0000000..a317f1b
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_print.less
@@ -0,0 +1,139 @@
+// Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css
+
+// ==========================================================================
+// Print styles.
+// Inlined to avoid the additional HTTP request:
+// https://www.phpied.com/delay-loading-your-print-css/
+// ==========================================================================
+
+@media print {
+ & when (@enable-print-styles) {
+ *,
+ *::before,
+ *::after {
+ // Bootstrap specific; comment out `color` and `background`
+ //color: @black !important; // Black prints faster: http://www.sanbeiji.com/archives/953
+ text-shadow: none !important;
+ //background: transparent !important;
+ box-shadow: none !important;
+ }
+
+ a {
+ &:not(.btn) {
+ text-decoration: underline;
+ }
+ }
+
+ // Bootstrap specific; comment the following selector out
+ //a[href]::after {
+ // content: " (" attr(href) ")";
+ //}
+
+ abbr[title]::after {
+ content: " (" attr(title) ")";
+ }
+
+ // Bootstrap specific; comment the following selector out
+ //
+ // Don't show links that are fragment identifiers,
+ // or use the `javascript:` pseudo protocol
+ //
+
+ //a[href^="#"]::after,
+ //a[href^="javascript:"]::after {
+ // content: "";
+ //}
+
+ pre {
+ white-space: pre-wrap !important;
+ }
+ pre,
+ blockquote {
+ border: @border-width solid @gray-500; // Bootstrap custom code; using `@border-width` instead of 1px
+ page-break-inside: avoid;
+ }
+
+ //
+ // Printing Tables:
+ // http://css-discuss.incutio.com/wiki/Printing_Tables
+ //
+
+ thead {
+ display: table-header-group;
+ }
+
+ tr,
+ img {
+ page-break-inside: avoid;
+ }
+
+ p,
+ h2,
+ h3 {
+ orphans: 3;
+ widows: 3;
+ }
+
+ h2,
+ h3 {
+ page-break-after: avoid;
+ }
+
+ // Bootstrap specific changes start
+
+ // Specify a size and min-width to make printing closer across browsers.
+ // We don't set margin here because it breaks `size` in Chrome. We also
+ // don't use `!important` on `size` as it breaks in Chrome.
+ @page {
+ size: @print-page-size;
+ }
+ body {
+ min-width: @print-body-min-width !important;
+ }
+ .container {
+ min-width: @print-body-min-width !important;
+ }
+
+ // Bootstrap components
+ .navbar {
+ display: none;
+ }
+ .badge {
+ border: @border-width solid @black;
+ }
+
+ .table {
+ border-collapse: collapse !important;
+
+ td,
+ th {
+ background-color: @white !important;
+ }
+ }
+
+ .table-bordered {
+ th,
+ td {
+ border: 1px solid @gray-300 !important;
+ }
+ }
+
+ .table-dark {
+ color: inherit;
+
+ th,
+ td,
+ thead th,
+ tbody + tbody {
+ border-color: @table-border-color;
+ }
+ }
+
+ .table .thead-dark th {
+ color: inherit;
+ border-color: @table-border-color;
+ }
+
+ // Bootstrap specific changes end
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/_progress.less b/include/thirdparty/Bootstrap4/less/_progress.less
new file mode 100644
index 0000000..202c277
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_progress.less
@@ -0,0 +1,34 @@
+@keyframes progress-bar-stripes {
+ from { background-position: @progress-height 0; }
+ to { background-position: 0 0; }
+}
+
+.progress {
+ display: flex;
+ height: @progress-height;
+ overflow: hidden; // force rounded corners by cropping it
+ font-size: @progress-font-size;
+ background-color: @progress-bg;
+ #border-radius(@progress-border-radius);
+ #box-shadow(@progress-box-shadow);
+}
+
+.progress-bar {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ color: @progress-bar-color;
+ text-align: center;
+ white-space: nowrap;
+ background-color: @progress-bar-bg;
+ #transition(@progress-bar-transition);
+}
+
+.progress-bar-striped {
+ #gradient-striped();
+ background-size: @progress-height @progress-height;
+}
+
+.progress-bar-animated {
+ animation: progress-bar-stripes @progress-bar-animation-timing;
+}
diff --git a/include/thirdparty/Bootstrap4/less/_reboot.less b/include/thirdparty/Bootstrap4/less/_reboot.less
new file mode 100644
index 0000000..2700796
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_reboot.less
@@ -0,0 +1,475 @@
+// Reboot
+//
+// Normalization of HTML elements, manually forked from Normalize.css to remove
+// styles targeting irrelevant browsers while applying new styles.
+//
+// Normalize is licensed MIT. https://github.com/necolas/normalize.css
+
+
+// Document
+//
+// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
+// 2. Change the default font family in all browsers.
+// 3. Correct the line height in all browsers.
+// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.
+// 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so
+// we force a non-overlapping, non-auto-hiding scrollbar to counteract.
+// 6. Change the default tap highlight to be completely transparent in iOS.
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box; // 1
+}
+
+html {
+ font-family: sans-serif; // 2
+ line-height: 1.15; // 3
+ -webkit-text-size-adjust: 100%; // 4
+ -ms-text-size-adjust: 100%; // 4
+ -ms-overflow-style: scrollbar; // 5
+ // LESS PORT: Sass version uses `fade(@black, 0%)` here but it ends up compiling to `transparent`.
+ -webkit-tap-highlight-color: transparent; // 6
+}
+
+// IE10+ doesn't honor `` in some cases.
+// LESS PORT: Less has no `@at-root` equivalent, so omitting that here.
+@-ms-viewport {
+ width: device-width;
+}
+
+// Shim for "new" HTML5 structural elements to display correctly (IE10, older browsers)
+article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
+ display: block;
+}
+
+// Body
+//
+// 1. Remove the margin in all browsers.
+// 2. As a best practice, apply a default `background-color`.
+// 3. Set an explicit initial text-align value so that we can later use the
+// the `inherit` value on things like `` elements.
+
+body {
+ margin: 0; // 1
+ font-family: @font-family-base;
+ font-size: @font-size-base;
+ font-weight: @font-weight-base;
+ line-height: @line-height-base;
+ color: @body-color;
+ text-align: left; // 3
+ background-color: @body-bg; // 2
+}
+
+// Suppress the focus outline on elements that cannot be accessed via keyboard.
+// This prevents an unwanted focus outline from appearing around elements that
+// might still respond to pointer events.
+//
+// Credit: https://github.com/suitcss/base
+[tabindex="-1"]:focus {
+ outline: 0 !important;
+}
+
+
+// Content grouping
+//
+// 1. Add the correct box sizing in Firefox.
+// 2. Show the overflow in Edge and IE.
+
+hr {
+ box-sizing: content-box; // 1
+ height: 0; // 1
+ overflow: visible; // 2
+}
+
+
+//
+// Typography
+//
+
+// Remove top margins from headings
+//
+// By default, ``-`` all receive top and bottom margins. We nuke the top
+// margin for easier control within type scales as it avoids margin collapsing.
+h1, h2, h3, h4, h5, h6 {
+ margin-top: 0;
+ margin-bottom: @headings-margin-bottom;
+}
+
+// Reset margins on paragraphs
+//
+// Similarly, the top margin on ``s get reset. However, we also reset the
+// bottom margin to use `rem` units instead of `em`.
+p {
+ margin-top: 0;
+ margin-bottom: @paragraph-margin-bottom;
+}
+
+// Abbreviations
+//
+// 1. Remove the bottom border in Firefox 39-.
+// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+// 3. Add explicit cursor to indicate changed behavior.
+// 4. Duplicate behavior to the data-* attribute for our tooltip plugin
+
+abbr[title],
+abbr[data-original-title] { // 4
+ text-decoration: underline; // 2
+ text-decoration: underline dotted; // 2
+ cursor: help; // 3
+ border-bottom: 0; // 1
+}
+
+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: @dt-font-weight;
+}
+
+dd {
+ margin-bottom: 0.5rem;
+ margin-left: 0; // Undo browser default
+}
+
+blockquote {
+ margin: 0 0 1rem;
+}
+
+dfn {
+ font-style: italic; // Add the correct font style in Android 4.3-
+}
+
+b,
+strong {
+ font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari
+}
+
+small {
+ font-size: 80%; // Add the correct font size in all browsers
+}
+
+//
+// Prevent `sub` and `sup` elements from affecting the line height in
+// all browsers.
+//
+
+sub,
+sup {
+ position: relative;
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline;
+}
+
+sub { bottom: -0.25em; }
+sup { top: -0.5em; }
+
+
+//
+// Links
+//
+
+a {
+ color: @link-color;
+ text-decoration: @link-decoration;
+ background-color: transparent; // Remove the gray background on active links in IE 10.
+ -webkit-text-decoration-skip: objects; // Remove gaps in links underline in iOS 8+ and Safari 8+.
+
+ #hover({
+ color: @link-hover-color;
+ text-decoration: @link-hover-decoration;
+ });
+}
+
+// And undo these styles for placeholder links/named anchors (without href)
+// which have not been made explicitly keyboard-focusable (without tabindex).
+// It would be more straightforward to just use a[href] in previous block, but that
+// causes specificity issues in many other styles that are too complex to fix.
+// See https://github.com/twbs/bootstrap/issues/19402
+
+a:not([href]):not([tabindex]) {
+ color: inherit;
+ text-decoration: none;
+
+ #hover-focus({
+ color: inherit;
+ text-decoration: none;
+ });
+
+ &:focus {
+ outline: 0;
+ }
+}
+
+
+//
+// Code
+//
+
+pre,
+code,
+kbd,
+samp {
+ font-family: @font-family-monospace; // Correct the inheritance and scaling of font size in all browsers.
+ font-size: 1em; // Correct the odd `em` font sizing in all browsers.
+}
+
+pre {
+ // Remove browser default top margin
+ margin-top: 0;
+ // Reset browser default of `1em` to use `rem`s
+ margin-bottom: 1rem;
+ // Don't allow content to break outside
+ overflow: auto;
+ // We have @viewport set which causes scrollbars to overlap content in IE11 and Edge, so
+ // we force a non-overlapping, non-auto-hiding scrollbar to counteract.
+ -ms-overflow-style: scrollbar;
+}
+
+
+//
+// Figures
+//
+
+figure {
+ // Apply a consistent margin strategy (matches our type styles).
+ margin: 0 0 1rem;
+}
+
+
+//
+// Images and content
+//
+
+img {
+ vertical-align: middle;
+ border-style: none; // Remove the border on images inside links in IE 10-.
+}
+
+svg {
+ // Workaround for the SVG overflow bug in IE10/11 is still required.
+ // See https://github.com/twbs/bootstrap/issues/26878
+ overflow: hidden;
+ vertical-align: middle;
+}
+
+
+//
+// Tables
+//
+
+table {
+ border-collapse: collapse; // Prevent double borders
+}
+
+caption {
+ padding-top: @table-cell-padding;
+ padding-bottom: @table-cell-padding;
+ color: @table-caption-color;
+ text-align: left;
+ caption-side: bottom;
+}
+
+th {
+ // Matches default ` | ` alignment by inheriting from the ``, or the
+ // closest parent with a set `text-align`.
+ text-align: inherit;
+}
+
+
+//
+// Forms
+//
+
+label {
+ // Allow labels to use `margin` for spacing.
+ display: inline-block;
+ margin-bottom: @label-margin-bottom;
+}
+
+// Remove the default `border-radius` that macOS Chrome adds.
+//
+// Details at https://github.com/twbs/bootstrap/issues/24093
+button {
+ border-radius: 0;
+}
+
+// Work around a Firefox/IE bug where the transparent `button` background
+// results in a loss of the default `button` focus styles.
+//
+// Credit: https://github.com/suitcss/base/
+button:focus {
+ outline: 1px dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+}
+
+input,
+button,
+select,
+optgroup,
+textarea {
+ margin: 0; // Remove the margin in Firefox and Safari
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+
+button,
+input {
+ overflow: visible; // Show the overflow in Edge
+}
+
+button,
+select {
+ text-transform: none; // Remove the inheritance of text transform in Firefox
+}
+
+// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
+// controls in Android 4.
+// 2. Correct the inability to style clickable types in iOS and Safari.
+button,
+html [type="button"], // 1
+[type="reset"],
+[type="submit"] {
+ -webkit-appearance: button; // 2
+}
+
+// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.
+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"] {
+ box-sizing: border-box; // 1. Add the correct box sizing in IE 10-
+ padding: 0; // 2. Remove the padding in IE 10-
+}
+
+
+input[type="date"],
+input[type="time"],
+input[type="datetime-local"],
+input[type="month"] {
+ // Remove the default appearance of temporal inputs to avoid a Mobile Safari
+ // bug where setting a custom line-height prevents text from being vertically
+ // centered within the input.
+ // See https://bugs.webkit.org/show_bug.cgi?id=139848
+ // and https://github.com/twbs/bootstrap/issues/11266
+ -webkit-appearance: listbox;
+}
+
+textarea {
+ overflow: auto; // Remove the default vertical scrollbar in IE.
+ // Textareas should really only resize vertically so they don't break their (horizontal) containers.
+ resize: vertical;
+}
+
+fieldset {
+ // Browsers set a default `min-width: min-content;` on fieldsets,
+ // unlike e.g. ` `s, which have `min-width: 0;` by default.
+ // So we reset that to ensure fieldsets behave more like a standard block element.
+ // See https://github.com/twbs/bootstrap/issues/12359
+ // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements
+ min-width: 0;
+ // Reset the default outline behavior of fieldsets so they don't affect page layout.
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+// 1. Correct the text wrapping in Edge and IE.
+// 2. Correct the color inheritance from `fieldset` elements in IE.
+legend {
+ display: block;
+ width: 100%;
+ max-width: 100%; // 1
+ padding: 0;
+ margin-bottom: 0.5rem;
+ font-size: 1.5rem;
+ line-height: inherit;
+ color: inherit; // 2
+ white-space: normal; // 1
+}
+
+progress {
+ vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.
+}
+
+// Correct the cursor style of increment and decrement buttons in Chrome.
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+[type="search"] {
+ // This overrides the extra rounded corners on search inputs in iOS so that our
+ // `.form-control` class can properly style them. Note that this cannot simply
+ // be added to `.form-control` as it's not specific enough. For details, see
+ // https://github.com/twbs/bootstrap/issues/11586.
+ outline-offset: -2px; // 2. Correct the outline style in Safari.
+ -webkit-appearance: none;
+}
+
+//
+// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
+//
+
+[type="search"]::-webkit-search-cancel-button,
+[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+//
+// 1. Correct the inability to style clickable types in iOS and Safari.
+// 2. Change font properties to `inherit` in Safari.
+//
+
+::-webkit-file-upload-button {
+ font: inherit; // 2
+ -webkit-appearance: button; // 1
+}
+
+//
+// Correct element displays
+//
+
+output {
+ display: inline-block;
+}
+
+summary {
+ display: list-item; // Add the correct display in all browsers
+ cursor: pointer;
+}
+
+template {
+ display: none; // Add the correct display in IE
+}
+
+// Always hide an element with the `hidden` HTML attribute (from PureCSS).
+// Needed for proper display in IE 10-.
+[hidden] {
+ display: none !important;
+}
diff --git a/include/thirdparty/Bootstrap4/less/_root.less b/include/thirdparty/Bootstrap4/less/_root.less
new file mode 100644
index 0000000..53ee819
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_root.less
@@ -0,0 +1,48 @@
+:root {
+ //// Custom variable values only support SassScript inside `#{}`.
+ //@each $color, $value in $colors {
+ // --#{$color}: #{$value};
+ //}
+ #each-color-css-var(@i: 1) when (@i =< length(@colors)) {
+ @item: extract(@colors, @i);
+ @color: extract(@item, 1);
+ @value: extract(@item, 2);
+
+ --@{color}: @value;
+
+ #each-color-css-var((@i + 1));
+ } #each-color-css-var();
+
+ //@each $color, $value in $theme-colors {
+ // --#{$color}: #{$value};
+ //}
+ #each-theme-color-css-var(@i: 1) when (@i =< length(@theme-colors)) {
+ @item: extract(@theme-colors, @i);
+ @color: extract(@item, 1);
+ @value: extract(@item, 2);
+
+ --@{color}: @value;
+
+ #each-theme-color-css-var((@i + 1));
+ } #each-theme-color-css-var();
+
+ //@each $bp, $value in $grid-breakpoints {
+ // --breakpoint-#{$bp}: #{$value};
+ //}
+ #each-breakpoint-css-var(@i: 1) when (@i =< length(@grid-breakpoints)) {
+ @item: extract(@grid-breakpoints, @i);
+ @bp: extract(@item, 1);
+ @value: extract(@item, 2);
+
+ --breakpoint-@{bp}: @value;
+
+ #each-breakpoint-css-var((@i + 1));
+ } #each-breakpoint-css-var();
+
+ //// Use `inspect` for lists so that quoted items keep the quotes.
+ //// See https://github.com/sass/sass/issues/2383#issuecomment-336349172
+ //--font-family-sans-serif: #{inspect($font-family-sans-serif)};
+ //--font-family-monospace: #{inspect($font-family-monospace)};
+ --font-family-sans-serif: @font-family-sans-serif;
+ --font-family-monospace: @font-family-monospace;
+}
diff --git a/include/thirdparty/Bootstrap4/less/_tables.less b/include/thirdparty/Bootstrap4/less/_tables.less
new file mode 100644
index 0000000..8b09723
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_tables.less
@@ -0,0 +1,218 @@
+//
+// Basic Bootstrap table
+//
+
+.table {
+ width: 100%;
+ margin-bottom: @spacer;
+ background-color: @table-bg; // Reset for nesting within parents with `background-color`.
+
+ th,
+ td {
+ padding: @table-cell-padding;
+ vertical-align: top;
+ border-top: @table-border-width solid @table-border-color;
+ }
+
+ thead th {
+ vertical-align: bottom;
+ border-bottom: (2 * @table-border-width) solid @table-border-color;
+ }
+
+ tbody + tbody {
+ border-top: (2 * @table-border-width) solid @table-border-color;
+ }
+
+ .table {
+ background-color: @body-bg;
+ }
+}
+
+
+//
+// Condensed table w/ half padding
+//
+
+.table-sm {
+ th,
+ td {
+ padding: @table-cell-padding-sm;
+ }
+}
+
+
+// Border versions
+//
+// Add or remove borders all around the table and between all the columns.
+
+.table-bordered {
+ border: @table-border-width solid @table-border-color;
+
+ th,
+ td {
+ border: @table-border-width solid @table-border-color;
+ }
+
+ thead {
+ th,
+ td {
+ border-bottom-width: (2 * @table-border-width);
+ }
+ }
+}
+
+.table-borderless {
+ th,
+ td,
+ thead th,
+ tbody + tbody {
+ border: 0;
+ }
+}
+
+
+// Zebra-striping
+//
+// Default zebra-stripe styles (alternating gray and transparent backgrounds)
+
+.table-striped {
+ tbody tr:nth-of-type(@{table-striped-order}) {
+ background-color: @table-accent-bg;
+ }
+}
+
+
+// Hover effect
+//
+// Placed here since it has to come after the potential zebra striping
+
+.table-hover {
+ tbody tr {
+ #hover({
+ background-color: @table-hover-bg;
+ });
+ }
+}
+
+
+// Table backgrounds
+//
+// Exact selectors below required to override `.table-striped` and prevent
+// inheritance to nested tables.
+
+//@each $color, $value in $theme-colors {
+// @include table-row-variant($color, theme-color-level($color, -9));
+//}
+#each-theme-color-table-row(@i: 1) when (@i =< length(@theme-colors)) {
+ @item: extract(@theme-colors, @i);
+ @color: extract(@item, 1);
+ @colorValue: extract(@item, 2);
+
+ #table-row-variant(@color, theme-color-level(@color, -9));
+
+ #each-theme-color-table-row((@i + 1));
+} #each-theme-color-table-row();
+
+#table-row-variant(active, @table-active-bg);
+
+
+// Dark styles
+//
+// Same table markup, but inverted color scheme: dark background and light text.
+
+.table {
+ .thead-dark {
+ th {
+ color: @table-dark-color;
+ background-color: @table-dark-bg;
+ border-color: @table-dark-border-color;
+ }
+ }
+
+ .thead-light {
+ th {
+ color: @table-head-color;
+ background-color: @table-head-bg;
+ border-color: @table-border-color;
+ }
+ }
+}
+
+.table-dark {
+ color: @table-dark-color;
+ background-color: @table-dark-bg;
+
+ th,
+ td,
+ thead th {
+ border-color: @table-dark-border-color;
+ }
+
+ &.table-bordered {
+ border: 0;
+ }
+
+ &.table-striped {
+ tbody tr:nth-of-type(odd) {
+ background-color: @table-dark-accent-bg;
+ }
+ }
+
+ &.table-hover {
+ tbody tr {
+ #hover({
+ background-color: @table-dark-hover-bg;
+ });
+ }
+ }
+}
+
+
+// Responsive tables
+//
+// Generate series of `.table-responsive-*` classes for configuring the screen
+// size of where your table will overflow.
+
+.table-responsive {
+ //@each $breakpoint in map-keys($grid-breakpoints) {
+ // $next: breakpoint-next($breakpoint, $grid-breakpoints);
+ // $infix: breakpoint-infix($next, $grid-breakpoints);
+ //
+ // {$infix} {
+ // @include media-breakpoint-down($breakpoint) {
+ // display: block;
+ // width: 100%;
+ // overflow-x: auto;
+ // -webkit-overflow-scrolling: touch;
+ // -ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057
+ //
+ // // Prevent double border on horizontal scroll due to use of `display: block;`
+ // &.table-bordered {
+ // border: 0;
+ // }
+ // }
+ // }
+ //}
+ #each-breakpoint-responsive-table(@i: 1) when (@i =< length(@grid-breakpoints)) {
+ @breakpoint: extract(extract(@grid-breakpoints, @i), 1);
+ @next: breakpoint-next(@breakpoint, @grid-breakpoints);
+ @infix: breakpoint-infix(@next, @grid-breakpoints);
+
+ &@{infix} {
+ #media-breakpoint-down(@breakpoint, {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ -ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057
+
+ // Prevent double border on horizontal scroll due to use of `display: block;`
+ > .table-bordered {
+ border: 0;
+ }
+ });
+ }
+
+ #each-breakpoint-responsive-table((@i + 1));
+ } #each-breakpoint-responsive-table();
+}
diff --git a/include/thirdparty/Bootstrap4/less/_tooltip.less b/include/thirdparty/Bootstrap4/less/_tooltip.less
new file mode 100644
index 0000000..c79c7bb
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_tooltip.less
@@ -0,0 +1,115 @@
+// Base class
+.tooltip {
+ position: absolute;
+ z-index: @zindex-tooltip;
+ display: block;
+ margin: @tooltip-margin;
+ // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
+ // So reset our font and text properties to avoid inheriting weird values.
+ #reset-text();
+ font-size: @tooltip-font-size;
+ // Allow breaking very long words so they don't overflow the tooltip's bounds
+ word-wrap: break-word;
+ opacity: 0;
+
+ &.show { opacity: @tooltip-opacity; }
+
+ .arrow {
+ position: absolute;
+ display: block;
+ width: @tooltip-arrow-width;
+ height: @tooltip-arrow-height;
+
+ &::before {
+ position: absolute;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+ }
+ }
+}
+
+.bs-tooltip-top {
+ padding: @tooltip-arrow-height 0;
+
+ .arrow {
+ bottom: 0;
+
+ &::before {
+ top: 0;
+ border-width: @tooltip-arrow-height (@tooltip-arrow-width / 2) 0;
+ border-top-color: @tooltip-arrow-color;
+ }
+ }
+}
+
+.bs-tooltip-right {
+ padding: 0 @tooltip-arrow-height;
+
+ .arrow {
+ left: 0;
+ width: @tooltip-arrow-height;
+ height: @tooltip-arrow-width;
+
+ &::before {
+ right: 0;
+ border-width: (@tooltip-arrow-width / 2) @tooltip-arrow-height (@tooltip-arrow-width / 2) 0;
+ border-right-color: @tooltip-arrow-color;
+ }
+ }
+}
+
+.bs-tooltip-bottom {
+ padding: @tooltip-arrow-height 0;
+
+ .arrow {
+ top: 0;
+
+ &::before {
+ bottom: 0;
+ border-width: 0 (@tooltip-arrow-width / 2) @tooltip-arrow-height;
+ border-bottom-color: @tooltip-arrow-color;
+ }
+ }
+}
+
+.bs-tooltip-left {
+ padding: 0 @tooltip-arrow-height;
+
+ .arrow {
+ right: 0;
+ width: @tooltip-arrow-height;
+ height: @tooltip-arrow-width;
+
+ &::before {
+ left: 0;
+ border-width: (@tooltip-arrow-width / 2) 0 (@tooltip-arrow-width / 2) @tooltip-arrow-height;
+ border-left-color: @tooltip-arrow-color;
+ }
+ }
+}
+
+.bs-tooltip-auto {
+ &[x-placement^="top"] {
+ &:extend(.bs-tooltip-top all);
+ }
+ &[x-placement^="right"] {
+ &:extend(.bs-tooltip-right all);
+ }
+ &[x-placement^="bottom"] {
+ &:extend(.bs-tooltip-bottom all);
+ }
+ &[x-placement^="left"] {
+ &:extend(.bs-tooltip-left all);
+ }
+}
+
+// Wrapper for the tooltip content
+.tooltip-inner {
+ max-width: @tooltip-max-width;
+ padding: @tooltip-padding-y @tooltip-padding-x;
+ color: @tooltip-color;
+ text-align: center;
+ background-color: @tooltip-bg;
+ #border-radius(@tooltip-border-radius);
+}
diff --git a/include/thirdparty/Bootstrap4/less/_transitions.less b/include/thirdparty/Bootstrap4/less/_transitions.less
new file mode 100644
index 0000000..ca74512
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_transitions.less
@@ -0,0 +1,20 @@
+.fade {
+ #transition(@transition-fade);
+
+ &:not(.show) {
+ opacity: 0;
+ }
+}
+
+.collapse {
+ &:not(.show) {
+ display: none;
+ }
+}
+
+.collapsing {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ #transition(@transition-collapse);
+}
diff --git a/include/thirdparty/Bootstrap4/less/_type.less b/include/thirdparty/Bootstrap4/less/_type.less
new file mode 100644
index 0000000..2eafc05
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_type.less
@@ -0,0 +1,123 @@
+//
+// Headings
+//
+
+h1, h2, h3, h4, h5, h6,
+.h1, .h2, .h3, .h4, .h5, .h6 {
+ margin-bottom: @headings-margin-bottom;
+ font-family: @headings-font-family;
+ font-weight: @headings-font-weight;
+ line-height: @headings-line-height;
+ color: @headings-color;
+}
+
+h1, .h1 { font-size: @h1-font-size; }
+h2, .h2 { font-size: @h2-font-size; }
+h3, .h3 { font-size: @h3-font-size; }
+h4, .h4 { font-size: @h4-font-size; }
+h5, .h5 { font-size: @h5-font-size; }
+h6, .h6 { font-size: @h6-font-size; }
+
+.lead {
+ font-size: @lead-font-size;
+ font-weight: @lead-font-weight;
+}
+
+// Type display classes
+.display-1 {
+ font-size: @display1-size;
+ font-weight: @display1-weight;
+ line-height: @display-line-height;
+}
+.display-2 {
+ font-size: @display2-size;
+ font-weight: @display2-weight;
+ line-height: @display-line-height;
+}
+.display-3 {
+ font-size: @display3-size;
+ font-weight: @display3-weight;
+ line-height: @display-line-height;
+}
+.display-4 {
+ font-size: @display4-size;
+ font-weight: @display4-weight;
+ line-height: @display-line-height;
+}
+
+
+//
+// Horizontal rules
+//
+
+hr {
+ margin-top: @hr-margin-y;
+ margin-bottom: @hr-margin-y;
+ border: 0;
+ border-top: @hr-border-width solid @hr-border-color;
+}
+
+
+//
+// Emphasis
+//
+
+small,
+.small {
+ font-size: @small-font-size;
+ font-weight: @font-weight-normal;
+}
+
+mark,
+.mark {
+ padding: @mark-padding;
+ background-color: @mark-bg;
+}
+
+
+//
+// Lists
+//
+
+.list-unstyled {
+ #list-unstyled();
+}
+
+// Inline turns list items into inline-block
+.list-inline {
+ #list-unstyled();
+}
+.list-inline-item {
+ display: inline-block;
+
+ &:not(:last-child) {
+ margin-right: @list-inline-padding;
+ }
+}
+
+
+//
+// Misc
+//
+
+// Builds on `abbr`
+.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
+}
+
+// Blockquotes
+.blockquote {
+ margin-bottom: @spacer;
+ font-size: @blockquote-font-size;
+}
+
+.blockquote-footer {
+ display: block;
+ font-size: 80%; // back to default font-size
+ color: @blockquote-small-color;
+
+ &::before {
+ content: "\2014 \00A0"; // em dash, nbsp
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/_utilities.less b/include/thirdparty/Bootstrap4/less/_utilities.less
new file mode 100644
index 0000000..8e876a6
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_utilities.less
@@ -0,0 +1,15 @@
+@import "utilities/_align";
+@import "utilities/_background";
+@import "utilities/_borders";
+@import "utilities/_clearfix";
+@import "utilities/_display";
+@import "utilities/_embed";
+@import "utilities/_flex";
+@import "utilities/_float";
+@import "utilities/_position";
+@import "utilities/_screenreaders";
+@import "utilities/_shadows";
+@import "utilities/_sizing";
+@import "utilities/_spacing";
+@import "utilities/_text";
+@import "utilities/_visibility";
diff --git a/include/thirdparty/Bootstrap4/less/_variables.less b/include/thirdparty/Bootstrap4/less/_variables.less
new file mode 100644
index 0000000..b34832a
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/_variables.less
@@ -0,0 +1,917 @@
+// Variables
+//
+// Variables should follow the `@component-state-property-size` formula for
+// consistent naming. Ex: @nav-link-disabled-color and @modal-content-box-shadow-xs.
+
+
+//
+// Color system
+//
+
+@white: #fff;
+@gray-100: #f8f9fa;
+@gray-200: #e9ecef;
+@gray-300: #dee2e6;
+@gray-400: #ced4da;
+@gray-500: #adb5bd;
+@gray-600: #6c757d;
+@gray-700: #495057;
+@gray-800: #343a40;
+@gray-900: #212529;
+@black: #000;
+
+@grays:
+ 100 @gray-100,
+ 200 @gray-200,
+ 300 @gray-300,
+ 400 @gray-400,
+ 500 @gray-500,
+ 600 @gray-600,
+ 700 @gray-700,
+ 800 @gray-800,
+ 900 @gray-900;
+
+@blue: #007bff;
+@indigo: #6610f2;
+@purple: #6f42c1;
+@pink: #e83e8c;
+@red: #dc3545;
+@orange: #fd7e14;
+@yellow: #ffc107;
+@green: #28a745;
+@teal: #20c997;
+@cyan: #17a2b8;
+
+@colors:
+ blue @blue,
+ indigo @indigo,
+ purple @purple,
+ pink @pink,
+ red @red,
+ orange @orange,
+ yellow @yellow,
+ green @green,
+ teal @teal,
+ cyan @cyan,
+ white @white,
+ gray @gray-600,
+ gray-dark @gray-800;
+
+@primary: @blue;
+@secondary: @gray-600;
+@success: @green;
+@info: @cyan;
+@warning: @yellow;
+@danger: @red;
+@light: @gray-100;
+@dark: @gray-800;
+
+@theme-colors:
+ primary @primary,
+ secondary @secondary,
+ success @success,
+ info @info,
+ warning @warning,
+ danger @danger,
+ light @light,
+ dark @dark;
+
+// Set a specific jump point for requesting color jumps
+@theme-color-interval: 8%;
+
+// The yiq lightness value that determines when the lightness of color changes from "dark" to "light". Acceptable values are between 0 and 255.
+@yiq-contrasted-threshold: 150;
+
+// Customize the light and dark text colors for use in our YIQ color contrast function.
+@yiq-text-dark: @gray-900;
+@yiq-text-light: @white;
+
+// Options
+//
+// Quickly modify global styling by enabling or disabling optional features.
+
+@enable-caret: true;
+@enable-rounded: true;
+@enable-shadows: false;
+@enable-gradients: false;
+@enable-transitions: true;
+@enable-hover-media-query: false; // Deprecated, no longer affects any compiled CSS
+@enable-grid-classes: true;
+@enable-print-styles: true;
+
+
+// Spacing
+//
+// Control the default styling of most Bootstrap elements by modifying these
+// variables. Mostly focused on spacing.
+// You can add more entries to the @spacers map, should you need more variation.
+
+@spacer: 1rem;
+@spacers:
+ 0 0,
+ 1 (@spacer * 0.25),
+ 2 (@spacer * 0.5),
+ 3 @spacer,
+ 4 (@spacer * 1.5),
+ 5 (@spacer * 3);
+
+// This variable affects the `.h-*` and `.w-*` classes.
+@sizes:
+ 25 25%,
+ 50 50%,
+ 75 75%,
+ 100 100%,
+ auto auto;
+
+// Body
+//
+// Settings for the `` element.
+
+@body-bg: @white;
+@body-color: @gray-900;
+
+// Links
+//
+// Style anchor elements.
+
+@link-color: theme-color(primary);
+@link-decoration: none;
+@link-hover-color: darken(@link-color, 15%);
+@link-hover-decoration: underline;
+
+// Paragraphs
+//
+// Style p element.
+
+@paragraph-margin-bottom: 1rem;
+
+
+// Grid breakpoints
+//
+// Define the minimum dimensions at which your layout will change,
+// adapting to different screen sizes, for use in media queries.
+
+@grid-breakpoints:
+ xs 0,
+ sm 576px,
+ md 768px,
+ lg 992px,
+ xl 1200px;
+
+//@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
+//@include _assert-starts-at-zero($grid-breakpoints);
+
+
+// Grid containers
+//
+// Define the maximum width of `.container` for different screen sizes.
+
+@container-max-widths:
+ sm 540px,
+ md 720px,
+ lg 960px,
+ xl 1140px;
+
+//@include _assert-ascending($container-max-widths, "$container-max-widths");
+
+
+// Grid columns
+//
+// Set the number of columns and specify the width of the gutters.
+
+@grid-columns: 12;
+@grid-gutter-width: 30px;
+
+// Components
+//
+// Define common padding and border radius sizes and more.
+
+@line-height-lg: 1.5;
+@line-height-sm: 1.5;
+
+@border-width: 1px;
+@border-color: @gray-300;
+
+@border-radius: 0.25rem;
+@border-radius-lg: 0.3rem;
+@border-radius-sm: 0.2rem;
+
+@box-shadow-sm: 0 0.125rem 0.25rem fade(@black, 7.5%);
+@box-shadow: 0 0.5rem 1rem fade(@black, 15%);
+@box-shadow-lg: 0 1rem 3rem fade(@black, 17.5%);
+
+@component-active-color: @white;
+@component-active-bg: theme-color(primary);
+
+@caret-width: 0.3em;
+
+@transition-base: all 0.2s ease-in-out;
+@transition-fade: opacity 0.15s linear;
+@transition-collapse: height 0.35s ease;
+
+
+// Fonts
+//
+// Font, line-height, and color for body text, headings, and more.
+
+@font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+@font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+@font-family-base: @font-family-sans-serif;
+
+@font-size-base: 1rem; // Assumes the browser default, typically `16px`
+@font-size-lg: (@font-size-base * 1.25);
+@font-size-sm: (@font-size-base * 0.875);
+
+@font-weight-light: 300;
+@font-weight-normal: 400;
+@font-weight-bold: 700;
+
+@font-weight-base: @font-weight-normal;
+@line-height-base: 1.5;
+
+@h1-font-size: (@font-size-base * 2.5);
+@h2-font-size: (@font-size-base * 2);
+@h3-font-size: (@font-size-base * 1.75);
+@h4-font-size: (@font-size-base * 1.5);
+@h5-font-size: (@font-size-base * 1.25);
+@h6-font-size: @font-size-base;
+
+@headings-margin-bottom: (@spacer / 2);
+@headings-font-family: inherit;
+@headings-font-weight: 500;
+@headings-line-height: 1.2;
+@headings-color: inherit;
+
+@display1-size: 6rem;
+@display2-size: 5.5rem;
+@display3-size: 4.5rem;
+@display4-size: 3.5rem;
+
+@display1-weight: 300;
+@display2-weight: 300;
+@display3-weight: 300;
+@display4-weight: 300;
+@display-line-height: @headings-line-height;
+
+@lead-font-size: (@font-size-base * 1.25);
+@lead-font-weight: 300;
+
+@small-font-size: 80%;
+
+@text-muted: @gray-600;
+
+@blockquote-small-color: @gray-600;
+@blockquote-font-size: (@font-size-base * 1.25);
+
+@hr-border-color: fade(@black, 10%);
+@hr-border-width: @border-width;
+
+@mark-padding: 0.2em;
+
+@dt-font-weight: @font-weight-bold;
+
+@kbd-box-shadow: inset 0 -0.1rem 0 fade(@black, 25%);
+@nested-kbd-font-weight: @font-weight-bold;
+
+@list-inline-padding: 0.5rem;
+
+@mark-bg: #fcf8e3;
+
+@hr-margin-y: @spacer;
+
+
+// Tables
+//
+// Customizes the `.table` component with basic values, each used across all table variations.
+
+@table-cell-padding: 0.75rem;
+@table-cell-padding-sm: 0.3rem;
+
+@table-bg: transparent;
+@table-accent-bg: fade(@black, 5%);
+@table-hover-bg: fade(@black, 7.5%);
+@table-active-bg: @table-hover-bg;
+
+@table-border-width: @border-width;
+@table-border-color: @gray-300;
+
+@table-head-bg: @gray-200;
+@table-head-color: @gray-700;
+
+@table-dark-bg: @gray-900;
+@table-dark-accent-bg: fade(@white, 5%);
+@table-dark-hover-bg: fade(@white, 7.5%);
+@table-dark-border-color: lighten(@gray-900, 7.5%);
+@table-dark-color: @body-bg;
+
+@table-striped-order: odd;
+
+@table-caption-color: @text-muted;
+
+
+// Buttons + Forms
+//
+// Shared variables that are reassigned to `@input-` and `@btn-` specific variables.
+
+@input-btn-padding-y: 0.375rem;
+@input-btn-padding-x: 0.75rem;
+@input-btn-line-height: @line-height-base;
+
+@input-btn-focus-width: 0.2rem;
+@input-btn-focus-color: fade(@component-active-bg, 25%);
+@input-btn-focus-box-shadow: 0 0 0 @input-btn-focus-width @input-btn-focus-color;
+
+@input-btn-padding-y-sm: 0.25rem;
+@input-btn-padding-x-sm: 0.5rem;
+@input-btn-line-height-sm: @line-height-sm;
+
+@input-btn-padding-y-lg: 0.5rem;
+@input-btn-padding-x-lg: 1rem;
+@input-btn-line-height-lg: @line-height-lg;
+
+@input-btn-border-width: @border-width;
+
+
+// Buttons
+//
+// For each of Bootstrap's buttons, define text, background, and border color.
+
+@btn-padding-y: @input-btn-padding-y;
+@btn-padding-x: @input-btn-padding-x;
+@btn-line-height: @input-btn-line-height;
+
+@btn-padding-y-sm: @input-btn-padding-y-sm;
+@btn-padding-x-sm: @input-btn-padding-x-sm;
+@btn-line-height-sm: @input-btn-line-height-sm;
+
+@btn-padding-y-lg: @input-btn-padding-y-lg;
+@btn-padding-x-lg: @input-btn-padding-x-lg;
+@btn-line-height-lg: @input-btn-line-height-lg;
+
+@btn-border-width: @input-btn-border-width;
+
+@btn-font-weight: @font-weight-normal;
+@btn-box-shadow: inset 0 1px 0 fade(@white, 15%), 0 1px 1px fade(@black, 7.5%);
+@btn-focus-width: @input-btn-focus-width;
+@btn-focus-box-shadow: @input-btn-focus-box-shadow;
+@btn-disabled-opacity: 0.65;
+@btn-active-box-shadow: inset 0 3px 5px fade(@black, 12.5%);
+
+@btn-link-disabled-color: @gray-600;
+
+@btn-block-spacing-y: 0.5rem;
+
+// Allows for customizing button radius independently from global border radius
+@btn-border-radius: @border-radius;
+@btn-border-radius-lg: @border-radius-lg;
+@btn-border-radius-sm: @border-radius-sm;
+
+@btn-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;
+
+
+// Forms
+
+@label-margin-bottom: 0.5rem;
+
+@input-padding-y: @input-btn-padding-y;
+@input-padding-x: @input-btn-padding-x;
+@input-line-height: @input-btn-line-height;
+
+@input-padding-y-sm: @input-btn-padding-y-sm;
+@input-padding-x-sm: @input-btn-padding-x-sm;
+@input-line-height-sm: @input-btn-line-height-sm;
+
+@input-padding-y-lg: @input-btn-padding-y-lg;
+@input-padding-x-lg: @input-btn-padding-x-lg;
+@input-line-height-lg: @input-btn-line-height-lg;
+
+@input-bg: @white;
+@input-disabled-bg: @gray-200;
+
+@input-color: @gray-700;
+@input-border-color: @gray-400;
+@input-border-width: @input-btn-border-width;
+@input-box-shadow: inset 0 1px 1px fade(@black, 7.5%);
+
+@input-border-radius: @border-radius;
+@input-border-radius-lg: @border-radius-lg;
+@input-border-radius-sm: @border-radius-sm;
+
+@input-focus-bg: @input-bg;
+@input-focus-border-color: lighten(@component-active-bg, 25%);
+@input-focus-color: @input-color;
+@input-focus-width: @input-btn-focus-width;
+@input-focus-box-shadow: @input-btn-focus-box-shadow;
+
+@input-placeholder-color: @gray-600;
+@input-plaintext-color: @body-color;
+
+@input-height-border: (@input-border-width * 2);
+
+@input-height-inner: (@font-size-base * @input-btn-line-height + @input-btn-padding-y * 2);
+@input-height: ~"calc(@{input-height-inner} + @{input-height-border})";
+
+@input-height-inner-sm: (@font-size-sm * @input-btn-line-height-sm + @input-btn-padding-y-sm * 2);
+@input-height-sm: ~"calc(@{input-height-inner-sm} + @{input-height-border})";
+
+@input-height-inner-lg: (@font-size-lg * @input-btn-line-height-lg + @input-btn-padding-y-lg * 2);
+@input-height-lg: ~"calc(@{input-height-inner-lg} + @{input-height-border})";
+
+@input-transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+
+@form-text-margin-top: 0.25rem;
+
+@form-check-input-gutter: 1.25rem;
+@form-check-input-margin-y: 0.3rem;
+@form-check-input-margin-x: 0.25rem;
+
+@form-check-inline-margin-x: 0.75rem;
+@form-check-inline-input-margin-x: 0.3125rem;
+
+@form-group-margin-bottom: 1rem;
+
+@input-group-addon-color: @input-color;
+@input-group-addon-bg: @gray-200;
+@input-group-addon-border-color: @input-border-color;
+
+@custom-forms-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+
+@custom-control-gutter: 1.5rem;
+@custom-control-spacer-x: 1rem;
+
+@custom-control-indicator-size: 1rem;
+@custom-control-indicator-bg: @gray-300;
+@custom-control-indicator-bg-size: 50% 50%;
+@custom-control-indicator-box-shadow: inset 0 0.25rem 0.25rem fade(@black, 10%);
+
+@custom-control-indicator-disabled-bg: @gray-200;
+@custom-control-label-disabled-color: @gray-600;
+
+@custom-control-indicator-checked-color: @component-active-color;
+@custom-control-indicator-checked-bg: @component-active-bg;
+@custom-control-indicator-checked-disabled-bg: fade(theme-color(primary), 50%);
+@custom-control-indicator-checked-box-shadow: none;
+
+@custom-control-indicator-focus-box-shadow: 0 0 0 1px @body-bg, @input-btn-focus-box-shadow;
+
+@custom-control-indicator-active-color: @component-active-color;
+@custom-control-indicator-active-bg: lighten(@component-active-bg, 35%);
+@custom-control-indicator-active-box-shadow: none;
+
+@custom-checkbox-indicator-border-radius: @border-radius;
+@custom-checkbox-indicator-icon-checked: %(~'url("data:image/svg+xml;charset=utf8,%s%s%s")', "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='", replace("@{custom-control-indicator-checked-color}", "#", "%23"), "' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E");
+
+@custom-checkbox-indicator-indeterminate-bg: @component-active-bg;
+@custom-checkbox-indicator-indeterminate-color: @custom-control-indicator-checked-color;
+@custom-checkbox-indicator-icon-indeterminate: %(~'url("data:image/svg+xml;charset=utf8,%s%s%s")', "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='", replace("@{custom-checkbox-indicator-indeterminate-color}", "#", "%23"), "' d='M0 2h4'/%3E%3C/svg%3E");
+@custom-checkbox-indicator-indeterminate-box-shadow: none;
+
+@custom-radio-indicator-border-radius: 50%;
+@custom-radio-indicator-icon-checked: %(~'url("data:image/svg+xml;charset=utf8,%s%s%s")', "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='", replace("@{custom-control-indicator-checked-color}", "#", "%23"), "'/%3E%3C/svg%3E");
+
+@custom-select-padding-y: 0.375rem;
+@custom-select-padding-x: 0.75rem;
+@custom-select-height: @input-height;
+@custom-select-indicator-padding: 1rem; // Extra padding to account for the presence of the background-image based indicator
+@custom-select-line-height: @input-btn-line-height;
+@custom-select-color: @input-color;
+@custom-select-disabled-color: @gray-600;
+@custom-select-bg: @input-bg;
+@custom-select-disabled-bg: @gray-200;
+@custom-select-bg-size: 8px 10px; // In pixels because image dimensions
+@custom-select-indicator-color: @gray-800;
+@custom-select-indicator: %(~'url("data:image/svg+xml;charset=utf8,%s%s%s")', "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='", replace("@{custom-select-indicator-color}", "#", "%23"), "' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E");
+@custom-select-border-width: @input-btn-border-width;
+@custom-select-border-color: @input-border-color;
+@custom-select-border-radius: @border-radius;
+@custom-select-box-shadow: inset 0 1px 2px fade(@black, 7.5%);
+
+@custom-select-focus-border-color: @input-focus-border-color;
+@custom-select-focus-width: @input-btn-focus-width;
+@custom-select-focus-box-shadow: 0 0 0 @custom-select-focus-width fade(@custom-select-focus-border-color, 50%);
+
+@custom-select-font-size-sm: 75%;
+@custom-select-height-sm: @input-height-sm;
+
+@custom-select-font-size-lg: 125%;
+@custom-select-height-lg: @input-height-lg;
+
+@custom-range-track-width: 100%;
+@custom-range-track-height: 0.5rem;
+@custom-range-track-cursor: pointer;
+@custom-range-track-bg: @gray-300;
+@custom-range-track-border-radius: 1rem;
+@custom-range-track-box-shadow: inset 0 0.25rem 0.25rem fade(@black, 10%);
+
+@custom-range-thumb-width: 1rem;
+@custom-range-thumb-height: @custom-range-thumb-width;
+@custom-range-thumb-bg: @component-active-bg;
+@custom-range-thumb-border: 0;
+@custom-range-thumb-border-radius: 1rem;
+@custom-range-thumb-box-shadow: 0 0.1rem 0.25rem fade(@black, 10%);
+@custom-range-thumb-focus-box-shadow: 0 0 0 1px @body-bg, @input-btn-focus-box-shadow;
+@custom-range-thumb-focus-box-shadow-width: @input-btn-focus-width; // For focus box shadow issue in IE/Edge
+@custom-range-thumb-active-bg: lighten(@component-active-bg, 35%);
+
+@custom-file-height: @input-height;
+@custom-file-height-inner: @input-height-inner;
+@custom-file-focus-border-color: @input-focus-border-color;
+@custom-file-focus-box-shadow: @input-btn-focus-box-shadow;
+@custom-file-disabled-bg: @input-disabled-bg;
+
+@custom-file-padding-y: @input-btn-padding-y;
+@custom-file-padding-x: @input-btn-padding-x;
+@custom-file-line-height: @input-btn-line-height;
+@custom-file-color: @input-color;
+@custom-file-bg: @input-bg;
+@custom-file-border-width: @input-btn-border-width;
+@custom-file-border-color: @input-border-color;
+@custom-file-border-radius: @input-border-radius;
+@custom-file-box-shadow: @input-box-shadow;
+@custom-file-button-color: @custom-file-color;
+@custom-file-button-bg: @input-group-addon-bg;
+@custom-file-text:
+ en "Browse";
+
+
+// Form validation
+@form-feedback-margin-top: @form-text-margin-top;
+@form-feedback-font-size: @small-font-size;
+@form-feedback-valid-color: theme-color(success);
+@form-feedback-invalid-color: theme-color(danger);
+
+
+// Dropdowns
+//
+// Dropdown menu container and contents.
+
+@dropdown-min-width: 10rem;
+@dropdown-padding-y: 0.5rem;
+@dropdown-spacer: 0.125rem;
+@dropdown-bg: @white;
+@dropdown-border-color: fade(@black, 15%);
+@dropdown-border-radius: @border-radius;
+@dropdown-border-width: @border-width;
+@dropdown-divider-bg: @gray-200;
+@dropdown-box-shadow: 0 0.5rem 1rem fade(@black, 17.5%);
+
+@dropdown-link-color: @gray-900;
+@dropdown-link-hover-color: darken(@gray-900, 5%);
+@dropdown-link-hover-bg: @gray-100;
+
+@dropdown-link-active-color: @component-active-color;
+@dropdown-link-active-bg: @component-active-bg;
+
+@dropdown-link-disabled-color: @gray-600;
+
+@dropdown-item-padding-y: 0.25rem;
+@dropdown-item-padding-x: 1.5rem;
+
+@dropdown-header-color: @gray-600;
+
+
+// Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+
+@zindex-dropdown: 1000;
+@zindex-sticky: 1020;
+@zindex-fixed: 1030;
+@zindex-modal-backdrop: 1040;
+@zindex-modal: 1050;
+@zindex-popover: 1060;
+@zindex-tooltip: 1070;
+
+// Navs
+
+@nav-link-padding-y: 0.5rem;
+@nav-link-padding-x: 1rem;
+@nav-link-disabled-color: @gray-600;
+
+@nav-tabs-border-color: @gray-300;
+@nav-tabs-border-width: @border-width;
+@nav-tabs-border-radius: @border-radius;
+@nav-tabs-link-hover-border-color: @gray-200 @gray-200 @nav-tabs-border-color;
+@nav-tabs-link-active-color: @gray-700;
+@nav-tabs-link-active-bg: @body-bg;
+@nav-tabs-link-active-border-color: @gray-300 @gray-300 @nav-tabs-link-active-bg;
+
+@nav-pills-border-radius: @border-radius;
+@nav-pills-link-active-color: @component-active-color;
+@nav-pills-link-active-bg: @component-active-bg;
+
+@nav-divider-color: @gray-200;
+@nav-divider-margin-y: (@spacer / 2);
+
+// Navbar
+
+@navbar-padding-y: (@spacer / 2);
+@navbar-padding-x: @spacer;
+
+@navbar-nav-link-padding-x: 0.5rem;
+
+@navbar-brand-font-size: @font-size-lg;
+// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link
+@nav-link-height: (@font-size-base * @line-height-base + @nav-link-padding-y * 2);
+@navbar-brand-height: (@navbar-brand-font-size * @line-height-base);
+@navbar-brand-padding-y: ((@nav-link-height - @navbar-brand-height) / 2);
+
+@navbar-toggler-padding-y: 0.25rem;
+@navbar-toggler-padding-x: 0.75rem;
+@navbar-toggler-font-size: @font-size-lg;
+@navbar-toggler-border-radius: @btn-border-radius;
+
+@navbar-dark-color: fade(@white, 50%);
+@navbar-dark-hover-color: fade(@white, 75%);
+@navbar-dark-active-color: @white;
+@navbar-dark-disabled-color: fade(@white, 25%);
+@navbar-dark-toggler-icon-bg: %(~'url("data:image/svg+xml;charset=utf8,%s%s%s")', "%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='", replace("@{navbar-dark-color}", "#", "%23"), "' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
+@navbar-dark-toggler-border-color: fade(@white, 10%);
+
+@navbar-light-color: fade(@black, 50%);
+@navbar-light-hover-color: fade(@black, 70%);
+@navbar-light-active-color: fade(@black, 90%);
+@navbar-light-disabled-color: fade(@black, 30%);
+@navbar-light-toggler-icon-bg: %(~'url("data:image/svg+xml;charset=utf8,%s%s%s")', "%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='", replace("@{navbar-light-color}", "#", "%23"), "' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
+@navbar-light-toggler-border-color: fade(@black, 10%);
+
+// Pagination
+
+@pagination-padding-y: 0.5rem;
+@pagination-padding-x: 0.75rem;
+@pagination-padding-y-sm: 0.25rem;
+@pagination-padding-x-sm: 0.5rem;
+@pagination-padding-y-lg: 0.75rem;
+@pagination-padding-x-lg: 1.5rem;
+@pagination-line-height: 1.25;
+
+@pagination-color: @link-color;
+@pagination-bg: @white;
+@pagination-border-width: @border-width;
+@pagination-border-color: @gray-300;
+
+@pagination-focus-box-shadow: @input-btn-focus-box-shadow;
+@pagination-focus-outline: 0;
+
+@pagination-hover-color: @link-hover-color;
+@pagination-hover-bg: @gray-200;
+@pagination-hover-border-color: @gray-300;
+
+@pagination-active-color: @component-active-color;
+@pagination-active-bg: @component-active-bg;
+@pagination-active-border-color: @pagination-active-bg;
+
+@pagination-disabled-color: @gray-600;
+@pagination-disabled-bg: @white;
+@pagination-disabled-border-color: @gray-300;
+
+
+// Jumbotron
+
+@jumbotron-padding: 2rem;
+@jumbotron-bg: @gray-200;
+
+
+// Cards
+
+@card-spacer-y: 0.75rem;
+@card-spacer-x: 1.25rem;
+@card-border-width: @border-width;
+@card-border-radius: @border-radius;
+@card-border-color: fade(@black, 12.5%);
+@card-inner-border-radius: ~"calc(@{card-border-radius} - @{card-border-width})";
+@card-cap-bg: fade(@black, 3%);
+@card-bg: @white;
+
+@card-img-overlay-padding: 1.25rem;
+
+@card-group-margin: (@grid-gutter-width / 2);
+@card-deck-margin: @card-group-margin;
+
+@card-columns-count: 3;
+@card-columns-gap: 1.25rem;
+@card-columns-margin: @card-spacer-y;
+
+
+// Tooltips
+
+@tooltip-font-size: @font-size-sm;
+@tooltip-max-width: 200px;
+@tooltip-color: @white;
+@tooltip-bg: @black;
+@tooltip-border-radius: @border-radius;
+@tooltip-opacity: 0.9;
+@tooltip-padding-y: 0.25rem;
+@tooltip-padding-x: 0.5rem;
+@tooltip-margin: 0;
+
+@tooltip-arrow-width: 0.8rem;
+@tooltip-arrow-height: 0.4rem;
+@tooltip-arrow-color: @tooltip-bg;
+
+
+// Popovers
+
+@popover-font-size: @font-size-sm;
+@popover-bg: @white;
+@popover-max-width: 276px;
+@popover-border-width: @border-width;
+@popover-border-color: fade(@black, 20%);
+@popover-border-radius: @border-radius-lg;
+@popover-box-shadow: 0 0.25rem 0.5rem fade(@black, 20%);
+
+@popover-header-bg: darken(@popover-bg, 3%);
+@popover-header-color: @headings-color;
+@popover-header-padding-y: 0.5rem;
+@popover-header-padding-x: 0.75rem;
+
+@popover-body-color: @body-color;
+@popover-body-padding-y: @popover-header-padding-y;
+@popover-body-padding-x: @popover-header-padding-x;
+
+@popover-arrow-width: 1rem;
+@popover-arrow-height: 0.5rem;
+@popover-arrow-color: @popover-bg;
+
+@popover-arrow-outer-color: fadein(@popover-border-color, 5%);
+
+
+// Badges
+
+@badge-font-size: 75%;
+@badge-font-weight: @font-weight-bold;
+@badge-padding-y: 0.25em;
+@badge-padding-x: 0.4em;
+@badge-border-radius: @border-radius;
+
+@badge-pill-padding-x: 0.6em;
+// Use a higher than normal value to ensure completely rounded edges when
+// customizing padding or font-size on labels.
+@badge-pill-border-radius: 10rem;
+
+
+// Modals
+
+// Padding applied to the modal body
+@modal-inner-padding: 1rem;
+
+@modal-dialog-margin: 0.5rem;
+@modal-dialog-margin-y-sm-up: 1.75rem;
+
+@modal-title-line-height: @line-height-base;
+
+@modal-content-bg: @white;
+@modal-content-border-color: fade(@black, 20%);
+@modal-content-border-width: @border-width;
+@modal-content-border-radius: @border-radius-lg;
+@modal-content-box-shadow-xs: 0 0.25rem 0.5rem fade(@black, 50%);
+@modal-content-box-shadow-sm-up: 0 0.5rem 1rem fade(@black, 50%);
+
+@modal-backdrop-bg: @black;
+@modal-backdrop-opacity: 0.5;
+@modal-header-border-color: @gray-200;
+@modal-footer-border-color: @modal-header-border-color;
+@modal-header-border-width: @modal-content-border-width;
+@modal-footer-border-width: @modal-header-border-width;
+@modal-header-padding: 1rem;
+
+@modal-lg: 800px;
+@modal-md: 500px;
+@modal-sm: 300px;
+
+@modal-transition: transform 0.3s ease-out;
+
+
+// Alerts
+//
+// Define alert colors, border radius, and padding.
+
+@alert-padding-y: 0.75rem;
+@alert-padding-x: 1.25rem;
+@alert-margin-bottom: 1rem;
+@alert-border-radius: @border-radius;
+@alert-link-font-weight: @font-weight-bold;
+@alert-border-width: @border-width;
+
+@alert-bg-level: -10;
+@alert-border-level: -9;
+@alert-color-level: 6;
+
+
+// Progress bars
+
+@progress-height: 1rem;
+@progress-font-size: (@font-size-base * 0.75);
+@progress-bg: @gray-200;
+@progress-border-radius: @border-radius;
+@progress-box-shadow: inset 0 0.1rem 0.1rem fade(@black, 10%);
+@progress-bar-color: @white;
+@progress-bar-bg: theme-color(primary);
+@progress-bar-animation-timing: 1s linear infinite;
+@progress-bar-transition: width 0.6s ease;
+
+// List group
+
+@list-group-bg: @white;
+@list-group-border-color: fade(@black, 12.5%);
+@list-group-border-width: @border-width;
+@list-group-border-radius: @border-radius;
+
+@list-group-item-padding-y: 0.75rem;
+@list-group-item-padding-x: 1.25rem;
+
+@list-group-hover-bg: @gray-100;
+@list-group-active-color: @component-active-color;
+@list-group-active-bg: @component-active-bg;
+@list-group-active-border-color: @list-group-active-bg;
+
+@list-group-disabled-color: @gray-600;
+@list-group-disabled-bg: @list-group-bg;
+
+@list-group-action-color: @gray-700;
+@list-group-action-hover-color: @list-group-action-color;
+
+@list-group-action-active-color: @body-color;
+@list-group-action-active-bg: @gray-200;
+
+
+// Image thumbnails
+
+@thumbnail-padding: 0.25rem;
+@thumbnail-bg: @body-bg;
+@thumbnail-border-width: @border-width;
+@thumbnail-border-color: @gray-300;
+@thumbnail-border-radius: @border-radius;
+@thumbnail-box-shadow: 0 1px 2px fade(@black, 7.5%);
+
+
+// Figures
+
+@figure-caption-font-size: 90%;
+@figure-caption-color: @gray-600;
+
+
+// Breadcrumbs
+
+@breadcrumb-padding-y: 0.75rem;
+@breadcrumb-padding-x: 1rem;
+@breadcrumb-item-padding: 0.5rem;
+
+@breadcrumb-margin-bottom: 1rem;
+
+@breadcrumb-bg: @gray-200;
+@breadcrumb-divider-color: @gray-600;
+@breadcrumb-active-color: @gray-600;
+@breadcrumb-divider: "/";
+
+@breadcrumb-border-radius: @border-radius;
+
+
+// Carousel
+
+@carousel-control-color: @white;
+@carousel-control-width: 15%;
+@carousel-control-opacity: 0.5;
+
+@carousel-indicator-width: 30px;
+@carousel-indicator-height: 3px;
+@carousel-indicator-spacer: 3px;
+@carousel-indicator-active-bg: @white;
+
+@carousel-caption-width: 70%;
+@carousel-caption-color: @white;
+
+@carousel-control-icon-width: 20px;
+
+@carousel-control-prev-icon-bg: %(~'url("data:image/svg+xml;charset=utf8,%s%s%s")', "%3Csvg xmlns='http://www.w3.org/2000/svg' fill='", replace("@{carousel-control-color}", "#", "%23"), "' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E");
+@carousel-control-next-icon-bg: %(~'url("data:image/svg+xml;charset=utf8,%s%s%s")', "%3Csvg xmlns='http://www.w3.org/2000/svg' fill='", replace("@{carousel-control-color}", "#", "%23"), "' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E");
+
+@carousel-transition: transform 0.6s ease; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity 0.5s ease-out`)
+
+
+// Close
+
+@close-font-size: (@font-size-base * 1.5);
+@close-font-weight: @font-weight-bold;
+@close-color: @black;
+@close-text-shadow: 0 1px 0 @white;
+
+// Code
+
+@code-font-size: 87.5%;
+@code-color: @pink;
+
+@kbd-padding-y: 0.2rem;
+@kbd-padding-x: 0.4rem;
+@kbd-font-size: @code-font-size;
+@kbd-color: @white;
+@kbd-bg: @gray-900;
+
+@pre-color: @gray-900;
+@pre-scrollable-max-height: 340px;
+
+
+// Printing
+@print-page-size: a3;
+@print-body-min-width: map-get(@grid-breakpoints, lg);
diff --git a/include/thirdparty/Bootstrap4/less/bootstrap-grid.less b/include/thirdparty/Bootstrap4/less/bootstrap-grid.less
new file mode 100644
index 0000000..b01af37
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/bootstrap-grid.less
@@ -0,0 +1,32 @@
+/*!
+ * Bootstrap Grid v4.1.3 (https://getbootstrap.com/)
+ * Copyright 2011-2018 The Bootstrap Authors
+ * Copyright 2011-2018 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+// LESS PORT: Less has no `@at-root` equivalent, so omitting that here.
+@-ms-viewport { width: device-width; }
+
+
+html {
+ box-sizing: border-box;
+ -ms-overflow-style: scrollbar;
+}
+
+*,
+*::before,
+*::after {
+ box-sizing: inherit;
+}
+
+@import "_functions";
+@import "_variables";
+
+@import "mixins/_breakpoints";
+@import "mixins/_grid-framework";
+@import "mixins/_grid";
+
+@import "_grid";
+@import "utilities/_display";
+@import "utilities/_flex";
diff --git a/include/thirdparty/Bootstrap4/less/bootstrap-reboot.less b/include/thirdparty/Bootstrap4/less/bootstrap-reboot.less
new file mode 100644
index 0000000..ef66ca2
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/bootstrap-reboot.less
@@ -0,0 +1,12 @@
+/*!
+ * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
+ * Copyright 2011-2018 The Bootstrap Authors
+ * Copyright 2011-2018 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
+ */
+
+@import "_functions";
+@import "_variables";
+@import "_mixins";
+@import "_reboot";
diff --git a/include/thirdparty/Bootstrap4/less/bootstrap.less b/include/thirdparty/Bootstrap4/less/bootstrap.less
new file mode 100644
index 0000000..44c3b63
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/bootstrap.less
@@ -0,0 +1,42 @@
+/*!
+ * Bootstrap v4.1.3 (https://getbootstrap.com/)
+ * Copyright 2011-2018 The Bootstrap Authors
+ * Copyright 2011-2018 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+@import "_functions";
+@import "_variables";
+@import "_mixins";
+@import "_root";
+@import "_reboot";
+@import "_type";
+@import "_images";
+@import "_code";
+@import "_grid";
+@import "_tables";
+@import "_forms";
+@import "_buttons";
+@import "_transitions";
+@import "_dropdown";
+@import "_button-group";
+@import "_input-group";
+@import "_custom-forms";
+@import "_nav";
+@import "_navbar";
+@import "_card";
+@import "_breadcrumb";
+@import "_pagination";
+@import "_badge";
+@import "_jumbotron";
+@import "_alert";
+@import "_progress";
+@import "_media";
+@import "_list-group";
+@import "_close";
+@import "_modal";
+@import "_tooltip";
+@import "_popover";
+@import "_carousel";
+@import "_utilities";
+@import "_print";
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_alert.less b/include/thirdparty/Bootstrap4/less/mixins/_alert.less
new file mode 100644
index 0000000..abffc63
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_alert.less
@@ -0,0 +1,13 @@
+#alert-variant(@background, @border, @color) {
+ color: @color;
+ #gradient-bg(@background);
+ border-color: @border;
+
+ hr {
+ border-top-color: darken(@border, 5%);
+ }
+
+ .alert-link {
+ color: darken(@color, 10%);
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_background-variant.less b/include/thirdparty/Bootstrap4/less/mixins/_background-variant.less
new file mode 100644
index 0000000..f40d8a2
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_background-variant.less
@@ -0,0 +1,19 @@
+// Contextual backgrounds
+
+#bg-variant(@parent, @color) {
+ @{parent} {
+ background-color: @color !important;
+ }
+ a@{parent},
+ button@{parent} {
+ #hover-focus({
+ background-color: darken(@color, 10%) !important;
+ });
+ }
+}
+
+#bg-gradient-variant(@parent, @color) {
+ @{parent} {
+ background: @color linear-gradient(180deg, mix(@body-bg, @color, 15%), @color) repeat-x !important;
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_badge.less b/include/thirdparty/Bootstrap4/less/mixins/_badge.less
new file mode 100644
index 0000000..5a4e630
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_badge.less
@@ -0,0 +1,12 @@
+#badge-variant(@bg) {
+ color: color-yiq(@bg);
+ background-color: @bg;
+
+ &[href] {
+ #hover-focus({
+ color: color-yiq(@bg);
+ text-decoration: none;
+ background-color: darken(@bg, 10%);
+ })
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_border-radius.less b/include/thirdparty/Bootstrap4/less/mixins/_border-radius.less
new file mode 100644
index 0000000..b6bb13b
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_border-radius.less
@@ -0,0 +1,25 @@
+// Single side border-radius
+
+#border-radius(@radius: @border-radius) when (@enable-rounded) {
+ border-radius: @radius;
+}
+
+#border-top-radius(@radius) when (@enable-rounded) {
+ border-top-left-radius: @radius;
+ border-top-right-radius: @radius;
+}
+
+#border-right-radius(@radius) when (@enable-rounded) {
+ border-top-right-radius: @radius;
+ border-bottom-right-radius: @radius;
+}
+
+#border-bottom-radius(@radius) when (@enable-rounded) {
+ border-bottom-right-radius: @radius;
+ border-bottom-left-radius: @radius;
+}
+
+#border-left-radius(@radius) when (@enable-rounded) {
+ border-top-left-radius: @radius;
+ border-bottom-left-radius: @radius;
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_box-shadow.less b/include/thirdparty/Bootstrap4/less/mixins/_box-shadow.less
new file mode 100644
index 0000000..760ed67
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_box-shadow.less
@@ -0,0 +1,24 @@
+//@mixin box-shadow($shadow...) {
+// @if $enable-shadows {
+// box-shadow: $shadow;
+// }
+//}
+#box-shadow(@shadow...) when (@enable-shadows) {
+ // LESS PORT: In order to output the shadows correctly we have to iterate over the list and use
+ // Less’s merge feature. Without this, shadows will be output space-separated instead of comma-
+ // separated. Also, since a single shadow can be misinterpreted as multiple shadows (since it
+ // will have a length > 1) we have to include a check for the length of the first item in the
+ // list. If the length is greater than 1, then we have a list of separate shadows. If the the
+ // length is 1, then we’re looking at the first value of a single shadow, so we output `@shadow`
+ // as-is.
+ & when (length(@shadow) > 0) and (length(extract(@shadow, 1)) = 1) {
+ box-shadow: @shadow;
+ }
+ & when (length(@shadow) > 0) and (length(extract(@shadow, 1)) > 1) {
+ #each-shadow(@i: 1) when (@i =< length(@shadow)) {
+ box-shadow+: extract(@shadow, @i);
+
+ #each-shadow((@i + 1));
+ } #each-shadow();
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_breakpoints.less b/include/thirdparty/Bootstrap4/less/mixins/_breakpoints.less
new file mode 100644
index 0000000..04f357d
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_breakpoints.less
@@ -0,0 +1,181 @@
+// Breakpoint viewport sizes and media queries.
+//
+// Breakpoints are defined as a map of (name: minimum width), order from small to large:
+//
+// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)
+//
+// The map defined in the `@grid-breakpoints` global variable is used as the `@breakpoints` argument by default.
+
+@plugin "../plugins/breakpoints";
+
+// Name of the next breakpoint, or null for the last breakpoint.
+//
+// >> breakpoint-next(sm)
+// md
+// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// md
+// >> breakpoint-next(sm, @breakpoint-names: (xs sm md lg xl))
+// md
+//@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
+// $n: index($breakpoint-names, $name);
+// @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
+//}
+
+// Minimum breakpoint width. Null for the smallest (first) breakpoint.
+//
+// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// 576px
+//@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
+// $min: map-get($breakpoints, $name);
+// @return if($min != 0, $min, null);
+//}
+
+// Maximum breakpoint width. Null for the largest (last) breakpoint.
+// The maximum value is calculated as the minimum of the next one less 0.02px
+// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.
+// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max
+// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.
+// See https://bugs.webkit.org/show_bug.cgi?id=178261
+//
+// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// 767.98px
+//@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
+// $next: breakpoint-next($name, $breakpoints);
+// @return if($next, breakpoint-min($next, $breakpoints) - 0.01px, null);
+//}
+
+// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.
+// Useful for making responsive utilities.
+//
+// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// "" (Returns a blank string)
+// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// "-sm"
+//@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {
+// @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}");
+//}
+
+// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.
+// Makes the @content apply to the given breakpoint and wider.
+//@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
+// $min: breakpoint-min($name, $breakpoints);
+// @if $min {
+// @media (min-width: $min) {
+// @content;
+// }
+// } @else {
+// @content;
+// }
+//}
+#media-breakpoint-up(@name, @content, @breakpoints: @grid-breakpoints) {
+ @min: breakpoint-min(@name, @breakpoints);
+
+ & when not (@min = ~"") {
+ @media (min-width: @min) {
+ @content();
+ }
+ }
+ & when (@min = ~"") {
+ @content();
+ }
+}
+
+// Media of at most the maximum breakpoint width. No query for the largest breakpoint.
+// Makes the @content apply to the given breakpoint and narrower.
+//@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
+// $max: breakpoint-max($name, $breakpoints);
+// @if $max {
+// @media (max-width: $max) {
+// @content;
+// }
+// } @else {
+// @content;
+// }
+//}
+#media-breakpoint-down(@name, @content, @breakpoints: @grid-breakpoints) {
+ @max: breakpoint-max(@name, @breakpoints);
+
+ & when not (@max = ~"") {
+ @media (max-width: @max) {
+ @content();
+ }
+ }
+ & when (@max = ~"") {
+ @content();
+ }
+}
+
+// Media that spans multiple breakpoint widths.
+// Makes the @content apply between the min and max breakpoints
+//@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {
+// $min: breakpoint-min($lower, $breakpoints);
+// $max: breakpoint-max($upper, $breakpoints);
+//
+// @if $min != null and $max != null {
+// @media (min-width: $min) and (max-width: $max) {
+// @content;
+// }
+// } @else if $max == null {
+// @include media-breakpoint-up($lower) {
+// @content;
+// }
+// } @else if $min == null {
+// @include media-breakpoint-down($upper) {
+// @content;
+// }
+// }
+//}
+#media-breakpoint-between(@lower, @upper, @content, @breakpoints: @grid-breakpoints) {
+ @min: breakpoint-min(@lower, @breakpoints);
+ @max: breakpoint-max(@upper, @breakpoints);
+
+ & when not (@min = ~"") and not (@max = ~"") {
+ @media (min-width: @min) and (max-width: @max) {
+ @content();
+ }
+ }
+ & when not (@min = ~"") and (@max = ~"") {
+ #media-breakpoint-up(@lower, @content, @breakpoints);
+ }
+ & when (@min = ~"") and not (@max = ~"") {
+ #media-breakpoint-down(@upper, @content, @breakpoints);
+ }
+}
+
+// Media between the breakpoint's minimum and maximum widths.
+// No minimum for the smallest breakpoint, and no maximum for the largest one.
+// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.
+//@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {
+// $min: breakpoint-min($name, $breakpoints);
+// $max: breakpoint-max($name, $breakpoints);
+//
+// @if $min != null and $max != null {
+// @media (min-width: $min) and (max-width: $max) {
+// @content;
+// }
+// } @else if $max == null {
+// @include media-breakpoint-up($name) {
+// @content;
+// }
+// } @else if $min == null {
+// @include media-breakpoint-down($name) {
+// @content;
+// }
+// }
+//}
+#media-breakpoint-only(@name, @content, @breakpoints: @grid-breakpoints) {
+ @min: breakpoint-min(@name, @breakpoints);
+ @max: breakpoint-max(@name, @breakpoints);
+
+ & when not (@min = ~"") and not (@max = ~"") {
+ @media (min-width: @min) and (max-width: @max) {
+ @content();
+ }
+ }
+ & when not (@min = ~"") and (@max = ~"") {
+ #media-breakpoint-up(@name, @content, @breakpoints);
+ }
+ & when (@min = ~"") and not (@max = ~"") {
+ #media-breakpoint-down(@name, @content, @breakpoints);
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_buttons.less b/include/thirdparty/Bootstrap4/less/mixins/_buttons.less
new file mode 100644
index 0000000..62c2a27
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_buttons.less
@@ -0,0 +1,113 @@
+// Button variants
+//
+// Easily pump out default styles, as well as :hover, :focus, :active,
+// and disabled options for all buttons
+
+#button-variant(@background, @border, @hover-background: darken(@background, 7.5%), @hover-border: darken(@border, 10%), @active-background: darken(@background, 10%), @active-border: darken(@border, 12.5%)) {
+ color: color-yiq(@background);
+ #gradient-bg(@background);
+ border-color: @border;
+ #box-shadow(@btn-box-shadow);
+
+ #hover({
+ color: color-yiq(@hover-background);
+ #gradient-bg(@hover-background);
+ border-color: @hover-border;
+ });
+
+ &:focus,
+ &.focus {
+ // Avoid using mixin so we can pass custom focus shadow properly
+ & when (@enable-shadows) {
+ box-shadow: @btn-box-shadow, 0 0 0 @btn-focus-width fade(@border, 50%);
+ }
+ & when not (@enable-shadows) {
+ box-shadow: 0 0 0 @btn-focus-width fade(@border, 50%);
+ }
+ }
+
+ // Disabled comes first so active can properly restyle
+ &.disabled,
+ &:disabled {
+ color: color-yiq(@background);
+ background-color: @background;
+ border-color: @border;
+ }
+
+ &:not(:disabled):not(.disabled):active,
+ &:not(:disabled):not(.disabled).active,
+ .show > &.dropdown-toggle {
+ color: color-yiq(@active-background);
+ background-color: @active-background;
+ & when (@enable-gradients) {
+ background-image: none; // Remove the gradient for the pressed/active state
+ }
+ border-color: @active-border;
+
+ &:focus {
+ // Avoid using mixin so we can pass custom focus shadow properly
+ & when (@enable-shadows) {
+ box-shadow: @btn-active-box-shadow, 0 0 0 @input-btn-focus-width fade(@border, 50%);
+ }
+ & when not (@enable-shadows) {
+ box-shadow: 0 0 0 @input-btn-focus-width fade(@border, 50%);
+ }
+ }
+ }
+}
+
+#button-outline-variant(@color, @color-hover: color-yiq(@color), @active-background: @color, @active-border: @color) {
+ color: @color;
+ background-color: transparent;
+ background-image: none;
+ border-color: @color;
+
+ &:hover {
+ color: @color-hover;
+ background-color: @active-background;
+ border-color: @active-border;
+ }
+
+ &:focus,
+ &.focus {
+ box-shadow: 0 0 0 @btn-focus-width fade(@color, 50%);
+ }
+
+ &.disabled,
+ &:disabled {
+ color: @color;
+ background-color: transparent;
+ }
+
+ &:not(:disabled):not(.disabled):active,
+ &:not(:disabled):not(.disabled).active,
+ .show > &.dropdown-toggle {
+ color: color-yiq(@active-background);
+ background-color: @active-background;
+ border-color: @active-border;
+
+ &:focus {
+ // Avoid using mixin so we can pass custom focus shadow properly
+ & when (@enable-shadows) and not (@btn-active-box-shadow = ~"") {
+ box-shadow: @btn-active-box-shadow, 0 0 0 @btn-focus-width fade(@color, 50%);
+ }
+ & when not (@enable-shadows) or (@btn-active-box-shadow = ~"") {
+ box-shadow: 0 0 0 @btn-focus-width fade(@color, 50%);
+ }
+ }
+ }
+}
+
+// Button sizes
+#button-size(@padding-y, @padding-x, @font-size, @line-height, @border-radius) {
+ padding: @padding-y @padding-x;
+ font-size: @font-size;
+ line-height: @line-height;
+ // Manually declare to provide an override to the browser default
+ & when (@enable-rounded) {
+ border-radius: @border-radius;
+ }
+ & when not (@enable-rounded) {
+ border-radius: 0;
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_caret.less b/include/thirdparty/Bootstrap4/less/mixins/_caret.less
new file mode 100644
index 0000000..e79698e
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_caret.less
@@ -0,0 +1,68 @@
+#caret-down() {
+ border-top: @caret-width solid;
+ border-right: @caret-width solid transparent;
+ border-bottom: 0;
+ border-left: @caret-width solid transparent;
+}
+
+#caret-up() {
+ border-top: 0;
+ border-right: @caret-width solid transparent;
+ border-bottom: @caret-width solid;
+ border-left: @caret-width solid transparent;
+}
+
+#caret-right() {
+ border-top: @caret-width solid transparent;
+ border-right: 0;
+ border-bottom: @caret-width solid transparent;
+ border-left: @caret-width solid;
+}
+
+#caret-left() {
+ border-top: @caret-width solid transparent;
+ border-right: @caret-width solid;
+ border-bottom: @caret-width solid transparent;
+}
+
+#caret(@direction: down) {
+ & when (@enable-caret) {
+ &::after {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-left: (@caret-width * 0.85);
+ vertical-align: (@caret-width * 0.85);
+ content: "";
+ & when (@direction = down) {
+ #caret-down();
+ }
+ & when (@direction = up) {
+ #caret-up();
+ }
+ & when (@direction = right) {
+ #caret-right();
+ }
+ }
+
+ & when (@direction = left) {
+ &::after {
+ display: none;
+ }
+
+ &::before {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-right: (@caret-width * 0.85);
+ vertical-align: (@caret-width * 0.85);
+ content: "";
+ #caret-left();
+ }
+ }
+
+ &:empty::after {
+ margin-left: 0;
+ }
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_clearfix.less b/include/thirdparty/Bootstrap4/less/mixins/_clearfix.less
new file mode 100644
index 0000000..7e508f8
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_clearfix.less
@@ -0,0 +1,7 @@
+#clearfix() {
+ &::after {
+ display: block;
+ clear: both;
+ content: "";
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_float.less b/include/thirdparty/Bootstrap4/less/mixins/_float.less
new file mode 100644
index 0000000..c5c07f8
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_float.less
@@ -0,0 +1,9 @@
+#float-left() {
+ float: left !important;
+}
+#float-right() {
+ float: right !important;
+}
+#float-none() {
+ float: none !important;
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_forms.less b/include/thirdparty/Bootstrap4/less/mixins/_forms.less
new file mode 100644
index 0000000..ac2dfec
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_forms.less
@@ -0,0 +1,148 @@
+// Form control focus state
+//
+// Generate a customized focus state and for any input with the specified color,
+// which defaults to the `@input-focus-border-color` variable.
+//
+// We highly encourage you to not customize the default value, but instead use
+// this to tweak colors on an as-needed basis. This aesthetic change is based on
+// WebKit's default styles, but applicable to a wider range of browsers. Its
+// usability and accessibility should be taken into account with any change.
+//
+// Example usage: change the default blue border and shadow to white for better
+// contrast against a dark gray background.
+#form-control-focus() {
+ &:focus {
+ color: @input-focus-color;
+ background-color: @input-focus-bg;
+ border-color: @input-focus-border-color;
+ outline: 0;
+ // Avoid using mixin so we can pass custom focus shadow properly
+ & when (@enable-shadows) {
+ box-shadow: @input-box-shadow, @input-focus-box-shadow;
+ }
+ & when not (@enable-shadows) {
+ box-shadow: @input-focus-box-shadow;
+ }
+ }
+}
+
+
+#form-validation-state(@state, @color) {
+ .@{state}-feedback {
+ display: none;
+ width: 100%;
+ margin-top: @form-feedback-margin-top;
+ font-size: @form-feedback-font-size;
+ color: @color;
+ }
+
+ .@{state}-tooltip {
+ position: absolute;
+ top: 100%;
+ z-index: 5;
+ display: none;
+ max-width: 100%; // Contain to parent when possible
+ padding: @tooltip-padding-y @tooltip-padding-x;
+ margin-top: 0.1rem;
+ font-size: @tooltip-font-size;
+ line-height: @line-height-base;
+ color: color-yiq(@color);
+ background-color: rgba(red(@color), green(@color), blue(@color), @tooltip-opacity);
+ #border-radius(@tooltip-border-radius);
+ }
+
+ .form-control,
+ .custom-select {
+ .was-validated &:@{state},
+ &.is-@{state} {
+ border-color: @color;
+
+ &:focus {
+ border-color: @color;
+ box-shadow: 0 0 0 @input-focus-width fade(@color, 25%);
+ }
+
+ ~ .@{state}-feedback,
+ ~ .@{state}-tooltip {
+ display: block;
+ }
+ }
+ }
+
+ .form-control-file {
+ .was-validated &:@{state},
+ &.is-@{state} {
+ ~ .@{state}-feedback,
+ ~ .@{state}-tooltip {
+ display: block;
+ }
+ }
+ }
+
+ .form-check-input {
+ .was-validated &:@{state},
+ &.is-@{state} {
+ ~ .form-check-label {
+ color: @color;
+ }
+
+ ~ .@{state}-feedback,
+ ~ .@{state}-tooltip {
+ display: block;
+ }
+ }
+ }
+
+ .custom-control-input {
+ .was-validated &:@{state},
+ &.is-@{state} {
+ ~ .custom-control-label {
+ color: @color;
+
+ &::before {
+ background-color: lighten(@color, 25%);
+ }
+ }
+
+ ~ .@{state}-feedback,
+ ~ .@{state}-tooltip {
+ display: block;
+ }
+
+ &:checked {
+ ~ .custom-control-label::before {
+ #gradient-bg(lighten(@color, 10%));
+ }
+ }
+
+ &:focus {
+ ~ .custom-control-label::before {
+ box-shadow: 0 0 0 1px @body-bg, 0 0 0 @input-focus-width fade(@color, 25%);
+ }
+ }
+ }
+ }
+
+ // custom file
+ .custom-file-input {
+ .was-validated &:@{state},
+ &.is-@{state} {
+ ~ .custom-file-label {
+ border-color: @color;
+
+ &::after { border-color: inherit; }
+ }
+
+ ~ .@{state}-feedback,
+ ~ .@{state}-tooltip {
+ display: block;
+ }
+
+ &:focus {
+ ~ .custom-file-label {
+ box-shadow: 0 0 0 @input-focus-width fade(@color, 25%);
+ }
+ }
+ }
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_gradients.less b/include/thirdparty/Bootstrap4/less/mixins/_gradients.less
new file mode 100644
index 0000000..746f3d2
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_gradients.less
@@ -0,0 +1,46 @@
+// Gradients
+
+#gradient-bg(@color) {
+ & when (@enable-gradients) {
+ background: @color linear-gradient(180deg, mix(@body-bg, @color, 15%), @color) repeat-x;
+ }
+ & when not (@enable-gradients) {
+ background-color: @color;
+ }
+}
+
+// Horizontal gradient, from left to right
+//
+// Creates two color stops, start and end, by specifying a color and position for each color stop.
+#gradient-x(@start-color: @gray-700, @end-color: @gray-800, @start-percent: 0%, @end-percent: 100%) {
+ background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent);
+ background-repeat: repeat-x;
+}
+
+// Vertical gradient, from top to bottom
+//
+// Creates two color stops, start and end, by specifying a color and position for each color stop.
+#gradient-y(@start-color: @gray-700, @end-color: @gray-800, @start-percent: 0%, @end-percent: 100%) {
+ background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent);
+ background-repeat: repeat-x;
+}
+
+#gradient-directional(@start-color: @gray-700, @end-color: @gray-800, @deg: 45deg) {
+ background-image: linear-gradient(@deg, @start-color, @end-color);
+ background-repeat: repeat-x;
+}
+#gradient-x-three-colors(@start-color: @blue, @mid-color: @purple, @color-stop: 50%, @end-color: @red) {
+ background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);
+ background-repeat: no-repeat;
+}
+#gradient-y-three-colors(@start-color: @blue, @mid-color: @purple, @color-stop: 50%, @end-color: @red) {
+ background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);
+ background-repeat: no-repeat;
+}
+#gradient-radial(@inner-color: @gray-700, @outer-color: @gray-800) {
+ background-image: radial-gradient(circle, @inner-color, @outer-color);
+ background-repeat: no-repeat;
+}
+#gradient-striped(@color: fade(@white, 15%), @angle: 45deg) {
+ background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_grid-framework.less b/include/thirdparty/Bootstrap4/less/mixins/_grid-framework.less
new file mode 100644
index 0000000..343440f
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_grid-framework.less
@@ -0,0 +1,144 @@
+// Framework grid generation
+//
+// Used only by Bootstrap to generate the correct number of grid classes given
+// any value of `@grid-columns`.
+
+//@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {
+// // Common properties for all breakpoints
+// %grid-column {
+// position: relative;
+// width: 100%;
+// min-height: 1px; // Prevent columns from collapsing when empty
+// padding-right: ($gutter / 2);
+// padding-left: ($gutter / 2);
+// }
+//
+// @each $breakpoint in map-keys($breakpoints) {
+// $infix: breakpoint-infix($breakpoint, $breakpoints);
+//
+// // Allow columns to stretch full width below their breakpoints
+// @for $i from 1 through $columns {
+// .col#{$infix}-#{$i} {
+// @extend %grid-column;
+// }
+// }
+// .col#{$infix},
+// .col#{$infix}-auto {
+// @extend %grid-column;
+// }
+//
+// @include media-breakpoint-up($breakpoint, $breakpoints) {
+// // Provide basic `.col-{bp}` classes for equal-width flexbox columns
+// .col#{$infix} {
+// flex-basis: 0;
+// flex-grow: 1;
+// max-width: 100%;
+// }
+// .col#{$infix}-auto {
+// flex: 0 0 auto;
+// width: auto;
+// max-width: none; // Reset earlier grid tiers
+// }
+//
+// @for $i from 1 through $columns {
+// .col#{$infix}-#{$i} {
+// @include make-col($i, $columns);
+// }
+// }
+//
+// .order#{$infix}-first { order: -1; }
+//
+// .order#{$infix}-last { order: $columns + 1; }
+//
+// @for $i from 0 through $columns {
+// .order#{$infix}-#{$i} { order: $i; }
+// }
+//
+// // `$columns - 1` because offsetting by the width of an entire row isn't possible
+// @for $i from 0 through ($columns - 1) {
+// @if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-0
+// .offset#{$infix}-#{$i} {
+// @include make-col-offset($i, $columns);
+// }
+// }
+// }
+// }
+// }
+//}
+#make-grid-columns(@columns: @grid-columns, @gutter: @grid-gutter-width, @breakpoints: @grid-breakpoints) {
+ // Common properties for all breakpoints
+ // LESS PORT: Unfortunately we have to use an actual selector here in order to be able to
+ // `:extend()` it later. This means that the selector is output in the compiled CSS, creating a
+ // small disparity between the Less and Sass versions.
+ .grid-column {
+ position: relative;
+ width: 100%;
+ min-height: 1px; // Prevent columns from collapsing when empty
+ padding-right: (@gutter / 2);
+ padding-left: (@gutter / 2);
+ };
+
+ #each-breakpoint-column(@i: 1) when (@i <= length(@breakpoints)) {
+ @breakpoint: extract(extract(@breakpoints, @i), 1);
+ @infix: breakpoint-infix(@breakpoint, @breakpoints);
+
+ // Allow columns to stretch full width below their breakpoints
+ #each-column(@ii: 1) when (@ii <= @columns) {
+ .col@{infix}-@{ii} {
+ &:extend(.grid-column);
+ }
+
+ #each-column((@ii + 1));
+ } #each-column();
+ .col@{infix},
+ .col@{infix}-auto {
+ &:extend(.grid-column);
+ }
+
+ #media-breakpoint-up(@breakpoint, {
+ // Provide basic `.col-{bp}` classes for equal-width flexbox columns
+ .col@{infix} {
+ flex-basis: 0;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+ .col@{infix}-auto {
+ flex: 0 0 auto;
+ width: auto;
+ max-width: none; // Reset earlier grid tiers
+ }
+
+ #each-column-col(@iii: 1) when (@iii <= @columns) {
+ .col@{infix}-@{iii} {
+ #make-col(@iii, @columns);
+ }
+
+ #each-column-col((@iii + 1));
+ } #each-column-col();
+
+ .order@{infix}-first { order: -1; }
+
+ .order@{infix}-last { order: (@columns + 1); }
+
+ #each-column-order(@iii: 0) when (@iii <= @columns) {
+ .order@{infix}-@{iii} { order: @iii; }
+
+ #each-column-order((@iii + 1));
+ } #each-column-order();
+
+ // `@iii < @columns` because offsetting by the width of an entire row isn't possible
+ #each-column-offset(@iii: 0) when (@iii < @columns) {
+ & when not (@iii = 0),
+ (@iii = 0) and not (@infix = ~"") {
+ .offset@{infix}-@{iii} {
+ #make-col-offset(@iii, @columns);
+ }
+ }
+
+ #each-column-offset((@iii + 1));
+ } #each-column-offset();
+ });
+
+ #each-breakpoint-column((@i + 1));
+ } #each-breakpoint-column();
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_grid.less b/include/thirdparty/Bootstrap4/less/mixins/_grid.less
new file mode 100644
index 0000000..599d99b
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_grid.less
@@ -0,0 +1,71 @@
+/// Grid system
+//
+// Generate semantic grid columns with these mixins.
+
+#make-container() {
+ width: 100%;
+ padding-right: (@grid-gutter-width / 2);
+ padding-left: (@grid-gutter-width / 2);
+ margin-right: auto;
+ margin-left: auto;
+}
+
+
+// For each breakpoint, define the maximum width of the container in a media query
+//@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {
+// @each $breakpoint, $container-max-width in $max-widths {
+// @include media-breakpoint-up($breakpoint, $breakpoints) {
+// max-width: $container-max-width;
+// }
+// }
+//}
+#make-container-max-widths(@max-widths: @container-max-widths, @breakpoints: @grid-breakpoints) {
+ #each-max-width(@i: 1) when (@i <= length(@max-widths)) {
+ @item: extract(@max-widths, @i);
+ @breakpoint: extract(@item, 1);
+ @container-max-width: extract(@item, 2);
+
+ #media-breakpoint-up(@breakpoint, {
+ max-width: @container-max-width;
+ }, @breakpoints);
+
+ #each-max-width((@i + 1));
+ } #each-max-width();
+}
+
+#make-row() {
+ display: flex;
+ flex-wrap: wrap;
+ margin-right: (@grid-gutter-width / -2);
+ margin-left: (@grid-gutter-width / -2);
+}
+
+#make-col-ready() {
+ position: relative;
+ // Prevent columns from becoming too narrow when at smaller grid tiers by
+ // always setting `width: 100%;`. This works because we use `flex` values
+ // later on to override this initial width.
+ width: 100%;
+ min-height: 1px; // Prevent collapsing
+ padding-right: (@grid-gutter-width / 2);
+ padding-left: (@grid-gutter-width / 2);
+}
+
+#make-col(@size, @columns: @grid-columns) {
+ flex: 0 0 percentage((@size / @columns));
+ // Add a `max-width` to ensure content within each column does not blow out
+ // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari
+ // do not appear to require this.
+ max-width: percentage((@size / @columns));
+}
+
+#make-col-offset(@size, @columns: @grid-columns) {
+ @num: (@size / @columns);
+
+ & when (@num = 0) {
+ margin-left: 0;
+ }
+ & when not (@num = 0) {
+ margin-left: percentage(@num);
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_hover.less b/include/thirdparty/Bootstrap4/less/mixins/_hover.less
new file mode 100644
index 0000000..1488fd6
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_hover.less
@@ -0,0 +1,37 @@
+// Hover mixin and `@enable-hover-media-query` are deprecated.
+//
+// Originally added during our alphas and maintained during betas, this mixin was
+// designed to prevent `:hover` stickiness on iOS-an issue where hover styles
+// would persist after initial touch.
+//
+// For backward compatibility, we've kept these mixins and updated them to
+// always return their regular pseudo-classes instead of a shimmed media query.
+//
+// Issue: https://github.com/twbs/bootstrap/issues/25195
+
+#hover(@content) {
+ &:hover { @content(); }
+}
+
+#hover-focus(@content) {
+ &:hover,
+ &:focus {
+ @content();
+ }
+}
+
+#plain-hover-focus(@content) {
+ &,
+ &:hover,
+ &:focus {
+ @content();
+ }
+}
+
+#hover-focus-active(@content) {
+ &:hover,
+ &:focus,
+ &:active {
+ @content();
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_image.less b/include/thirdparty/Bootstrap4/less/mixins/_image.less
new file mode 100644
index 0000000..6a8742b
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_image.less
@@ -0,0 +1,35 @@
+// Image Mixins
+// - Responsive image
+// - Retina image
+
+
+// Responsive image
+//
+// Keep images from scaling beyond the width of their parents.
+
+#img-fluid() {
+ // Part 1: Set a maximum relative to the parent
+ max-width: 100%;
+ // Part 2: Override the height to auto, otherwise images will be stretched
+ // when setting a width and height attribute on the img element.
+ height: auto;
+}
+
+
+// Retina image
+//
+// Short retina mixin for setting background-image and -size.
+
+#img-retina(@file-1x, @file-2x, @width-1x, @height-1x) {
+ background-image: url(@file-1x);
+
+ // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,
+ // but doesn't convert dppx=>dpi.
+ // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.
+ // Compatibility info: https://caniuse.com/#feat=css-media-resolution
+ @media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx
+ only screen and (min-resolution: 2dppx) { // Standardized
+ background-image: url(@file-2x);
+ background-size: @width-1x @height-1x;
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_list-group.less b/include/thirdparty/Bootstrap4/less/mixins/_list-group.less
new file mode 100644
index 0000000..aa54b57
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_list-group.less
@@ -0,0 +1,21 @@
+// List Groups
+
+#list-group-item-variant(@state, @background, @color) {
+ .list-group-item-@{state} {
+ color: @color;
+ background-color: @background;
+
+ &.list-group-item-action {
+ #hover-focus({
+ color: @color;
+ background-color: darken(@background, 5%);
+ });
+
+ &.active {
+ color: @white;
+ background-color: @color;
+ border-color: @color;
+ }
+ }
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_lists.less b/include/thirdparty/Bootstrap4/less/mixins/_lists.less
new file mode 100644
index 0000000..b83ea7e
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_lists.less
@@ -0,0 +1,7 @@
+// Lists
+
+// Unstyled keeps list items block level, just removes default browser padding and list-style
+#list-unstyled() {
+ padding-left: 0;
+ list-style: none;
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_nav-divider.less b/include/thirdparty/Bootstrap4/less/mixins/_nav-divider.less
new file mode 100644
index 0000000..25e5923
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_nav-divider.less
@@ -0,0 +1,10 @@
+// Horizontal dividers
+//
+// Dividers (basically an hr) within dropdowns and nav lists
+
+#nav-divider(@color: @nav-divider-color, @margin-y: @nav-divider-margin-y) {
+ height: 0;
+ margin: @margin-y 0;
+ overflow: hidden;
+ border-top: 1px solid @color;
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_pagination.less b/include/thirdparty/Bootstrap4/less/mixins/_pagination.less
new file mode 100644
index 0000000..0766484
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_pagination.less
@@ -0,0 +1,22 @@
+// Pagination
+
+#pagination-size(@padding-y, @padding-x, @font-size, @line-height, @border-radius) {
+ .page-link {
+ padding: @padding-y @padding-x;
+ font-size: @font-size;
+ line-height: @line-height;
+ }
+
+ .page-item {
+ &:first-child {
+ .page-link {
+ #border-left-radius(@border-radius);
+ }
+ }
+ &:last-child {
+ .page-link {
+ #border-right-radius(@border-radius);
+ }
+ }
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_reset-text.less b/include/thirdparty/Bootstrap4/less/mixins/_reset-text.less
new file mode 100644
index 0000000..d26597d
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_reset-text.less
@@ -0,0 +1,17 @@
+#reset-text() {
+ font-family: @font-family-base;
+ // We deliberately do NOT reset font-size or word-wrap.
+ font-style: normal;
+ font-weight: @font-weight-normal;
+ line-height: @line-height-base;
+ text-align: left; // Fallback for where `start` is not supported
+ 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;
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_resize.less b/include/thirdparty/Bootstrap4/less/mixins/_resize.less
new file mode 100644
index 0000000..f64807c
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_resize.less
@@ -0,0 +1,6 @@
+// Resize anything
+
+#resizable(@direction) {
+ overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible`
+ resize: @direction; // Options: horizontal, vertical, both
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_screen-reader.less b/include/thirdparty/Bootstrap4/less/mixins/_screen-reader.less
new file mode 100644
index 0000000..229d252
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_screen-reader.less
@@ -0,0 +1,33 @@
+// Only display content to screen readers
+//
+// See: https://a11yproject.com/posts/how-to-hide-content
+// See: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/
+
+#sr-only() {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+// Use in conjunction with .sr-only to only display content when it's focused.
+//
+// Useful for "Skip to main content" links; see https://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
+//
+// Credit: HTML5 Boilerplate
+
+#sr-only-focusable() {
+ &:active,
+ &:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ overflow: visible;
+ clip: auto;
+ white-space: normal;
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_size.less b/include/thirdparty/Bootstrap4/less/mixins/_size.less
new file mode 100644
index 0000000..ae0bc16
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_size.less
@@ -0,0 +1,6 @@
+// Sizing shortcuts
+
+#size(@width, @height: @width) {
+ width: @width;
+ height: @height;
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_table-row.less b/include/thirdparty/Bootstrap4/less/mixins/_table-row.less
new file mode 100644
index 0000000..377b820
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_table-row.less
@@ -0,0 +1,30 @@
+// Tables
+
+#table-row-variant(@state, @background) {
+ // Exact selectors below required to override `.table-striped` and prevent
+ // inheritance to nested tables.
+ .table-@{state} {
+ &,
+ > th,
+ > td {
+ background-color: @background;
+ }
+ }
+
+ // Hover states for `.table-hover`
+ // Note: this is not available for cells or rows within `thead` or `tfoot`.
+ .table-hover {
+ @hover-background: darken(@background, 5%);
+
+ .table-@{state} {
+ #hover({
+ background-color: @hover-background;
+
+ > td,
+ > th {
+ background-color: @hover-background;
+ }
+ });
+ }
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_text-emphasis.less b/include/thirdparty/Bootstrap4/less/mixins/_text-emphasis.less
new file mode 100644
index 0000000..d0f003e
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_text-emphasis.less
@@ -0,0 +1,12 @@
+// Typography
+
+#text-emphasis-variant(@parent, @color) {
+ @{parent} {
+ color: @color !important;
+ }
+ a@{parent} {
+ #hover-focus({
+ color: darken(@color, 10%) !important;
+ });
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_text-hide.less b/include/thirdparty/Bootstrap4/less/mixins/_text-hide.less
new file mode 100644
index 0000000..921d11d
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_text-hide.less
@@ -0,0 +1,12 @@
+// CSS image replacement
+#text-hide(@ignore-warning) {
+ font: ~"0/0 a";
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+
+ & when not (@ignore-warning) {
+ // @warn "The `text-hide()` mixin has been deprecated as of v4.1.0. It will be removed entirely in v5.";
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_text-truncate.less b/include/thirdparty/Bootstrap4/less/mixins/_text-truncate.less
new file mode 100644
index 0000000..4acf100
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_text-truncate.less
@@ -0,0 +1,8 @@
+// Text truncate
+// Requires inline-block or block for proper styling
+
+#text-truncate() {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_transition.less b/include/thirdparty/Bootstrap4/less/mixins/_transition.less
new file mode 100644
index 0000000..6cdc254
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_transition.less
@@ -0,0 +1,35 @@
+//@mixin transition($transition...) {
+// @if $enable-transitions {
+// @if length($transition) == 0 {
+// transition: $transition-base;
+// } @else {
+// transition: $transition;
+// }
+// }
+//}
+#transition(@transition...) when (@enable-transitions) {
+ & when (length(@transition) = 0) {
+ transition: @transition-base;
+ }
+ // LESS PORT: In order to output the transitions correctly we have to iterate over the list and
+ // use Less’s merge feature. Without this, transitions will be output space-separated instead of
+ // comma-separated. Also, since a single transition can be misinterpreted as multiple transitions
+ // (since it will have a length > 1) we have to include a check for the length of the first item
+ // in the list. If the length is greater than 1, then we have a list of separate transitions. If
+ // the the length is 1, then we’re looking at the first value of a single transition, so we
+ // output `@transition` as-is.
+ & when (length(@transition) > 0) and (length(extract(@transition, 1)) = 1) {
+ transition: @transition;
+ }
+ & when (length(@transition) > 0) and (length(extract(@transition, 1)) > 1) {
+ #each-transition(@i: 1) when (@i =< length(@transition)) {
+ transition+: extract(@transition, @i);
+
+ #each-transition((@i + 1));
+ } #each-transition();
+ }
+
+ @media screen and (prefers-reduced-motion: reduce) {
+ transition: none;
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/mixins/_visibility.less b/include/thirdparty/Bootstrap4/less/mixins/_visibility.less
new file mode 100644
index 0000000..d982d60
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/mixins/_visibility.less
@@ -0,0 +1,5 @@
+// Visibility
+
+#invisible(@visibility) {
+ visibility: @visibility !important;
+}
diff --git a/include/thirdparty/Bootstrap4/less/plugins/breakpoints.js b/include/thirdparty/Bootstrap4/less/plugins/breakpoints.js
new file mode 100644
index 0000000..1bdb078
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/plugins/breakpoints.js
@@ -0,0 +1,117 @@
+let gridBreakpoints = {}
+
+//
+// Helper Functions
+//
+
+function lookupVariable(context, variableName) {
+ const { frames, importantScope } = context
+
+ return tree.Variable.prototype.find(frames, frame => {
+ const { value, important } = frame.variable(variableName) || {}
+
+ if (value === undefined)
+ return
+
+ if (important && importantScope[importantScope.length - 1])
+ importantScope[importantScope.length - 1].important = important
+
+ return value.eval(context)
+ })
+}
+
+function listToMap({ value: list } = { value: [] }) {
+ const map = {}
+
+ // Handle maps that only have one key/value pair (since they will look like a plain list of
+ // length 2).
+ if (list.length === 2 && ! Array.isArray(list[0].value)) {
+ const [{ value: key }, value] = list || [{}]
+
+ map[key] = value
+ } else
+ list.forEach(({ value: item } = {}) => {
+ if (Array.isArray(item)) {
+ const [{ value: key }, value] = item || [{}]
+
+ map[`${key}`] = value
+ }
+ })
+
+ return map
+}
+
+function getBreakpoints(context, breakpoints) {
+ if (! breakpoints) {
+ if (Object.keys(gridBreakpoints).length === 0)
+ gridBreakpoints = lookupVariable(context, '@grid-breakpoints')
+
+ breakpoints = gridBreakpoints
+ }
+
+ return listToMap(breakpoints)
+}
+
+//
+// Less Functions
+//
+
+functions.add('breakpoint-next', function ({ value: breakpointName }, breakpoints) {
+ const breakpointsMap = getBreakpoints(this.context, breakpoints)
+ const breakpointNames = Object.keys(breakpointsMap)
+ const breakpointIndex = breakpointNames.indexOf(breakpointName)
+
+ if (breakpointIndex === -1)
+ return new tree.Quoted('"')
+
+ // Next breakpoint is null for the last breakpoint.
+ if ((breakpointIndex + 1) === breakpointNames.length)
+ return new tree.Quoted('"')
+
+ return new tree.Quoted('"', breakpointNames[breakpointIndex + 1])
+})
+
+functions.add('breakpoint-min', function ({ value: breakpointName }, breakpoints) {
+ const breakpointsMap = getBreakpoints(this.context, breakpoints)
+ const breakpointNames = Object.keys(breakpointsMap)
+ const breakpointIndex = breakpointNames.indexOf(breakpointName)
+
+ if (breakpointIndex === -1)
+ return new tree.Quoted('"')
+
+ // Minumum breakpoint width is null for the first breakpoint.
+ if (breakpointIndex === 0)
+ return new tree.Quoted('"')
+
+ return breakpointsMap[breakpointName]
+})
+
+functions.add('breakpoint-max', function ({ value: breakpointName }, breakpoints) {
+ const breakpointsMap = getBreakpoints(this.context, breakpoints)
+ const breakpointNames = Object.keys(breakpointsMap)
+ const breakpointIndex = breakpointNames.indexOf(breakpointName)
+
+ if (breakpointIndex === -1)
+ return new tree.Quoted('"')
+
+ // Maximum breakpoint width is null for the last breakpoint.
+ if ((breakpointIndex + 1) === breakpointNames.length)
+ return new tree.Quoted('"')
+
+ return new tree.Dimension((breakpointsMap[breakpointNames[breakpointIndex + 1]].value - 0.02), 'px')
+})
+
+functions.add('breakpoint-infix', function ({ value: breakpointName }, breakpoints) {
+ const breakpointsMap = getBreakpoints(this.context, breakpoints)
+ const breakpointNames = Object.keys(breakpointsMap)
+ const breakpointIndex = breakpointNames.indexOf(breakpointName)
+
+ if (breakpointIndex === -1)
+ return new tree.Quoted('"')
+
+ // Breakpoint infix is null the first breakpoint.
+ if (breakpointIndex === 0)
+ return new tree.Quoted('"')
+
+ return new tree.Quoted('"', `-${breakpointName}`)
+})
diff --git a/include/thirdparty/Bootstrap4/less/plugins/color-yiq.js b/include/thirdparty/Bootstrap4/less/plugins/color-yiq.js
new file mode 100644
index 0000000..04fe883
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/plugins/color-yiq.js
@@ -0,0 +1,40 @@
+let yiqThreshold = 0
+let yiqTextDark = ''
+let yiqTextLight = ''
+
+//
+// Helper Functions
+//
+
+function lookupVariable(context, variableName) {
+ const { frames, importantScope } = context
+
+ return tree.Variable.prototype.find(frames, frame => {
+ const { value, important } = frame.variable(variableName) || {}
+
+ if (value === undefined)
+ return
+
+ if (important && importantScope[importantScope.length - 1])
+ importantScope[importantScope.length - 1].important = important
+
+ return value.eval(context)
+ })
+}
+
+//
+// Less Functions
+//
+
+functions.add('color-yiq', function ({ rgb: [r, g, b] }) {
+ const yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000
+
+ if (yiqThreshold === 0)
+ yiqThreshold = lookupVariable(this.context, '@yiq-contrasted-threshold').value
+ if (yiqTextDark === '')
+ yiqTextDark = lookupVariable(this.context, '@yiq-text-dark')
+ if (yiqTextLight === '')
+ yiqTextLight = lookupVariable(this.context, '@yiq-text-light')
+
+ return (yiq >= yiqThreshold) ? yiqTextDark : yiqTextLight
+})
diff --git a/include/thirdparty/Bootstrap4/less/plugins/color.js b/include/thirdparty/Bootstrap4/less/plugins/color.js
new file mode 100644
index 0000000..bdfc84b
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/plugins/color.js
@@ -0,0 +1,54 @@
+let colors = {}
+
+//
+// Helper Functions
+//
+
+function lookupVariable(context, variableName) {
+ const { frames, importantScope } = context
+
+ return tree.Variable.prototype.find(frames, frame => {
+ const { value, important } = frame.variable(variableName) || {}
+
+ if (value === undefined)
+ return
+
+ if (important && importantScope[importantScope.length - 1])
+ importantScope[importantScope.length - 1].important = important
+
+ return value.eval(context)
+ })
+}
+
+function listToMap({ value: list } = { value: [] }) {
+ const map = {}
+
+ // Handle maps that only have one key/value pair (since they will look like a plain list of
+ // length 2).
+ if (list.length === 2 && ! Array.isArray(list[0].value)) {
+ const [{ value: key }, value] = list || [{}]
+
+ map[key] = value
+ } else
+ list.forEach(({ value: item } = {}) => {
+ if (Array.isArray(item)) {
+ const [{ value: key }, value] = item || [{}]
+
+ map[`${key}`] = value
+ }
+ })
+
+ return map
+}
+
+//
+// Less Functions
+//
+
+functions.add('color', function ({ value: colorName } = { value: 'blue' }) {
+ // If `colors` hasn’t been defined yet, set it to the value of `@colors`.
+ if (Object.keys(colors).length === 0)
+ colors = listToMap(lookupVariable(this.context, '@colors'))
+
+ return colors[colorName]
+})
diff --git a/include/thirdparty/Bootstrap4/less/plugins/gray.js b/include/thirdparty/Bootstrap4/less/plugins/gray.js
new file mode 100644
index 0000000..0103116
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/plugins/gray.js
@@ -0,0 +1,54 @@
+let grays = {}
+
+//
+// Helper Functions
+//
+
+function lookupVariable(context, variableName) {
+ const { frames, importantScope } = context
+
+ return tree.Variable.prototype.find(frames, frame => {
+ const { value, important } = frame.variable(variableName) || {}
+
+ if (value === undefined)
+ return
+
+ if (important && importantScope[importantScope.length - 1])
+ importantScope[importantScope.length - 1].important = important
+
+ return value.eval(context)
+ })
+}
+
+function listToMap({ value: list } = { value: [] }) {
+ const map = {}
+
+ // Handle maps that only have one key/value pair (since they will look like a plain list of
+ // length 2).
+ if (list.length === 2 && ! Array.isArray(list[0].value)) {
+ const [{ value: key }, value] = list || [{}]
+
+ map[key] = value
+ } else
+ list.forEach(({ value: item } = {}) => {
+ if (Array.isArray(item)) {
+ const [{ value: key }, value] = item || [{}]
+
+ map[`${key}`] = value
+ }
+ })
+
+ return map
+}
+
+//
+// Less Functions
+//
+
+functions.add('gray', function ({ value: colorName } = { value: '100' }) {
+ // If `grays` hasn’t been defined yet, set it to the value of `@grays`.
+ if (Object.keys(grays).length === 0)
+ grays = listToMap(lookupVariable(this.context, '@grays'))
+
+ return grays[colorName]
+})
diff --git a/include/thirdparty/Bootstrap4/less/plugins/index.js b/include/thirdparty/Bootstrap4/less/plugins/index.js
new file mode 100644
index 0000000..d72087c
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/plugins/index.js
@@ -0,0 +1,17 @@
+//
+// Less Functions
+//
+
+// NOTE: Unlike the native JS `indexOf`, this function actually returns `0` if the value is not
+// found in the list/map, since Less lists are 1-indexed.
+functions.add('index', function ({ value: list }, { value: searchValue }) {
+ const index = list.findIndex(({ value: item } = {}) => {
+ // Check if the current item is an array, (in case the current list is actually a map).
+ if (Array.isArray(item))
+ return (item[0].value === searchValue)
+
+ return (item === searchValue)
+ })
+
+ return new tree.Dimension(index + 1)
+})
diff --git a/include/thirdparty/Bootstrap4/less/plugins/map-get.js b/include/thirdparty/Bootstrap4/less/plugins/map-get.js
new file mode 100644
index 0000000..accd06a
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/plugins/map-get.js
@@ -0,0 +1,37 @@
+//
+// Helper Functions
+//
+
+function listToMap({ value: list } = { value: [] }) {
+ const map = {}
+
+ // Handle maps that only have one key/value pair (since they will look like a plain list of
+ // length 2).
+ if (list.length === 2 && ! Array.isArray(list[0].value)) {
+ const [{ value: key }, value] = list || [{}]
+
+ map[key] = value
+ } else
+ list.forEach(({ value: item } = {}) => {
+ if (Array.isArray(item)) {
+ const [{ value: key }, value] = item || [{}]
+
+ map[`${key}`] = value
+ }
+ })
+
+ return map
+}
+
+//
+// Less Functions
+//
+
+functions.add('map-get', function (list, { value: key } = { value: '' }) {
+ const map = listToMap(list)
+
+ if (key && map[key])
+ return map[key]
+
+ return new tree.Quoted('"')
+})
diff --git a/include/thirdparty/Bootstrap4/less/plugins/theme-color-level.js b/include/thirdparty/Bootstrap4/less/plugins/theme-color-level.js
new file mode 100644
index 0000000..b2f4e9d
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/plugins/theme-color-level.js
@@ -0,0 +1,75 @@
+let themeColors = {}
+
+//
+// Helper Functions
+//
+
+function lookupVariable(context, variableName) {
+ const { frames, importantScope } = context
+
+ return tree.Variable.prototype.find(frames, frame => {
+ const { value, important } = frame.variable(variableName) || {}
+
+ if (value === undefined)
+ return
+
+ if (important && importantScope[importantScope.length - 1])
+ importantScope[importantScope.length - 1].important = important
+
+ return value.eval(context)
+ })
+}
+
+function listToMap({ value: list } = { value: [] }) {
+ const map = {}
+
+ // Handle maps that only have one key/value pair (since they will look like a plain list of
+ // length 2).
+ if (list.length === 2 && ! Array.isArray(list[0].value)) {
+ const [{ value: key }, value] = list || [{}]
+
+ map[key] = value
+ } else
+ list.forEach(({ value: item } = {}) => {
+ if (Array.isArray(item)) {
+ const [{ value: key }, value] = item || [{}]
+
+ map[`${key}`] = value
+ }
+ })
+
+ return map
+}
+
+//
+// Less Functions
+//
+
+functions.add('theme-color-level', function ({ value: colorName }, { value: level } = { value: 0 }) {
+ const context = this.context
+ const themeColorInterval = lookupVariable(context, '@theme-color-interval').value
+ const black = lookupVariable(context, '@black').toCSS().substr(1)
+ const white = lookupVariable(context, '@white').toCSS().substr(1)
+ const mix = context.pluginManager.less.functions.functionRegistry.get('mix')
+
+ // If `themeColors` hasn’t been defined yet, set it to the value of `@theme-colors`.
+ if (Object.keys(themeColors).length === 0)
+ themeColors = listToMap(lookupVariable(context, '@theme-colors'))
+
+ const color = themeColors[colorName]
+ const colorBase = new tree.Color(level > 0 ? black : white)
+ const mixPercent = new tree.Dimension(Math.abs(level * themeColorInterval) + '%')
+
+ if (! color)
+ throw new ReferenceError(`\n\n\tColor “${colorName}” is not present in the \`@theme-colors\` map.\n\n`)
+
+ // In order to match the output of the Sass version, we have to convert the color into a hex
+ // string and then use that value to create a new Less `Color`.
+ //
+ // NOTE: This doesn’t work every time but it does most of the time. There are still some color
+ // output discrepancies between the Less and Sass versions, but the differences are so small
+ // they’re imperceptible.
+ const colorResult = mix(colorBase, color, mixPercent)
+
+ return new tree.Color(colorResult.toCSS().substr(1))
+})
diff --git a/include/thirdparty/Bootstrap4/less/plugins/theme-color.js b/include/thirdparty/Bootstrap4/less/plugins/theme-color.js
new file mode 100644
index 0000000..77c1006
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/plugins/theme-color.js
@@ -0,0 +1,54 @@
+let themeColors = {}
+
+//
+// Helper Functions
+//
+
+function lookupVariable(context, variableName) {
+ const { frames, importantScope } = context
+
+ return tree.Variable.prototype.find(frames, frame => {
+ const { value, important } = frame.variable(variableName) || {}
+
+ if (value === undefined)
+ return
+
+ if (important && importantScope[importantScope.length - 1])
+ importantScope[importantScope.length - 1].important = important
+
+ return value.eval(context)
+ })
+}
+
+function listToMap({ value: list } = { value: [] }) {
+ const map = {}
+
+ // Handle maps that only have one key/value pair (since they will look like a plain list of
+ // length 2).
+ if (list.length === 2 && ! Array.isArray(list[0].value)) {
+ const [{ value: key }, value] = list || [{}]
+
+ map[key] = value
+ } else
+ list.forEach(({ value: item } = {}) => {
+ if (Array.isArray(item)) {
+ const [{ value: key }, value] = item || [{}]
+
+ map[`${key}`] = value
+ }
+ })
+
+ return map
+}
+
+//
+// Less Functions
+//
+
+functions.add('theme-color', function ({ value: colorName } = { value: 'primary' }) {
+ // If `themeColors` hasn’t been defined yet, set it to the value of `@theme-colors`.
+ if (Object.keys(themeColors).length === 0)
+ themeColors = listToMap(lookupVariable(this.context, '@theme-colors'))
+
+ return themeColors[colorName]
+})
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_align.less b/include/thirdparty/Bootstrap4/less/utilities/_align.less
new file mode 100644
index 0000000..4dbbbc2
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_align.less
@@ -0,0 +1,6 @@
+.align-baseline { vertical-align: baseline !important; } // Browser default
+.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; }
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_background.less b/include/thirdparty/Bootstrap4/less/utilities/_background.less
new file mode 100644
index 0000000..307b76f
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_background.less
@@ -0,0 +1,36 @@
+//@each $color, $value in $theme-colors {
+// @include bg-variant('.bg-#{$color}', $value);
+//}
+#each-theme-color-bg(@i: 1) when (@i =< length(@theme-colors)) {
+ @item: extract(@theme-colors, @i);
+ @color: extract(@item, 1);
+ @value: extract(@item, 2);
+
+ #bg-variant(~".bg-@{color}", @value);
+
+ #each-theme-color-bg((@i + 1));
+} #each-theme-color-bg();
+
+//@if $enable-gradients {
+// @each $color, $value in $theme-colors {
+// @include bg-gradient-variant(".bg-gradient-#{$color}", $value);
+// }
+//}
+#each-theme-color-bg-gradient(@i: 1) when (@i =< length(@theme-colors)) and (@enable-gradients) {
+ @item: extract(@theme-colors, @i);
+ @color: extract(@item, 1);
+ @value: extract(@item, 2);
+
+ #bg-gradient-variant(~".bg-gradient-@{color}", @value);
+
+ #each-theme-color-bg-gradient((@i + 1));
+} #each-theme-color-bg-gradient();
+
+
+.bg-white {
+ background-color: @white !important;
+}
+
+.bg-transparent {
+ background-color: transparent !important;
+}
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_borders.less b/include/thirdparty/Bootstrap4/less/utilities/_borders.less
new file mode 100644
index 0000000..5cd89bc
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_borders.less
@@ -0,0 +1,68 @@
+//
+// Border
+//
+
+.border { border: @border-width solid @border-color !important; }
+.border-top { border-top: @border-width solid @border-color !important; }
+.border-right { border-right: @border-width solid @border-color !important; }
+.border-bottom { border-bottom: @border-width solid @border-color !important; }
+.border-left { border-left: @border-width solid @border-color !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; }
+
+//@each $color, $value in $theme-colors {
+// .border-#{$color} {
+// border-color: $value !important;
+// }
+//}
+#each-theme-color-border(@i: 1) when (@i =< length(@theme-colors)) {
+ @item: extract(@theme-colors, @i);
+ @color: extract(@item, 1);
+ @value: extract(@item, 2);
+
+ .border-@{color} {
+ border-color: @value !important;
+ }
+
+ #each-theme-color-border((@i + 1));
+} #each-theme-color-border();
+
+.border-white {
+ border-color: @white !important;
+}
+
+//
+// Border-radius
+//
+
+.rounded {
+ border-radius: @border-radius !important;
+}
+.rounded-top {
+ border-top-left-radius: @border-radius !important;
+ border-top-right-radius: @border-radius !important;
+}
+.rounded-right {
+ border-top-right-radius: @border-radius !important;
+ border-bottom-right-radius: @border-radius !important;
+}
+.rounded-bottom {
+ border-bottom-right-radius: @border-radius !important;
+ border-bottom-left-radius: @border-radius !important;
+}
+.rounded-left {
+ border-top-left-radius: @border-radius !important;
+ border-bottom-left-radius: @border-radius !important;
+}
+
+.rounded-circle {
+ border-radius: 50% !important;
+}
+
+.rounded-0 {
+ border-radius: 0 !important;
+}
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_clearfix.less b/include/thirdparty/Bootstrap4/less/utilities/_clearfix.less
new file mode 100644
index 0000000..444c228
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_clearfix.less
@@ -0,0 +1,3 @@
+.clearfix {
+ #clearfix();
+}
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_display.less b/include/thirdparty/Bootstrap4/less/utilities/_display.less
new file mode 100644
index 0000000..c35dd11
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_display.less
@@ -0,0 +1,54 @@
+//
+// Utilities for common `display` values
+//
+
+//@each $breakpoint in map-keys($grid-breakpoints) {
+// @include media-breakpoint-up($breakpoint) {
+// $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+//
+// .d#{$infix}-none { display: none !important; }
+// .d#{$infix}-inline { display: inline !important; }
+// .d#{$infix}-inline-block { display: inline-block !important; }
+// .d#{$infix}-block { display: block !important; }
+// .d#{$infix}-table { display: table !important; }
+// .d#{$infix}-table-cell { display: table-cell !important; }
+// .d#{$infix}-flex { display: flex !important; }
+// .d#{$infix}-inline-flex { display: inline-flex !important; }
+// }
+//}
+#each-breakpoint-display(@i: 1) when (@i =< length(@grid-breakpoints)) {
+ @breakpoint: extract(extract(@grid-breakpoints, @i), 1);
+
+ #media-breakpoint-up(@breakpoint, {
+ @infix: breakpoint-infix(@breakpoint, @grid-breakpoints);
+
+ .d@{infix}-none { display: none !important; }
+ .d@{infix}-inline { display: inline !important; }
+ .d@{infix}-inline-block { display: inline-block !important; }
+ .d@{infix}-block { display: block !important; }
+ .d@{infix}-table { display: table !important; }
+ .d@{infix}-table-row { display: table-row !important; }
+ .d@{infix}-table-cell { display: table-cell !important; }
+ .d@{infix}-flex { display: flex !important; }
+ .d@{infix}-inline-flex { display: inline-flex !important; }
+ });
+
+ #each-breakpoint-display((@i + 1));
+} #each-breakpoint-display();
+
+
+//
+// Utilities for toggling `display` in print
+//
+
+@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: flex !important; }
+ .d-print-inline-flex { display: inline-flex !important; }
+}
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_embed.less b/include/thirdparty/Bootstrap4/less/utilities/_embed.less
new file mode 100644
index 0000000..a65741c
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_embed.less
@@ -0,0 +1,52 @@
+// Credit: Nicolas Gallagher and SUIT CSS.
+
+.embed-responsive {
+ position: relative;
+ display: block;
+ width: 100%;
+ padding: 0;
+ overflow: hidden;
+
+ &::before {
+ display: block;
+ content: "";
+ }
+
+ .embed-responsive-item,
+ iframe,
+ embed,
+ object,
+ video {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: 0;
+ }
+}
+
+.embed-responsive-21by9 {
+ &::before {
+ padding-top: percentage((9 / 21));
+ }
+}
+
+.embed-responsive-16by9 {
+ &::before {
+ padding-top: percentage((9 / 16));
+ }
+}
+
+.embed-responsive-4by3 {
+ &::before {
+ padding-top: percentage((3 / 4));
+ }
+}
+
+.embed-responsive-1by1 {
+ &::before {
+ padding-top: percentage((1 / 1));
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_flex.less b/include/thirdparty/Bootstrap4/less/utilities/_flex.less
new file mode 100644
index 0000000..8c595fb
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_flex.less
@@ -0,0 +1,98 @@
+// Flex variation
+//
+// Custom styles for additional flex alignment options.
+
+//@each $breakpoint in map-keys($grid-breakpoints) {
+// @include media-breakpoint-up($breakpoint) {
+// $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+//
+// .flex#{$infix}-row { flex-direction: row !important; }
+// .flex#{$infix}-column { flex-direction: column !important; }
+// .flex#{$infix}-row-reverse { flex-direction: row-reverse !important; }
+// .flex#{$infix}-column-reverse { flex-direction: column-reverse !important; }
+//
+// .flex#{$infix}-wrap { flex-wrap: wrap !important; }
+// .flex#{$infix}-nowrap { flex-wrap: nowrap !important; }
+// .flex#{$infix}-wrap-reverse { flex-wrap: wrap-reverse !important; }
+// .flex#{$infix}-fill { flex: 1 1 auto !important; }
+// .flex#{$infix}-grow-0 { flex-grow: 0 !important; }
+// .flex#{$infix}-grow-1 { flex-grow: 1 !important; }
+// .flex#{$infix}-shrink-0 { flex-shrink: 0 !important; }
+// .flex#{$infix}-shrink-1 { flex-shrink: 1 !important; }
+//
+// .justify-content#{$infix}-start { justify-content: flex-start !important; }
+// .justify-content#{$infix}-end { justify-content: flex-end !important; }
+// .justify-content#{$infix}-center { justify-content: center !important; }
+// .justify-content#{$infix}-between { justify-content: space-between !important; }
+// .justify-content#{$infix}-around { justify-content: space-around !important; }
+//
+// .align-items#{$infix}-start { align-items: flex-start !important; }
+// .align-items#{$infix}-end { align-items: flex-end !important; }
+// .align-items#{$infix}-center { align-items: center !important; }
+// .align-items#{$infix}-baseline { align-items: baseline !important; }
+// .align-items#{$infix}-stretch { align-items: stretch !important; }
+//
+// .align-content#{$infix}-start { align-content: flex-start !important; }
+// .align-content#{$infix}-end { align-content: flex-end !important; }
+// .align-content#{$infix}-center { align-content: center !important; }
+// .align-content#{$infix}-between { align-content: space-between !important; }
+// .align-content#{$infix}-around { align-content: space-around !important; }
+// .align-content#{$infix}-stretch { align-content: stretch !important; }
+//
+// .align-self#{$infix}-auto { align-self: auto !important; }
+// .align-self#{$infix}-start { align-self: flex-start !important; }
+// .align-self#{$infix}-end { align-self: flex-end !important; }
+// .align-self#{$infix}-center { align-self: center !important; }
+// .align-self#{$infix}-baseline { align-self: baseline !important; }
+// .align-self#{$infix}-stretch { align-self: stretch !important; }
+// }
+//}
+#each-breakpoint-flex(@i: 1) when (@i =< length(@grid-breakpoints)) {
+ @breakpoint: extract(extract(@grid-breakpoints, @i), 1);
+
+ #media-breakpoint-up(@breakpoint, {
+ @infix: breakpoint-infix(@breakpoint, @grid-breakpoints);
+
+ .flex@{infix}-row { flex-direction: row !important; }
+ .flex@{infix}-column { flex-direction: column !important; }
+ .flex@{infix}-row-reverse { flex-direction: row-reverse !important; }
+ .flex@{infix}-column-reverse { flex-direction: column-reverse !important; }
+
+ .flex@{infix}-wrap { flex-wrap: wrap !important; }
+ .flex@{infix}-nowrap { flex-wrap: nowrap !important; }
+ .flex@{infix}-wrap-reverse { flex-wrap: wrap-reverse !important; }
+ .flex@{infix}-fill { flex: 1 1 auto !important; }
+ .flex@{infix}-grow-0 { flex-grow: 0 !important; }
+ .flex@{infix}-grow-1 { flex-grow: 1 !important; }
+ .flex@{infix}-shrink-0 { flex-shrink: 0 !important; }
+ .flex@{infix}-shrink-1 { flex-shrink: 1 !important; }
+
+ .justify-content@{infix}-start { justify-content: flex-start !important; }
+ .justify-content@{infix}-end { justify-content: flex-end !important; }
+ .justify-content@{infix}-center { justify-content: center !important; }
+ .justify-content@{infix}-between { justify-content: space-between !important; }
+ .justify-content@{infix}-around { justify-content: space-around !important; }
+
+ .align-items@{infix}-start { align-items: flex-start !important; }
+ .align-items@{infix}-end { align-items: flex-end !important; }
+ .align-items@{infix}-center { align-items: center !important; }
+ .align-items@{infix}-baseline { align-items: baseline !important; }
+ .align-items@{infix}-stretch { align-items: stretch !important; }
+
+ .align-content@{infix}-start { align-content: flex-start !important; }
+ .align-content@{infix}-end { align-content: flex-end !important; }
+ .align-content@{infix}-center { align-content: center !important; }
+ .align-content@{infix}-between { align-content: space-between !important; }
+ .align-content@{infix}-around { align-content: space-around !important; }
+ .align-content@{infix}-stretch { align-content: stretch !important; }
+
+ .align-self@{infix}-auto { align-self: auto !important; }
+ .align-self@{infix}-start { align-self: flex-start !important; }
+ .align-self@{infix}-end { align-self: flex-end !important; }
+ .align-self@{infix}-center { align-self: center !important; }
+ .align-self@{infix}-baseline { align-self: baseline !important; }
+ .align-self@{infix}-stretch { align-self: stretch !important; }
+ });
+
+ #each-breakpoint-flex((@i + 1));
+} #each-breakpoint-flex();
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_float.less b/include/thirdparty/Bootstrap4/less/utilities/_float.less
new file mode 100644
index 0000000..ffb5bab
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_float.less
@@ -0,0 +1,22 @@
+//@each $breakpoint in map-keys($grid-breakpoints) {
+// @include media-breakpoint-up($breakpoint) {
+// $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+//
+// .float#{$infix}-left { @include float-left; }
+// .float#{$infix}-right { @include float-right; }
+// .float#{$infix}-none { @include float-none; }
+// }
+//}
+#each-breakpoint-float(@i: 1) when (@i =< length(@grid-breakpoints)) {
+ @breakpoint: extract(extract(@grid-breakpoints, @i), 1);
+
+ #media-breakpoint-up(@breakpoint, {
+ @infix: breakpoint-infix(@breakpoint, @grid-breakpoints);
+
+ .float@{infix}-left { #float-left(); }
+ .float@{infix}-right { #float-right(); }
+ .float@{infix}-none { #float-none(); }
+ });
+
+ #each-breakpoint-float((@i + 1));
+} #each-breakpoint-float();
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_position.less b/include/thirdparty/Bootstrap4/less/utilities/_position.less
new file mode 100644
index 0000000..42821de
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_position.less
@@ -0,0 +1,41 @@
+// Common values
+
+// Less list not in variables since it's not intended for customization.
+@positions: static, relative, absolute, fixed, sticky;
+
+//@each $position in $positions {
+// .position-#{$position} { position: $position !important; }
+//}
+#each-position(@i: 1) when (@i =< length(@positions)) {
+ @position: extract(@positions, @i);
+
+ .position-@{position} { position: @position !important; }
+
+ #each-position((@i + 1));
+} #each-position();
+
+// Shorthand
+
+.fixed-top {
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: @zindex-fixed;
+}
+
+.fixed-bottom {
+ position: fixed;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: @zindex-fixed;
+}
+
+.sticky-top {
+ @supports (position: sticky) {
+ position: sticky;
+ top: 0;
+ z-index: @zindex-sticky;
+ }
+}
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_screenreaders.less b/include/thirdparty/Bootstrap4/less/utilities/_screenreaders.less
new file mode 100644
index 0000000..4557ffc
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_screenreaders.less
@@ -0,0 +1,11 @@
+//
+// Screenreaders
+//
+
+.sr-only {
+ #sr-only();
+}
+
+.sr-only-focusable {
+ #sr-only-focusable();
+}
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_shadows.less b/include/thirdparty/Bootstrap4/less/utilities/_shadows.less
new file mode 100644
index 0000000..295d4d3
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_shadows.less
@@ -0,0 +1,4 @@
+.shadow-sm { box-shadow: @box-shadow-sm !important; }
+.shadow { box-shadow: @box-shadow !important; }
+.shadow-lg { box-shadow: @box-shadow-lg !important; }
+.shadow-none { box-shadow: none !important; }
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_sizing.less b/include/thirdparty/Bootstrap4/less/utilities/_sizing.less
new file mode 100644
index 0000000..647c77d
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_sizing.less
@@ -0,0 +1,29 @@
+// Width and height
+
+//@each $prop, $abbrev in (width: w, height: h) {
+// @each $size, $length in $sizes {
+// .#{$abbrev}-#{$size} { #{$prop}: $length !important; }
+// }
+//}
+@sizing-properties: width w, height h;
+
+#each-sizing-property(@i: 1) when (@i =< length(@sizing-properties)) {
+ @sizing-property: extract(@sizing-properties, @i);
+ @prop: extract(@sizing-property, 1);
+ @abbrev: extract(@sizing-property, 2);
+
+ #each-size(@ii: 1) when (@ii =< length(@sizes)) {
+ @item: extract(@sizes, @ii);
+ @size: extract(@item, 1);
+ @length: extract(@item, 2);
+
+ .@{abbrev}-@{size} { @{prop}: @length !important; }
+
+ #each-size((@ii + 1));
+ } #each-size();
+
+ #each-sizing-property((@i + 1));
+} #each-sizing-property();
+
+.mw-100 { max-width: 100% !important; }
+.mh-100 { max-height: 100% !important; }
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_spacing.less b/include/thirdparty/Bootstrap4/less/utilities/_spacing.less
new file mode 100644
index 0000000..d95f8f9
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_spacing.less
@@ -0,0 +1,113 @@
+// Margin and Padding
+
+//@each $breakpoint in map-keys($grid-breakpoints) {
+// @include media-breakpoint-up($breakpoint) {
+// $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+//
+// @each $prop, $abbrev in (margin: m, padding: p) {
+// @each $size, $length in $spacers {
+//
+// .#{$abbrev}#{$infix}-#{$size} { #{$prop}: $length !important; }
+// .#{$abbrev}t#{$infix}-#{$size},
+// .#{$abbrev}y#{$infix}-#{$size} {
+// #{$prop}-top: $length !important;
+// }
+// .#{$abbrev}r#{$infix}-#{$size},
+// .#{$abbrev}x#{$infix}-#{$size} {
+// #{$prop}-right: $length !important;
+// }
+// .#{$abbrev}b#{$infix}-#{$size},
+// .#{$abbrev}y#{$infix}-#{$size} {
+// #{$prop}-bottom: $length !important;
+// }
+// .#{$abbrev}l#{$infix}-#{$size},
+// .#{$abbrev}x#{$infix}-#{$size} {
+// #{$prop}-left: $length !important;
+// }
+// }
+// }
+//
+// // Some special margin utils
+// .m#{$infix}-auto { margin: auto !important; }
+// .mt#{$infix}-auto,
+// .my#{$infix}-auto {
+// margin-top: auto !important;
+// }
+// .mr#{$infix}-auto,
+// .mx#{$infix}-auto {
+// margin-right: auto !important;
+// }
+// .mb#{$infix}-auto,
+// .my#{$infix}-auto {
+// margin-bottom: auto !important;
+// }
+// .ml#{$infix}-auto,
+// .mx#{$infix}-auto {
+// margin-left: auto !important;
+// }
+// }
+//}
+#each-breakpoint-spacing(@i: 1) when (@i =< length(@grid-breakpoints)) {
+ @breakpoint: extract(extract(@grid-breakpoints, @i), 1);
+
+ #media-breakpoint-up(@breakpoint, {
+ @infix: breakpoint-infix(@breakpoint, @grid-breakpoints);
+
+ @spacing-properties: margin m, padding p;
+
+ #each-spacing-property(@ii: 1) when (@ii =< length(@spacing-properties)) {
+ @spacing-property: extract(@spacing-properties, @ii);
+ @prop: extract(@spacing-property, 1);
+ @abbrev: extract(@spacing-property, 2);
+
+ #each-spacer(@iii: 1) when (@iii =< length(@spacers)) {
+ @item: extract(@spacers, @iii);
+ @size: extract(@item, 1);
+ @length: extract(@item, 2);
+
+ .@{abbrev}@{infix}-@{size} { @{prop}: @length !important; }
+ .@{abbrev}t@{infix}-@{size},
+ .@{abbrev}y@{infix}-@{size} {
+ @{prop}-top: @length !important;
+ }
+ .@{abbrev}r@{infix}-@{size},
+ .@{abbrev}x@{infix}-@{size} {
+ @{prop}-right: @length !important;
+ }
+ .@{abbrev}b@{infix}-@{size},
+ .@{abbrev}y@{infix}-@{size} {
+ @{prop}-bottom: @length !important;
+ }
+ .@{abbrev}l@{infix}-@{size},
+ .@{abbrev}x@{infix}-@{size} {
+ @{prop}-left: @length !important;
+ }
+
+ #each-spacer((@iii + 1));
+ } #each-spacer();
+
+ #each-spacing-property((@ii + 1));
+ } #each-spacing-property();
+
+ // Some special margin utils
+ .m@{infix}-auto { margin: auto !important; }
+ .mt@{infix}-auto,
+ .my@{infix}-auto {
+ margin-top: auto !important;
+ }
+ .mr@{infix}-auto,
+ .mx@{infix}-auto {
+ margin-right: auto !important;
+ }
+ .mb@{infix}-auto,
+ .my@{infix}-auto {
+ margin-bottom: auto !important;
+ }
+ .ml@{infix}-auto,
+ .mx@{infix}-auto {
+ margin-left: auto !important;
+ }
+ });
+
+ #each-breakpoint-spacing((@i + 1));
+} #each-breakpoint-spacing();
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_text.less b/include/thirdparty/Bootstrap4/less/utilities/_text.less
new file mode 100644
index 0000000..34c1e4e
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_text.less
@@ -0,0 +1,78 @@
+//
+// Text
+//
+
+.text-monospace { font-family: @font-family-monospace; }
+
+// Alignment
+
+.text-justify { text-align: justify !important; }
+.text-nowrap { white-space: nowrap !important; }
+.text-truncate { #text-truncate(); }
+
+// Responsive alignment
+
+//@each $breakpoint in map-keys($grid-breakpoints) {
+// @include media-breakpoint-up($breakpoint) {
+// $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+//
+// .text#{$infix}-left { text-align: left !important; }
+// .text#{$infix}-right { text-align: right !important; }
+// .text#{$infix}-center { text-align: center !important; }
+// }
+//}
+#each-breakpoint-text(@i: 1) when (@i =< length(@grid-breakpoints)) {
+ @breakpoint: extract(extract(@grid-breakpoints, @i), 1);
+
+ #media-breakpoint-up(@breakpoint, {
+ @infix: breakpoint-infix(@breakpoint, @grid-breakpoints);
+
+ .text@{infix}-left { text-align: left !important; }
+ .text@{infix}-right { text-align: right !important; }
+ .text@{infix}-center { text-align: center !important; }
+ });
+
+ #each-breakpoint-text((@i + 1));
+} #each-breakpoint-text();
+
+// Transformation
+
+.text-lowercase { text-transform: lowercase !important; }
+.text-uppercase { text-transform: uppercase !important; }
+.text-capitalize { text-transform: capitalize !important; }
+
+// Weight and italics
+
+.font-weight-light { font-weight: @font-weight-light !important; }
+.font-weight-normal { font-weight: @font-weight-normal !important; }
+.font-weight-bold { font-weight: @font-weight-bold !important; }
+.font-italic { font-style: italic !important; }
+
+// Contextual colors
+
+.text-white { color: @white !important; }
+
+//@each $color, $value in $theme-colors {
+// @include text-emphasis-variant('.text-#{$color}', $value);
+//}
+#each-theme-color-text(@i: 1) when (@i =< length(@theme-colors)) {
+ @item: extract(@theme-colors, @i);
+ @color: extract(@item, 1);
+ @value: extract(@item, 2);
+
+ #text-emphasis-variant(~".text-@{color}", @value);
+
+ #each-theme-color-text((@i + 1));
+} #each-theme-color-text();
+
+.text-body { color: @body-color !important; }
+.text-muted { color: @text-muted !important; }
+
+.text-black-50 { color: fade(@black, 50%) !important; }
+.text-white-50 { color: fade(@white, 50%) !important; }
+
+// Misc
+
+.text-hide {
+ #text-hide(@ignore-warning: true);
+}
diff --git a/include/thirdparty/Bootstrap4/less/utilities/_visibility.less b/include/thirdparty/Bootstrap4/less/utilities/_visibility.less
new file mode 100644
index 0000000..c0a7285
--- /dev/null
+++ b/include/thirdparty/Bootstrap4/less/utilities/_visibility.less
@@ -0,0 +1,11 @@
+//
+// Visibility utilities
+//
+
+.visible {
+ #invisible(visible);
+}
+
+.invisible {
+ #invisible(hidden);
+}
diff --git a/themes/Bootswatch_Flatly/Bootswatch/variables.less b/themes/Bootswatch_Flatly/Bootswatch/variables.less
index 54f05cf..a27acf9 100644
--- a/themes/Bootswatch_Flatly/Bootswatch/variables.less
+++ b/themes/Bootswatch_Flatly/Bootswatch/variables.less
@@ -49,7 +49,7 @@
@font-size-small: ceil(@font-size-base * 0.85); // ~12px
@line-height-base: 1.428571429; // 20/14
-@line-height-computed: floor(@font-size-base * @line-height-base); // ~20px
+@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px
@headings-font-family: @font-family-base;
@headings-font-weight: 500;
@@ -242,7 +242,7 @@
@navbar-default-bg: @brand-primary;
@navbar-default-border: darken(@navbar-default-bg, 6.5%);
@navbar-border-radius: @border-radius-base;
-@navbar-padding-horizontal: floor(@grid-gutter-width / 2); // ~15px
+@navbar-padding-horizontal: floor((@grid-gutter-width / 2)); // ~15px
@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
// Navbar links
|