/* ==========================
   THEME VARIABLE FIXES
   ========================== */
/* Confirmed gap in the vendored Metronic bundle (dist/plugins/metronic/css/
   style.bundle.css, not editable per project convention - fixed here at the
   integration point instead): [data-theme="dark"] redefines --bs-secondary
   (#e4e6ef -> #323248) but never redefines its "-rgb" companion,
   --bs-secondary-rgb, which every rgba()-based utility actually reads
   (.bg-secondary, .text-secondary, .border-secondary all use
   rgba(var(--bs-secondary-rgb), ...), not the hex variable directly). So in
   dark mode those utilities kept computing against the light-mode RGB triplet
   (228, 230, 239) regardless of theme - most visible on a light gray badge
   like the item_filter facet's selected-value pill
   (`<span class="... bg-secondary ...">`), which read as low-contrast/wrong
   against dark-mode's own default (light) text color. Value below matches
   --kt-secondary-rgb's own already-correct dark-mode entry in the same
   bundle. */
[data-theme="dark"] {
	--bs-secondary-rgb: 50, 50, 72;
}

/* Belt-and-suspenders on top of the variable fix above: explicitly pair
   background and text color for the facet "current value" pill itself
   (include/form_custom.php's renderItemFilter()/renderDateRangeFilter(),
   `<span data-facet-value class="... bg-secondary ...">`) so it's readable
   in both themes regardless of any other -rgb chain gaps in the vendored
   bundle - uses --bs-secondary directly (already theme-correct) rather than
   the rgba()/-rgb form. --bs-gray-800 itself is already theme-adaptive
   (#3f4254, dark, in light mode; #cdcdde, light, in dark mode - the theme
   INVERTS what each gray step means rather than keeping fixed literal
   shades, confirmed via the [data-theme="dark"] block in the bundle), so one
   rule handles both themes with no [data-theme="dark"]-scoped override
   needed - adding one here would have to *reduce* gray-800 wrongly. */
[data-facet-value].bg-secondary {
	background-color: var(--bs-secondary) !important;
	color: var(--bs-gray-800) !important;
}

/* The same pairing for every .badge-light-secondary (e.g. a "secondary" status
   badge): the bundle gives it a light gray background with light gray text,
   near invisible in light mode. Theme-adaptive in both themes, as above. */
.badge-light-secondary {
	background-color: var(--bs-secondary) !important;
	color: var(--bs-gray-800) !important;
}

/* Plain .badge-light (the Neutral badge slot, e.g. notification times, a list's
   tags_ column): the bundle's default was gray on gray in dark mode. Same
   theme-adaptive pairing as above; no !important, so a theme's Neutral slot
   (.badge.badge-light, more specific) still wins. */
.badge-light {
	background-color: var(--bs-secondary);
	color: var(--bs-gray-800);
}
/* badge-light-dark (e.g. "Archived"): a stronger gray, same idea. */
.badge-light-dark {
	background-color: var(--bs-gray-300) !important;
	color: var(--bs-gray-900) !important;
}

/* Coloured light badges (badge-light-primary, -warning, ...) draw the brand
   colour on its own pale tint, faint in both modes. Same fix as the alerts
   below: text mixed toward black (light) or white (dark), so it still follows
   the colour theme (TPL #1171). */
.badge-light-primary { color: color-mix(in srgb, var(--kt-primary) 55%, #000) !important; }
.badge-light-success { color: color-mix(in srgb, var(--kt-success) 50%, #000) !important; }
.badge-light-info { color: color-mix(in srgb, var(--kt-info) 55%, #000) !important; }
.badge-light-warning { color: color-mix(in srgb, var(--kt-warning) 45%, #000) !important; }
.badge-light-danger { color: color-mix(in srgb, var(--kt-danger) 55%, #000) !important; }
[data-theme="dark"] .badge-light-primary { color: color-mix(in srgb, var(--kt-primary) 55%, #fff) !important; }
[data-theme="dark"] .badge-light-success { color: color-mix(in srgb, var(--kt-success) 55%, #fff) !important; }
[data-theme="dark"] .badge-light-info { color: color-mix(in srgb, var(--kt-info) 55%, #fff) !important; }
[data-theme="dark"] .badge-light-warning { color: color-mix(in srgb, var(--kt-warning) 70%, #fff) !important; }
[data-theme="dark"] .badge-light-danger { color: color-mix(in srgb, var(--kt-danger) 55%, #fff) !important; }

/* The bundle colours alert text with the plain brand colour on its pale tint:
   warning yellow on pale yellow in light mode (1.47:1, e.g. a summary table's
   "No records found.") and info purple on dark purple in dark mode (2.31:1).
   Text mixed from the theme's own colour toward black (light) or white (dark)
   keeps both above 4.5:1 and still follows the colour theme (TPL #1138). */
.alert-warning,
.alert-warning .alert-link {
	color: color-mix(in srgb, var(--kt-warning) 45%, #000);
}
[data-theme="dark"] .alert-warning,
[data-theme="dark"] .alert-warning .alert-link {
	color: var(--kt-warning);
}
[data-theme="dark"] .alert-info,
[data-theme="dark"] .alert-info .alert-link {
	color: color-mix(in srgb, var(--kt-info) 50%, #fff);
}

/* ==========================
   GLOBAL ELEMENT STYLES
   ========================== */
::placeholder {
	opacity: 1;
	color: var(--bs-gray-600) !important;
}

.label-placeholder::placeholder {
	opacity: 1;
	color: var(--bs-gray-750) !important;
}

body {
	background-image: var(--user-bg);
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	transition: background-image 0.4s ease;
}

/* ==========================
   UTILITY CLASSES
   ========================== */
.pt-5px {
	padding-top: 5px !important;
}

.w-min {
	width: 1%;
	white-space: nowrap;
}

.w-50px {
	width: 50px;
}

.w-64px {
	width: 64px;
}

.w-72px {
	width: 72px;
}

.h-150px {
	height: 150px;
}

.h-600px {
	height: 600px;
}

.mh-185px {
	max-height: 185px;
}

.mh-400px {
	max-height: 400px;
}

.m-w-100px {
	min-width: 100px;
}

.m-w-150px {
	min-width: 150px;
}

.m-w-200px {
	min-width: 200px;
}

.m-w-250px {
	min-width: 250px;
}

.m-w-300px {
	min-width: 300px;
}

.m-w-350px {
	min-width: 350px;
}

.cursor-pointer {
	cursor: pointer;
}

/* Keeps line breaks and spaces of logged text (the params / details popup).
   A class, not a style attribute: the site's CSP blocks inline styles (TPL #1251). */
.text-pre-wrap {
	white-space: pre-wrap;
}

/* The image preview popup: the whole picture fits the screen. */
.mh-75vh {
	max-height: 75vh;
}

.aspect-ratio-1x1 {
	aspect-ratio: 1/1;
}

.isolate {
	isolation: isolate;
}

.z-1 {
	z-index: 1;
}

.z-5 {
	z-index: 5;
}

.bg-current-color-10 {
	background-color: color-mix(in srgb, currentColor, transparent 90%);
}

/* ==========================
   LAYOUT CONTAINERS
   ========================== */
#kt_app_root {
	overflow: auto;
}

.kt_gallery {
	width: 99%;
}

.breadcrumb-item {
	position: relative;
	color: var(--bs-gray-750);
}

/* ==========================
   COMPONENT STYLES
   ========================== */
/* Menu Icon */
.menu-icon {
	color: var(--kt-text-gray-800);
	transition: all 0.3s ease;
}

.menu-icon:hover {
	color: var(--kt-primary);
	transform: translateY(-3px);
}

.btn.menu-icon i {
	font-size: 100px;
	height: 100px;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* Sidebar Search */
#sidebarSearchWrapper {
	position: relative;
}

#sidebarSearchIcon {
	position: absolute;
	top: 50%;
	left: 0.75rem;
	transform: translateY(-50%);
	pointer-events: none;
	z-index: 2;
	color: #b0b0be !important;
}

#sidebarSearch {
	background-color: transparent !important;
	padding-left: 2.5rem !important;
	height: 2.25rem !important;
	color: #b0b0be !important;
	border-color: #b0b0be !important;
}

#sidebarSearch:hover,
#sidebarSearch:focus,
#sidebarSearch:active {
	border-color: #b0b0be !important;
}

#sidebarSearchWrapper:hover #sidebarSearchIcon,
#sidebarSearchWrapper:focus-within #sidebarSearchIcon,
#sidebarSearch::placeholder,
#sidebarSearch:hover::placeholder,
#sidebarSearch:focus::placeholder {
	color: #b0b0be !important;
}

/* Background Preview */
#bg-scroll-container {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.5rem 1rem;
	max-width: 90vw;
	justify-content: center;
}

.bg-preview {
	width: 60px;
	height: 45px;
	background-size: cover;
	background-position: center;
	border-radius: 6px;
	opacity: 0.5;
	transition:
		transform 0.3s ease,
		opacity 0.3s ease,
		border 0.3s ease;
	cursor: pointer;
	flex: 0 0 auto;
}

.bg-preview.active {
	width: 100px;
	height: 70px;
	border: 2px solid var(--bs-primary);
	transform: scale(1.2);
	opacity: 1;
	z-index: 2;
}

/* Media & Video */
.camera-video {
	width: 100%;
	max-width: 640px;
	border: 2px solid #ddd;
	border-radius: 8px;
}

.lecture-video {
	width: 100%;
	height: auto;
}

/* CKEditor */
.ck-content {
	min-height: 50px;
}

/* Nav Tabs */
.nav.nav-tabs.nav-line-tabs .nav-link {
	display: block;
	padding: 0.75rem 1rem;
	border-radius: 0.5rem 0.5rem 0 0;
	margin: 0;
}
/* Neighbouring tabs overlap by 1px, so a themed tab border draws one shared line. */
.nav.nav-tabs.nav-line-tabs .nav-item + .nav-item .nav-link {
	margin-left: -1px;
}

/* Without a theme's Tabs colours, line tabs are folder tabs coloured like the side
   tabs (side-tab-layout.css): each outlined on top and sides, body text; the active
   one bold on the Primary tint with a Primary underline. The row's line is a shadow, so no tab pokes out and adds
   a scrollbar. Confirmed bug, fixed (TPL #1329): white on the light-mode secondary
   was 1.25:1 and the inactive gray-500 was faint in dark. Theme colours are
   !important, so they win. */
.nav.nav-tabs.nav-line-tabs {
	border-bottom: 0;
	box-shadow: inset 0 -1px 0 var(--bs-gray-400);
}
/* The bundle's hover drops the top and side borders, which shifts the tab by 1px. */
.nav.nav-tabs.nav-line-tabs .nav-link,
.nav.nav-tabs.nav-line-tabs .nav-link:hover:not(.disabled) {
	border: 1px solid var(--bs-gray-400);
}
.nav.nav-tabs.nav-line-tabs .nav-link.active,
.nav.nav-tabs.nav-line-tabs .nav-item.show .nav-link {
	background-color: var(--kt-primary-light);
	color: var(--bs-body-color);
	font-weight: 600;
}
.nav.nav-tabs.nav-line-tabs .nav-link.active,
.nav.nav-tabs.nav-line-tabs .nav-link.active:hover,
.nav.nav-tabs.nav-line-tabs .nav-item.show .nav-link {
	border-bottom-color: var(--kt-primary);
}
.nav-line-tabs .nav-item .nav-link:where(:not(.active)) {
	color: var(--bs-body-color);
}
/* Hover is the same in every theme (Classic included), in the theme's own Primary:
   important, as theme tab colours are. */
.nav.nav-tabs.nav-line-tabs .nav-link:not(.active):hover {
	background-color: var(--kt-primary-light) !important;
	color: var(--kt-primary) !important;
}

/* Sidebar Menu Titles */
.app-sidebar .menu .menu-item .menu-link .menu-title,
.flatpickr-day {
	color: var(--bs-gray-750);
}

/* Month picker (flatpickr monthSelect plugin): its own stylesheet fixes the
   month text near-black and the hover/selected colors, so in dark mode the
   months were barely readable. Same colors as the day cells; themes override
   them (include/helpers/theme.php). */
.flatpickr-calendar .flatpickr-monthSelect-month {
	color: var(--bs-gray-750);
}
.flatpickr-calendar .flatpickr-monthSelect-month:not(.selected):not(.flatpickr-disabled):hover {
	background: var(--bs-primary-light);
	border-color: var(--bs-primary-light);
	color: var(--bs-primary);
}
.flatpickr-calendar .flatpickr-monthSelect-month.selected {
	background: var(--bs-primary);
	border-color: var(--bs-primary);
	color: var(--bs-primary-inverse);
}
.flatpickr-calendar .flatpickr-monthSelect-month.flatpickr-disabled {
	color: var(--bs-gray-400);
}

/* Checkbox, switch and radio labels: the bundle's label color is a dim grey,
   unreadable in dark mode. Body text color instead (follows the mode);
   a disabled box keeps the faded look. */
.form-check .form-check-label,
.form-check .form-check-input:not(:checked) + .form-check-label {
	color: var(--bs-gray-800);
}
.form-check .form-check-input:disabled + .form-check-label {
	opacity: 0.6;
}
/* An unticked box is a pale fill with no edge, invisible on a white card
   (and blank when a theme makes inputs white): give it an outline. A theme's
   Switches and Checkboxes border still overrides it. */
.form-check .form-check-input:not(:checked):not(:indeterminate) {
	border: 1px solid var(--bs-gray-400);
}

/* Slider field (range): its track is an empty field, so it takes the input
   background and border like the boxes beside it (themes: Inputs slot). */
[data-range2].noUi-target {
	background: var(--kt-input-bg) !important;
	border: 1px solid var(--kt-input-border-color) !important;
	box-shadow: none !important;
}

/* Notification Containers */
#notif-wrapper,
#notif-container,
#notif-footer {
	background-color: var(--bs-body-bg);
}

/* ==========================
   MEDIA QUERIES
   ========================== */
@media (min-width: 992px) {
	[data-kt-app-sidebar-minimize="on"][data-kt-app-sidebar-hoverable="true"] .app-sidebar:not(:hover) #sidebarSearch {
		opacity: 0;
		transition: opacity 0.3s ease !important;
	}
}

[data-kt-app-layout="dark-sidebar"] .app-sidebar .menu .menu-item .menu-link,
[data-kt-app-layout="dark-sidebar"] .app-sidebar .menu .menu-item .menu-link .menu-title {
	color: #b0b0be;
}

.menu-item.active > .menu-link {
	background-color: #2b2b40 !important;
}

.gridjs-wrapper {
	overflow: auto;
}

/* Grid/table borders were too subtle in both themes - the theme's own
   --bs-border-color/--kt-border-color (#eff2f5 light, #2b2b40 dark) is barely
   distinguishable from the page background either way. --bs-gray-400 is
   already theme-adaptive (light: #b5b5c3, dark: #474761 - the theme INVERTS
   what each gray step means, same as --bs-gray-800 elsewhere in this file),
   so one rule gives real contrast in both themes with no separate
   [data-theme="dark"] override needed. Scoped to tables specifically, not
   the global --bs-border-color/--kt-border-color variables, so cards/inputs/
   dropdowns etc. elsewhere in the theme are left alone. */
.table-bordered > :not(caption) > * > * {
	border: 1px solid var(--bs-gray-400) !important;
	vertical-align: middle !important;
}

.table.table-row-bordered tr {
	border-bottom-color: var(--bs-gray-400) !important;
}

/* The bundle removes the last row's bottom border (.table tbody tr:last-child
   td { border-bottom: 0 !important }), and it outranks the rule above, so a
   plain bordered table had no bottom edge. Put it back. Tables inside a card
   (.subtable-card, below) still drop it on purpose: their rule is more
   specific. */
.table.table-bordered > tbody > tr:last-child > *,
.table.table-bordered > tfoot > tr:last-child > * {
	border-bottom: 1px solid var(--bs-gray-400) !important;
}

/* renderTable() cards use the same border color as their table's lines. */
.subtable-card.border {
	border-color: var(--bs-gray-400) !important;
}

/* The table has no outer edges inside the card: the card border, the header,
   the search bar and the pager lines frame it, so no edge is ever doubled. */
.subtable-card .table-bordered > :first-child > tr:first-child > * {
	border-top: 0 !important;
}

.subtable-card .table-bordered > :last-child > tr:last-child > * {
	border-bottom: 0 !important;
}

.subtable-card .table-bordered > * > tr > :first-child {
	border-left: 0 !important;
}

.subtable-card .table-bordered > * > tr > :last-child {
	border-right: 0 !important;
}

.subtable-card .table {
	margin-bottom: 0;
}

.subtable-card .table-bordered,
.subtable-card .table-bordered > :first-child > tr:first-child,
.subtable-card .table-bordered > :last-child > tr:last-child {
	border-top: 0 !important;
	border-bottom: 0 !important;
}

.subtable-card .table-bordered {
	border-left: 0 !important;
	border-right: 0 !important;
}

/* Search bar / pager dividers use the same grey as the table lines. */
.subtable-card .border-top,
.subtable-card .border-bottom {
	border-color: var(--bs-gray-400) !important;
}

.subtable-card > .card-header {
	border-bottom-color: var(--bs-gray-400);
}

.gridjs-tbody tr:last-child {
	border-bottom: 1px solid var(--bs-gray-400) !important;
}

/* Datatable outer border and last row match the cell lines (theme default is paler, and strips the last row). */
.gridjs .gridjs-table.table-bordered {
	border-color: var(--bs-gray-400);
}

.gridjs-tbody > tr:last-child > td {
	border-bottom: 1px solid var(--bs-gray-400) !important;
}

/* Plain light buttons (e.g. rowTextButton 'plain', the Users form's Cancel) read in
   both modes: the bundle's gray text was 2.7:1 dark, 3.5:1 light (TPL #1329). */
.btn.btn-light {
	color: var(--bs-gray-800);
}

/* Page buttons are btn-light, whose hover/focus is a fixed gray that themes
   don't touch; use the theme's primary tint like the current page does. */
.gridjs-pages .btn.btn-light:not(.active):not(:disabled):hover,
.gridjs-pages .btn.btn-light:not(.active):not(:disabled):focus {
	color: var(--bs-primary);
	border-color: var(--bs-primary-light);
	background-color: var(--bs-primary-light) !important;
}

.marker-red {
	color: red !important;
}

.marker-blue {
	color: blue !important;
}

.marker-green {
	color: green !important;
}

.marker-yellow {
	color: yellow !important;
}

.marker-purple {
	color: purple !important;
}

.marker-orange {
	color: orange !important;
}

.marker-black {
	color: black !important;
}

.marker-white {
	color: white !important;
}

.marker-gray {
	color: gray !important;
}

.marker-brown {
	color: brown !important;
}

.app-container {
	padding-left: 0 !important;
	padding-right: 0 !important;
}

.app-content {
	padding-top: 0;
	padding-bottom: 0;
}

/* ==========================
   Placeholders
   ========================== */
.placeholder-pulse {
	animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

@keyframes pulse {
	/* At the start and end of the animation, the opacity is 1 */
	0%,
	100% {
		opacity: 1;
	}

	/* At the middle of the animation, the opacity is lower */
	50% {
		opacity: 0.5;
	}
}

.breadcrumb-wrapper::-webkit-scrollbar {
	height: 6px;
}

.h-btn-sm {
	height: 31px;
}

.h-btn-md {
	height: 38px;
}

/* Grows with its content (e.g. a toolbar line wrapping on small screens). */
.min-h-btn-md {
	min-height: 38px;
}

.h-btn-lg {
	height: 46px;
}

.kpi-container {
	container-type: inline-size;

	.card {
		height: 100% !important;
	}
}

@container (width < 1200px) {
	.kpi-container {
		& > * {
			width: calc(100% / 2) !important;
		}
	}
}

@container (width < 562px) {
	.kpi-container {
		& > * {
			width: calc(100%) !important;
		}
	}
}

textarea.form-control,
.choices__inner,
.select2-container {
	min-width: 120px !important;
}

input.form-control {
	min-width: 60px !important;
}

/* Choices' box has a 44px min-height that is taller than its own padding +
   text, and the spare space all lands under the text. Centre it vertically. */
.choices[data-type*="select-one"] .choices__inner {
	display: flex;
	flex-direction: column;
	justify-content: center;
}
/* Dropdowns match a text input's height exactly (.form-control: 1.1rem x 1.5
   line height + 0.775rem padding each side + 1px border each side), so a row
   with both lines up. Single selects are that height; multi selects start there
   and still grow with their tags. */
.choices__inner {
	box-sizing: border-box;
	min-height: calc(1.1rem * 1.5 + 1.55rem + 2px) !important;
}
.choices[data-type*="select-one"] .choices__inner {
	height: calc(1.1rem * 1.5 + 1.55rem + 2px);
}
/* Remove (×) button: a small filled circle instead of a bare character, and
   single selects keep the text clear of it and the arrow. */
/* Remove (×) button, one look for both: the dropdowns' × (Choices renders a
   .choices__button, drawn with ::before) and the × on multi-value inputs such
   as several emails (.input-remove-btn with a Font Awesome icon). An 18px gray
   circle with the Font Awesome "times" icon at 0.7rem, red on hover/focus.
   !important beats Choices' own × styles (opacity .75, 20px, its colors). */
.choices__button,
.input-remove-btn i {
	display: inline-flex !important;
	align-items: center;
	justify-content: center;
	width: 18px !important;
	height: 18px !important;
	padding: 0 !important;
	border-radius: 50% !important;
	background-color: var(--bs-gray-300) !important;
	color: var(--bs-gray-800) !important;
	opacity: 1 !important;
	font-size: 0.7rem;
	line-height: 1;
	vertical-align: middle;
	transition: background-color 0.15s, color 0.15s;
}
.choices__button {
	font-size: 0 !important; /* hides Choices' own "Remove item" text */
}
.choices__button::before {
	content: "\f00d";
	font-family: "Font Awesome 6 Free";
	font-weight: 900;
	font-size: 0.7rem;
	line-height: 1;
	color: inherit !important;
}
/* Nothing to clear: no × on a dropdown still showing its placeholder ("Select …");
   the forced display above would otherwise show it. A multi-value input keeps its ×. */
.choices__placeholder .choices__button {
	display: none !important;
}
.choices__button:hover,
.choices__button:focus-visible,
.input-remove-btn:hover i,
.input-remove-btn:focus-visible i {
	background-color: var(--bs-danger) !important;
	color: #fff !important;
}
/* Single select: × sits 4px left of the arrow (arrow = 10px at the side padding),
   and the text reserves room up to 4px before the ×, so long options end in "…". */
.choices[data-type*="select-one"] {
	--choices-clear-right: calc(var(--choices-padding-x) + 10px + 4px);
}
.choices[data-type*="select-one"] .choices__button {
	right: var(--choices-clear-right) !important;
	margin: 0 !important;
}
/* Small screens: the × icon read 1px high inside its circle there. */
@media (max-width: 767.98px) {
	.choices[data-type*="select-one"] .choices__button::before {
		position: relative;
		top: 1px;
	}
}
.multi-row:has(.input-remove-btn) .form-control {
	padding-right: 2.75rem;
}
.choices[data-type*="select-one"] .choices__list--single .choices__item {
	padding-right: calc(var(--choices-clear-right) + 18px + 4px - var(--choices-padding-x));
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* One look for every field (light mode): the dropdowns' fill, the text inputs' border. */
:root:not([data-theme="dark"]) {
	.form-control,
	.form-control:focus,
	.form-select,
	.form-select:focus {
		background-color: var(--bs-body-bg);
	}

	.choices__inner {
		border-color: #e4e6ef;
	}

	.is-focused .choices__inner,
	.is-open .choices__inner {
		border-color: #b5b5c3;
	}
}

input:disabled:not([type="checkbox"]),
select:disabled,
textarea:disabled,
input:read-only:not(.select2-input):not([data-choices]):not(.flatpickr-input):not(.flatpickr-alt-input):not([type="checkbox"]):not([type="file"]),
select:read-only:not(.select2-hidden-accessible):not([data-choices]),
textarea:read-only {
	background-color: var(--bs-gray-150) !important;
}

.choices {
	&:has(select:disabled) {
		cursor: not-allowed;
		background-color: var(--bs-gray-150) !important;

		.choices__button {
			visibility: hidden !important;
		}
	}

	&.choices--readonly {
		pointer-events: none;
		cursor: default;
		background-color: var(--bs-gray-150) !important;

		.choices__button {
			visibility: hidden !important;
		}

		.choices__input {
			display: none !important;
		}
	}
}

.gridjs-tbody {
	&:has(.gridjs-tr) > tr:not(.gridjs-tr):first-child {
		display: none;
	}
}

/* Datatable color-column swatch (src/mjs/grid_comp.mjs's configColumns() -
   any column named "color" or "color_*") - background set via this rule
   instead of relying on the element's own inline style alone, so it can
   never be dimmed/overridden by anything else in this stylesheet or the
   vendored bundle (in particular, a clickable grid row's own
   `td.gridjs-td *:hover { color: ... !important }` rule right below only
   ever touches text color, never background, but this removes any doubt). */
.color-swatch-dot {
	display: inline-block !important;
	width: 16px !important;
	height: 16px !important;
	background-color: var(--swatch-color) !important;
	opacity: 1 !important;
	visibility: visible !important;
	flex-shrink: 0;
}

/* Detail popup (View Details / View Params): stay inside the popup, wrap long
   values (error text, URLs, tokens) anywhere, read left to right. */
.json-preview table {
	width: 100%;
	max-width: 100%;
	/* Fixed layout: columns share the popup width instead of growing to the
	   longest value, so long text has to wrap. */
	table-layout: fixed;
}

.json-preview th,
.json-preview td {
	white-space: normal !important;
	text-align: start !important;
	vertical-align: top;
	overflow-wrap: anywhere;
	word-break: break-word;
}

.json-preview td {
	min-width: 0;
}

/* A cell that opens something (View Details, a text preview) always reads as a
   link, whatever the row or theme does with plain cell text. */
td.gridjs-td a.cell-link {
	color: var(--bs-primary);
	text-decoration: underline;
	text-underline-offset: 2px;
	cursor: pointer;
}

td.gridjs-td a.cell-link:hover {
	text-decoration-thickness: 2px;
}

tr.gridjs-tr.gridjs-tr-clickable {
	td.gridjs-td * {
		cursor: pointer;
	}

	td.gridjs-td *:hover {
		color: var(--bs-primary) !important;
	}
}

/* The "Choose File" button gets a fixed width, and the file-name overlay starts
   right after it (+1px for the input's border), so the two never overlap. */
.file-upload,
.custom-file-label {
	--file-button-width: 8.28rem;
}
.file-upload::file-selector-button {
	box-sizing: border-box;
	width: var(--file-button-width);
	/* Gap before the picked file's name (the overlay starts at the width alone). */
	margin-inline-end: 1rem;
	/* Paints in its own color, so hiding the input's text below leaves the button readable. */
	-webkit-text-fill-color: currentColor;
}
/* While the placeholder overlay shows, hide the browser's own "No file chosen"
   text instead of covering it: an opaque overlay painted over the input's
   border at fractional zoom (65-80%). Text fill, not color: themes set the
   input's color with !important. */
/* "~", not "+": a CSV field has a hidden input between the two, and the
   browser's text then showed through under the placeholder. */
.file-upload:has(~ .custom-file-label:not(.d-none)) {
	-webkit-text-fill-color: transparent;
}
/* Rich-text field (TPL #1061): pasted images fit the box, both while typing and when shown read-only. */
.rich-text-view {
	height: auto;
	min-height: 3rem;
}
.rich-text-view img,
.ck-editor__editable img {
	max-width: 100%;
	height: auto;
}
.ck-editor__editable {
	min-height: 8rem;
}

/* Remove button sits inside the input's right edge; the picked name stops short of it. */
.file-upload.has-file,
.form-control.has-remove {
	padding-inline-end: 2.5rem;
}
.file-upload-remove {
	position: absolute;
	top: 50%;
	inset-inline-end: 0.35rem;
	transform: translateY(-50%);
	z-index: 2;
}
img.file-preview-zoom {
	cursor: zoom-in;
}
img.file-preview-pick {
	cursor: pointer;
}
/* Overlay only shows while no file is picked: styled like every other placeholder.
   A picked file's name is the input's own text, so it keeps normal input styling. */
.custom-file-label .custom-file-placeholder {
	color: var(--bs-gray-600) !important;
	font-weight: normal;
	/* See-through, so it can never paint over the input's border at any zoom. */
	background-color: transparent !important;
	/* Inline-block on the baseline leaves descender space under it, pushing it over the input's bottom border. */
	vertical-align: top;
	/* .form-control's min-height is the full input height: inside the 1px inset it spilled over the bottom border. */
	min-height: 0;
}
.custom-file-label {
	position: absolute;
	inset: 0;
	padding: 1px 1px 1px calc(var(--file-button-width) + 1px);
}

.object-fit-contain {
	object-fit: contain !important;
}

.text-hover-primary,
.hover-text-primary,
.text-hover.text-primary {
	transition: color 0.2s ease;
}

.text-hover-primary:hover,
.text-hover-primary:focus,
.hover-text-primary:hover,
.hover-text-primary:focus,
.text-hover.text-primary:hover,
.text-hover.text-primary:focus {
	color: var(--bs-primary) !important;
}

.text-hover-primary:hover *,
.text-hover-primary:focus *,
.hover-text-primary:hover *,
.hover-text-primary:focus *,
.text-hover.text-primary:hover *,
.text-hover.text-primary:focus * {
	color: var(--bs-primary) !important;
}

.csv-preview-wrapper {
	overflow-x: auto;
	overflow-y: hidden;
	white-space: nowrap;
}

.csv-preview-wrapper table {
	white-space: nowrap;
}

/* Grid Action Button Styles */
.grid-action-button {
	width: 50px;
	height: 50px;
}

.grid-action-label {
	white-space: nowrap;
	font-size: 10px;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* PDF Iframe */
.pdf-iframe {
	width: 100%;
	height: 500px;
}

/* Spreadsheets & Tables Overrides - Transparent with theme colors */
.jexcel_container,
.jexcel,
.jss,
.jss_container,
.jss_content,
.jss_worksheet,
.jexcel_content {
	background-color: transparent !important;
	border-color: rgba(128, 128, 128, 0.2) !important;
}

/* Force cells to be transparent with inherited text color */
.jexcel td,
.jexcel thead td,
.jexcel tbody td,
.jss td,
.jss thead td,
.jss_worksheet td,
.jss_worksheet thead td,
.jss_worksheet tbody td {
	background-color: transparent !important;
	color: inherit !important;
	border: 1px solid rgba(128, 128, 128, 0.2) !important;
}

/* Force inputs and textareas to be transparent */
.jexcel td input,
.jexcel td textarea,
.jexcel td select,
.jss td input,
.jss td textarea,
.jss td select,
.jss_worksheet td input,
.jss_worksheet td textarea,
.jss_worksheet td select {
	background-color: transparent !important;
	color: inherit !important;
}

/* Row numbers and column headers with subtle background */
.jexcel tbody td:first-child,
.jss tbody td:first-child,
.jss_worksheet tbody td:first-child,
.jexcel thead td:first-child,
.jss thead td:first-child,
.jss_worksheet thead td:first-child {
	background-color: rgba(128, 128, 128, 0.1) !important;
	color: inherit !important;
}

.jexcel thead td,
.jss thead td,
.jss_worksheet thead td {
	background-color: rgba(128, 128, 128, 0.1) !important;
	color: inherit !important;
	font-weight: 600 !important;
}

/* Light mode specific - ensure black text on white */
[data-theme="light"] .jexcel td,
[data-theme="light"] .jexcel thead td,
[data-theme="light"] .jexcel tbody td,
[data-theme="light"] .jss td,
[data-theme="light"] .jss thead td,
[data-theme="light"] .jss_worksheet td,
[data-theme="light"] .jss_worksheet thead td,
[data-theme="light"] .jss_worksheet tbody td,
[data-theme="light"] .jexcel td input,
[data-theme="light"] .jexcel td textarea,
[data-theme="light"] .jss td input,
[data-theme="light"] .jss td textarea,
[data-theme="light"] .jss_worksheet td input,
[data-theme="light"] .jss_worksheet td textarea {
	color: #000000 !important;
}

/* Dark mode specific - ensure white text on dark */
[data-theme="dark"] .jexcel td,
[data-theme="dark"] .jexcel thead td,
[data-theme="dark"] .jexcel tbody td,
[data-theme="dark"] .jss td,
[data-theme="dark"] .jss thead td,
[data-theme="dark"] .jss_worksheet td,
[data-theme="dark"] .jss_worksheet thead td,
[data-theme="dark"] .jss_worksheet tbody td,
[data-theme="dark"] .jexcel td input,
[data-theme="dark"] .jexcel td textarea,
[data-theme="dark"] .jss td input,
[data-theme="dark"] .jss td textarea,
[data-theme="dark"] .jss_worksheet td input,
[data-theme="dark"] .jss_worksheet td textarea {
	color: #ffffff !important;
}

/* Context Menu (Essential for Add/Delete Row) */
/* Same colors as the app's dropdown menus (themes: Dropdown Menus and
   Dropdown Active / Hover Item, include/helpers/theme.php). */
.jcontextmenu {
	z-index: 10001 !important;
	background-color: var(--kt-menu-dropdown-bg-color, var(--kt-card-bg)) !important;
	color: var(--bs-gray-800) !important;
	box-shadow: var(--kt-menu-dropdown-box-shadow, 0 4px 10px rgba(0, 0, 0, 0.3)) !important;
	border: 1px solid var(--bs-gray-300) !important;
	border-radius: 0.475rem !important;
	padding: 5px 0 !important;
}

.jcontextmenu > div {
	padding: 8px 20px !important;
	color: var(--bs-gray-800) !important;
	cursor: pointer !important;
}

.jcontextmenu > div:hover {
	background-color: var(--kt-primary-light) !important;
	color: var(--kt-primary) !important;
}

.jcontextmenu > hr {
	border-color: var(--bs-gray-300) !important;
	opacity: 1;
}

/* A select list opened inside a sub-table's scroll box (when it isn't lifted to
   the page) shows in full instead of being clipped by the box. */
.table-responsive:has(.choices.is-open),
.gridjs-wrapper:has(.choices.is-open) {
	overflow: visible !important;
}

/* A hidden field's wrapper takes no room, so a field shown inline with it (a
   sub-table cell's 'inline_with') has no gap before it. Still posted. */
.fv-row:has(> input[data-form-field-type="hidden"]) {
	display: none;
}

/* A sub-table's select reads from the left in any column (the last one centres
   its content when there is no actions column after it). */
[data-kt-subtable] .choices {
	text-align: start;
}

/* Ensure spreadsheet container doesn't overflow improperly */
.csv-preview-wrapper {
	overflow: visible !important;
}

/* Dark mode inputs share the dropdowns' fill. */
[data-theme="dark"] .form-control,
[data-theme="dark"] .form-control:focus,
[data-theme="dark"] .form-select,
[data-theme="dark"] .form-select:focus {
	background-color: #1b1b29;
}

/* A field shown on one mode's input fill whatever the current mode (e.g. the
   Website settings' light/dark logo and font color). Uses the active theme's
   Inputs fill when it sets one, else the defaults above. */
.bg-field-light {
	background-color: var(--field-bg-light, #ffffff) !important;
}
.bg-field-dark {
	background-color: var(--field-bg-dark, #1b1b29) !important;
}

/* Choices.js Dark Mode Support */
[data-theme="dark"] .choices__inner,
[data-theme="dark"] .choices__list--dropdown {
	background-color: #1b1b29 !important;
	color: #ffffff !important;
	border-color: #323248 !important;
}

[data-theme="dark"] .choices__list--dropdown .choices__item--selectable {
	color: #ffffff !important;
}

/* A grouped list filter (renderGroupSelectFilter): its panel holds the search box
   with the grouped list always open under it, in place like the other filters'
   lists; it never floats out, so nothing cuts it off or covers it. */
[data-table-filter="groupselect"] .choices {
	width: 100%;
	margin-bottom: 0;
}
[data-table-filter="groupselect"] .choices__list--dropdown {
	position: static !important;
	display: block !important;
	visibility: visible !important;
	z-index: auto;
	margin-top: 0.5rem;
	border: 0;
	box-shadow: none;
}

[data-theme="dark"] .choices__item--selectable.is-highlighted {
	background-color: #2b2b40 !important;
}

[data-theme="dark"] .choices__input {
	background-color: transparent !important;
	color: #ffffff !important;
}

/* Checkbox list with a search box or filter (renderInput 'list_search' / 'list_filter'): scrolls. */
.checkbox-list__body {
	max-height: 320px;
	overflow-y: auto;
	overflow-x: hidden;
}

/* Checkbox tree (renderInput 'checkbox_tree'): each level indented under its parent. */
.checkbox-tree__body {
	max-height: 320px;
	overflow-y: auto;
	padding: 0.5rem 0.75rem;
	border: 1px solid var(--bs-gray-300);
	border-radius: 0.475rem;
}
.checkbox-tree__list {
	list-style: none;
	margin: 0;
	padding: 0;
}
.checkbox-tree__list .checkbox-tree__list {
	padding-left: 1.5rem;
}
.checkbox-tree__node.is-collapsed > .checkbox-tree__list {
	display: none;
}
.checkbox-tree__row {
	display: flex;
	align-items: center;
	padding: 0.3rem 0;
}
.checkbox-tree__caret {
	width: 1.25rem;
	flex-shrink: 0;
	cursor: pointer;
	color: var(--bs-body-color);
	transition: transform 0.15s;
}
.checkbox-tree__caret i {
	color: inherit;
}
/* A locked box (covered by its ticked parent) stays readable: beats the general
   faded disabled label above. */
.checkbox-tree .form-check .checkbox-tree__box:disabled + .form-check-label {
	opacity: 0.85;
}
.checkbox-tree__node.is-collapsed > .checkbox-tree__row .checkbox-tree__caret {
	transform: rotate(-90deg);
}

/* Grouped checkbox select (renderInput 'option_groups'): one summary line instead of chips,
   clickable group headers, and a tick on picked rows. */
.choices--grouped .choices__list--multiple {
	display: none;
}
.choices--grouped .choices__summary {
	display: inline-block;
	max-width: 100%;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	vertical-align: middle;
}
.choices--grouped .choices__summary:empty {
	display: none;
}
.choices--grouped .choices__summary:not(:empty) + .choices__input {
	width: 1ch !important;
	min-width: 0 !important;
}
.choices__list--grouped .choices__heading {
	cursor: pointer;
	padding: 0.75rem 0.75rem 0.4rem !important;
	font-size: 0.9rem;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	/* Readable in both modes (colors.md: no mid grey for text). Confirmed bug,
	   fixed (OPS #1327): gray-600 was 3.4:1 on the dark page. */
	color: var(--bs-gray-800) !important;
	border-bottom: 0 !important;
}
.choices__list--grouped .choices__group:not(:first-child) {
	border-top: 1px solid var(--bs-border-color);
}
.choices__list--grouped .choices__heading:hover {
	color: var(--kt-primary) !important;
}
/* In a grouped list filter the button names the picks, so the box is only the
   search (the picks shown there read like typed text). */
[data-table-filter="groupselect"] .choices__summary {
	display: none !important;
}
[data-table-filter="groupselect"] .choices__input {
	width: 100% !important;
}
.choices__list--grouped .choices__item--choice {
	padding-left: 2rem !important;
	position: relative;
}
.choices__list--grouped .choices__item--choice.is-selected::before {
	content: "\2713";
	position: absolute;
	left: 0.75rem;
	color: var(--bs-primary);
}
.choices__list--grouped .choices__item--choice::after {
	display: none;
}

/* ==========================
   FLOATING CHAT
   ========================== */

/* Unread badge (rendered on the navbar "Ask AI" button) */
#chat-badge {
	position: absolute;
	top: -3px;
	right: -3px;
	min-width: 18px;
	height: 18px;
	border-radius: 9px;
	background: #f1416c;
	color: #fff;
	font-size: 0.65rem;
	font-weight: 700;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0 4px;
	pointer-events: none;
}

/* Drawer */
#chat-drawer {
	position: fixed;
	top: 0;
	right: 0;
	width: 360px;
	height: 100vh;
	z-index: 1059;
	background: var(--bs-body-bg, #fff);
	border-left: 1px solid var(--bs-border-color, #e4e6ef);
	box-shadow: -4px 0 24px rgba(0, 0, 0, 0.18);
	display: flex;
	flex-direction: column;
	transform: translateX(100%);
	transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}

#chat-drawer.chat-open {
	transform: translateX(0);
}

/* Overlay */
#chat-overlay {
	display: none;
	position: fixed;
	inset: 0;
	z-index: 1058;
	background: rgba(0, 0, 0, 0.35);
}

#chat-overlay.chat-open {
	display: block;
}

/* Drawer header */
.chat-drawer-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 14px 18px;
	background: var(--bs-primary, #009ef7);
	color: #fff;
	font-weight: 600;
	font-size: 0.95rem;
	flex-shrink: 0;
}

.chat-close-btn {
	background: none;
	border: none;
	color: #fff;
	opacity: 0.8;
	cursor: pointer;
	padding: 0;
	font-size: 1.1rem;
	line-height: 1;
}

.chat-close-btn:hover {
	opacity: 1;
}

/* Messages area */
#chat-body {
	flex: 1;
	overflow-y: auto;
	padding: 16px 14px;
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.chat-bubble {
	max-width: 80%;
	padding: 8px 12px;
	border-radius: 12px;
	font-size: 0.85rem;
	line-height: 1.45;
}

.chat-bubble-self {
	background: var(--bs-primary, #009ef7);
	color: #fff;
	border-bottom-right-radius: 3px;
}

.chat-bubble-other {
	background: var(--bs-gray-200, #f1f1f4);
	color: var(--bs-gray-800, #3f4254);
	border-bottom-left-radius: 3px;
}

.chat-bubble-name {
	font-size: 0.7rem;
	font-weight: 600;
	margin-bottom: 2px;
	opacity: 0.72;
}

.chat-bubble-time {
	font-size: 0.67rem;
	margin-top: 3px;
	opacity: 0.58;
	text-align: right;
}

/* Footer input */
.chat-drawer-footer {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 12px 14px;
	border-top: 1px solid var(--bs-border-color, #e4e6ef);
	flex-shrink: 0;
}

#chat-input {
	flex: 1;
	border: 1px solid var(--bs-border-color, #e4e6ef);
	border-radius: 20px;
	padding: 8px 14px;
	font-size: 0.85rem;
	background: var(--bs-body-bg, #fff);
	color: var(--bs-body-color, #181c32);
	outline: none;
}

#chat-input:focus {
	border-color: var(--bs-primary, #009ef7);
}

#chat-send {
	width: 36px;
	height: 36px;
	border-radius: 50%;
	border: none;
	background: var(--bs-primary, #009ef7);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	flex-shrink: 0;
	transition: background 0.15s;
	padding: 0;
}

#chat-send:hover {
	background: var(--bs-primary-active, #0095e8);
}

/* ==========================
   LOADING
   ========================== */
.loading-overlay {
	position: fixed;
	inset: 0;
	background: rgba(0, 0, 0, 0.6);
	display: flex;
	justify-content: center;
	align-items: center;
	z-index: 99999;
	visibility: hidden;
	opacity: 0;
	transition: 0.2s ease;
}
.loading-overlay.active {
	visibility: visible;
	opacity: 1;
}
.loading-box {
	background: white;
	padding: 40px 60px;
	border-radius: 12px;
}
.spinner {
	width: 50px;
	height: 50px;
	border: 5px solid #eee;
	border-top: 5px solid #007bff;
	border-radius: 50%;
	margin: 0 auto 20px;
	animation: spin 1s linear infinite;
}
@keyframes spin {
	to {
		transform: rotate(360deg);
	}
}

/* w-10/w-15 fill the gap in Metronic's own w-25/w-33/w-50/w-75 utility set
   (dist/plugins/metronic/css/style.bundle.css) - same naming/!important
   convention, added here since editing the vendored bundle isn't allowed. */
.w-10 {
	width: 10% !important;
}
.w-15 {
	width: 15% !important;
}

/* Report freeze panes + drag-to-pan (reportPaneBehavior() in src/mjs/helper.mjs).
   Shared by the on-page report and its "View in New Tab" copy; only the pane
   scrolls, so the page around the report stays still. */
/* A report inside a flex row (e.g. a "row d-contents" result container)
   would otherwise grow to its table's full width, leaving nothing to pan. */
.rpt-host, .rpt-pane { min-width: 0; max-width: 100%; }
/* Stable gutter: a scrollbar appearing/disappearing must not change the pane's
   width, or the width-driven re-freeze can loop (seen in popups). */
.rpt-pane { overflow: auto; max-height: calc(100vh - 220px); cursor: grab; scrollbar-gutter: stable; }
/* Pinned cells stick inside the pane's padding, so any padding would leave a
   strip where scrolled rows/columns show past the frozen header/columns. */
.rpt-pane { padding: 0 !important; }
.rpt-pane.rpt-dragging { cursor: grabbing; user-select: none; }
.rpt-pane .table-responsive { overflow: visible !important; }
.rpt-freeze-head { position: sticky; z-index: 2; }
.rpt-freeze-col { position: sticky; z-index: 1; }
.rpt-freeze-head.rpt-freeze-col { z-index: 3; }
.rpt-freeze-head, .rpt-freeze-col { outline: 1px solid var(--bs-border-color, #e4e6ef); outline-offset: -1px; }
/* A frozen cell's own bottom border when it differs from its outline. */
.rpt-underline::after { content: ""; position: absolute; left: -1px; right: -1px; bottom: 0; border-bottom: var(--rpt-underline); pointer-events: none; }
.rpt-freeze-card { position: sticky; left: 0; z-index: 4; background-color: inherit !important; box-sizing: border-box; }
.rpt-freeze-wide { width: max-content; min-width: 100%; }
.rpt-freeze-edge { box-shadow: inset -1px 0 0 #b5b5c3; }
@media print {
	.rpt-pane { overflow: visible !important; max-height: none !important; height: auto !important; cursor: auto; }
	.rpt-underline::after { display: none; }
	.rpt-freeze-head, .rpt-freeze-col, .rpt-freeze-card { position: static !important; box-shadow: none !important; outline: none !important; }
	.rpt-freeze-card, .rpt-freeze-wide { width: auto !important; min-width: 0 !important; }
}

/* Metronic strips the last row's bottom border off every .table
   (".table tbody tr:last-child td { border-bottom: 0 !important }"), leaving
   bordered report tables open at the bottom. */
[data-report-output] .table-bordered > tbody > tr:last-child > td,
[data-report-output] .table-bordered > tbody > tr:last-child > th,
[data-report-output] .table-bordered > tfoot > tr:last-child > td,
[data-report-output] .table-bordered > tfoot > tr:last-child > th {
	border-bottom: 1px solid var(--bs-gray-400) !important;
}

/* Chart.js sizes a responsive chart from the canvas's parent; a dedicated
   relative wrapper that can shrink stops the canvas's own size feeding back
   into it (the resize-shrink loop). */
.chart-canvas-wrap {
	position: relative;
	width: 100%;
	min-width: 0;
}

/* A field with buttons beside it (e.g. the + that adds a new option): the
   field takes the leftover width and never grows. An open dropdown drops
   Choices' overflow clipping, which would otherwise let its text widen the
   field and squeeze the button thin; the open list floats over the button. */
.input-with-buttons > :not(button) {
	flex: 1 1 0;
	min-width: 0;
}
.input-with-buttons > button {
	flex-shrink: 0;
}
/* The field sits 1px off its first button; extra buttons keep a wider gap. */
.input-with-buttons > :not(button) + button {
	margin-left: 1px;
}
.input-with-buttons > button + button {
	margin-left: 0.5rem;
}
.input-with-buttons .choices,
.input-with-buttons .choices__inner {
	/* The global 120px floor would overflow a narrow slot and clip its border. */
	min-width: 0 !important;
}
/* While open, the dropdown field spreads over its buttons (the row is its
   anchor) instead of sitting beside them; the slot keeps its height. */
.input-with-buttons {
	position: relative;
}
.input-with-buttons > :not(button):has(> .choices.is-open, .choices.is-open) {
	min-height: 44px;
}
.input-with-buttons .choices.is-open {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	z-index: 3;
}

/* An upload page's File Template Preview: restore the last row's bottom
   border the theme strips from every table. */
.template-sample-table.table-bordered tbody tr:last-child td {
	border-bottom: 1px solid var(--bs-gray-400) !important;
}

/* Light mode: default text is pure black instead of the theme's dark gray.
   Set on the theme variables so body text, labels, inputs, selects, dropdowns
   and every table/report pick it up. Muted/gray helper classes are left gray. */
[data-theme="light"] {
	--bs-body-color: #000000;
	--bs-body-color-rgb: 0, 0, 0;
	--kt-body-color: #000000;
	--kt-text-dark: #000000;
	--kt-table-color: #000000;
	--kt-form-label-color: #000000;
	--kt-input-color: #000000;
	--kt-input-solid-color: #000000;
	--kt-form-select-color: #000000;
}
[data-theme="light"] .choices__inner,
[data-theme="light"] .choices__list--single .choices__item {
	color: #000000;
}

/* Table headers are bold everywhere (listings, sub-tables, reports). */
.table thead th,
.table thead td,
.table th,
th.gridjs-th {
	font-weight: 700 !important;
}

/* Dropdown placeholders ("Select ...") match the input/date picker placeholder
   color (the global ::placeholder rule above). Choices renders them as a normal
   item, so they need their own rule. */
.choices__placeholder,
.choices__item--placeholder,
.choices__list--single .choices__item.choices__placeholder,
[data-theme="light"] .choices__list--single .choices__item.choices__placeholder {
	color: var(--bs-gray-600) !important;
	opacity: 1 !important;
}

/* Listing filter triggers and the Columns button (built-in look; color themes
   override these). Filters borrow the sidebar's navy: in light mode a very
   light navy-tinted fill with navy text and the value as plain text (a solid
   navy fill is too heavy on a light page); in dark mode one step up from the
   sidebar, edged with its divider color. Columns is a soft gray in light
   mode. */
[data-theme="dark"] [data-filter-type] > button:first-of-type,
[data-theme="dark"] [data-facets-toggle],
[data-theme="dark"] #table-actions button:first-of-type {
	background-color: #2b2b40 !important;
	color: var(--kt-primary-inverse) !important;
}
[data-theme="dark"] [data-filter-type] > button:first-of-type,
[data-theme="dark"] [data-facets-toggle] {
	border-color: #393945 !important;
}
[data-theme="dark"] span[data-facet-value] {
	color: black;
}

[data-theme="light"] [data-filter-type] > button:first-of-type,
[data-theme="light"] [data-facets-toggle] {
	background-color: #ebebf2 !important;
	border-color: #a9a9bd !important;
	color: #1e1e2d !important;
}
[data-theme="light"] [data-filter-type] > button:first-of-type i,
[data-theme="light"] [data-facets-toggle] i {
	color: #5e5e78 !important;
}
[data-theme="light"] [data-filter-type] > button:first-of-type span[data-facet-value] {
	background-color: transparent !important;
	color: var(--kt-body-color) !important;
}

[data-theme="light"] #table-actions button:first-of-type {
	background-color: var(--kt-gray-200) !important;
	border-color: var(--kt-gray-400) !important;
	color: var(--kt-gray-800) !important;
}
[data-theme="light"] #table-actions button:first-of-type i {
	color: var(--kt-gray-700) !important;
}

/* Themes settings page: preview samples open a color popup. */
[id^="theme-preview-"] [data-theme-sample] {
	cursor: pointer;
}
/* Each preview pane carries its own data-theme; use that mode's text color
   instead of inheriting the page's (white on the light pane in dark mode). */
[id^="theme-preview-"] {
	color: var(--kt-body-color);
}
.theme-sample-popup {
	z-index: 1060;
	width: 280px;
}
.theme-group-chevron {
	transition: transform 0.2s ease;
	transform: rotate(180deg);
}
.collapsed .theme-group-chevron {
	transform: rotate(0deg);
}
/* Buttons with an icon (form footer Save/Submit, Generate): below md the icon sits on
   top of a small caption, matching the page header buttons. */
@media (max-width: 767.98px) {
	/* Button font, not a label span: Generate's text is bare. Icons keep their own size. */
	.btn.btn-stack-sm {
		flex-direction: column;
		padding-top: 0.5rem !important;
		padding-bottom: 0.5rem !important;
		font-size: 0.85rem;
		line-height: 1;
	}
	.btn.btn-stack-sm > i {
		margin-bottom: 0.25rem !important;
	}
	/* Header buttons stack icon over caption here too: drop the icon's side spacing
	   (ms-1 + the theme's icon padding) so it stays centered. */
	.btn[data-head-button] i,
	.btn.btn-stack-sm > i {
		margin-left: 0 !important;
		margin-right: 0 !important;
		padding-right: 0 !important;
	}
}
/* Themes preview: each pane is a mock page drawn from its own theme
   variables (header = Top Bar, body = Page Background). */
.theme-sample-page {
	background-color: var(--kt-app-bg-color);
	color: var(--kt-body-color);
	container-type: inline-size;
}
/* Narrow preview pane: the mini sidebar collapses to icons, like the real
   minimized sidebar, so the page content keeps its width. Themes pane only:
   the Appearance preview card is a container too and keeps its own menus (TPL #877). */
@container (max-width: 560px) {
	.theme-sample-page .theme-sample-sidebar {
		width: 52px !important;
	}
	.theme-sample-page .theme-sample-sidebar .theme-sample-label,
	.theme-sample-page .theme-sample-sidebar .sidebar-brand-on-dark,
	.theme-sample-page .theme-sample-sidebar .sidebar-brand-on-light {
		display: none !important;
	}
	.theme-sample-page .theme-sample-sidebar-logo {
		justify-content: center !important;
	}
	.theme-sample-page .theme-sample-sidebar-toggle i {
		transform: rotate(180deg);
	}
	.theme-sample-page .theme-sample-sidebar-item,
	.theme-sample-page .theme-sample-sidebar-search {
		text-align: center;
	}
	.theme-sample-page .theme-sample-sidebar-item i,
	.theme-sample-page .theme-sample-sidebar-search i {
		margin: 0 !important;
	}
}
.theme-sample-topbar {
	background-color: var(--kt-app-header-base-bg-color);
	color: var(--kt-body-color);
}
.theme-sample-card {
	background-color: var(--kt-card-bg);
	color: var(--kt-body-color);
}
/* Themes preview: mini sidebar / breadcrumb bar, drawn in the app's default
   sidebar and toolbar colors so a theme's overrides show on top. */
.theme-sample-sidebar {
	width: 150px;
	background-color: #1e1e2d;
	color: #b0b0be;
	border-color: #393945;
}
.theme-sample-sidebar-logo {
	border-bottom: 1px dashed;
	border-bottom-color: inherit;
}
.theme-sample-sidebar-item {
	color: #b0b0be;
}
.theme-sample-sidebar-item i {
	color: inherit;
}
.theme-sample-sidebar-item.is-active {
	background-color: #2b2b40;
	color: #ffffff;
}
.theme-sample-sidebar-search {
	border: 1px solid #b0b0be;
	color: #b0b0be;
	background-color: transparent;
}
.theme-sample-crumbs {
	background-color: var(--bs-light);
	color: var(--bs-gray-750);
	border: 1px solid transparent;
}
.theme-sample-crumbs [data-theme-sample="breadcrumb_link"] {
	color: var(--bs-gray-750);
}
/* Themes preview, Light Mode pane while the page is in dark mode: the app's
   light --bs-* values live only on :root (the dark ones on [data-theme="dark"]),
   so a nested data-theme="light" wouldn't reset them. Re-declare the ones the
   preview uses, and undo the page's dark-mode input fill for this pane. */
#theme-preview-light {
	--bs-gray-100: #f5f8fa;
	--bs-gray-150: #f1f4f6;
	--bs-gray-200: #eff2f5;
	--bs-gray-300: #e4e6ef;
	--bs-gray-400: #b5b5c3;
	--bs-gray-500: #a1a5b7;
	--bs-gray-600: #7e8299;
	--bs-gray-700: #5e6278;
	--bs-gray-750: #4e5266;
	--bs-gray-800: #3f4254;
	--bs-gray-900: #181c32;
	--bs-dark: #181c32;
	--bs-light: #f5f8fa;
	--bs-light-rgb: 245, 248, 250;
	--bs-secondary: #e4e6ef;
	--bs-secondary-rgb: 228, 230, 239;
	--bs-body-bg: #ffffff;
	--bs-body-bg-rgb: 255, 255, 255;
	--bs-body-color: #000000;
	--bs-body-color-rgb: 0, 0, 0;
	--bs-border-color: #eff2f5;
	--bs-heading-color: #181c32;
}
[data-theme="dark"] #theme-preview-light .form-control,
[data-theme="dark"] #theme-preview-light .form-select {
	background-color: #ffffff;
	color: var(--kt-input-color);
	border-color: var(--kt-input-border-color);
}
/* Sidebar brand: the "on dark" logo/site name shows by default; a color theme
   with a light sidebar swaps to "on light" (include/helpers/theme.php). The
   wrappers use display: contents so the logo row's layout and the sidebar's
   own minimize rules still act on the images inside. */
.sidebar-brand-on-dark {
	display: contents;
}
.sidebar-brand-on-light {
	display: none;
}
/* Themes preview: samples for the collapse button, top bar icon and a dropdown
   menu, drawn in the app's default look. */
.theme-sample-sidebar-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 22px;
	height: 22px;
	border-radius: 0.475rem;
	background-color: var(--kt-body-bg);
	color: var(--kt-gray-500);
	box-shadow: 0 0 6px rgba(0, 0, 0, 0.15);
}
/* Drawn like the real top-bar buttons (btn-icon btn-custom btn-icon-muted:
   muted icon color, 35px, rounded). */
.theme-sample-navicon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 35px;
	height: 35px;
	border-radius: 0.475rem;
	color: var(--kt-text-muted);
}
.theme-sample-dropdown {
	background-color: var(--kt-body-bg);
	color: var(--kt-body-color);
	box-shadow: 0 0 20px rgba(0, 0, 0, 0.08);
}
/* Themes preview "Hovered row": paint it like a real listing row under the
   mouse (.table-hover). It's an odd row of a striped table, and the stripe
   rule is more specific than .table-active, so it showed the stripe color. */
.table > tbody > tr[data-theme-sample="table_hover"] > * {
	--bs-table-accent-bg: var(--bs-table-hover-bg) !important;
	color: var(--bs-table-hover-color);
}
/* Themes preview table: restore the last row's bottom border the theme strips
   from every table. */
[id^="theme-preview-"] .table-bordered tbody tr:last-child td {
	border-bottom: 1px solid var(--bs-gray-400) !important;
}

/* Listing filters on small screens: one "Filters (n)" button opens a menu with
   every filter stacked full width; a filter's own list opens inline under it
   (its floating position is overridden). Desktop keeps the separate buttons. */
@media (max-width: 767.98px) {
	.table-facets {
		display: none !important;
		position: absolute;
		top: calc(100% + 0.5rem);
		left: 0;
		/* Below the top bar (100), so its menus (notifications, profile) open over an open Filters menu. */
		z-index: 99;
		width: min(360px, calc(100vw - 2rem));
		max-height: 70vh;
		overflow-y: auto;
		flex-direction: column;
		flex-wrap: nowrap !important;
		align-items: stretch !important;
		padding: 0.75rem;
		background-color: var(--kt-card-bg, var(--kt-body-bg));
		border: 1px solid var(--bs-gray-400);
		border-radius: 0.65rem;
		box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.15);
	}
	.table-facets.is-open {
		display: flex !important;
	}
	.table-facets > [data-filter-type] {
		flex-direction: column;
		align-items: stretch !important;
		width: 100%;
	}
	.table-facets > [data-filter-type] > button {
		width: 100%;
	}
	.table-facets .item-filter-content {
		position: static !important;
		left: auto !important;
		top: auto !important;
		width: 100%;
		min-width: 0 !important;
		margin-top: 0.5rem;
		box-shadow: none !important;
		border: 1px solid var(--bs-gray-300);
	}
}
/* Listing toolbar when it doesn't fit on one line (data-stacked, set by the fit
   script in pages/header.php; always on small screens): search on its own
   full-width row, then "N results", then Filters / Clear / Columns, all the
   same height. */
	.table-toolbar-row[data-stacked] {
		flex-wrap: wrap !important;
		align-items: center !important;
		gap: 0.75rem !important;
	}
	.table-toolbar-row[data-stacked] .table-toolbar-left {
		display: contents !important;
	}
	.table-toolbar-row[data-stacked] .table-toolbar-search {
		order: 0;
		width: 100% !important;
		flex: 1 1 100%;
	}
	/* Result count: a small line of its own right under the search box. */
	.table-toolbar-row[data-stacked] [data-kt-table-results-count],
	.table-toolbar-row[data-stacked] [data-kt-table-selected-count] {
		order: 1;
		display: block !important;
		flex: 1 1 100%;
		min-width: 0 !important;
		margin-top: -0.5rem;
		font-size: 0.85rem;
		font-weight: 500 !important;
		color: var(--bs-gray-800) !important;
	}
	/* Filters + Clear on the left, Columns on the right. The empty bulk-action
	   block is hidden so it doesn't add a gap. */
	.table-toolbar-row[data-stacked] #table-actions { margin-left: auto; }
	.table-toolbar-row[data-stacked] > div:not(.table-toolbar-left):not(#table-actions):not(:has(:not(.d-none))) {
		display: none !important;
	}
	.table-toolbar-row[data-stacked] [data-kt-table-selected-count].d-none {
		display: none !important;
	}
	.table-toolbar-row[data-stacked] [data-facets-wrap] { order: 2; }
	.table-toolbar-row[data-stacked] [data-clear-all-filters] { order: 3; }
	.table-toolbar-row[data-stacked] > div:not(.table-toolbar-left):not(#table-actions) { order: 4; }
	.table-toolbar-row[data-stacked] #table-actions {
		order: 5;
		align-self: center !important;
	}
/* A crowded toolbar on a wider screen (data-compact): Filters and Clear take
   the shape of the table buttons beside them (Compact, Columns): icon over a
   small caption, same padding (their own phone look, .btn-stack-sm). */
.table-toolbar-row[data-compact] .btn-stack-sm {
	flex-direction: column;
	justify-content: center;
	padding: 0.5rem !important;
	line-height: 1;
}
.table-toolbar-row[data-compact] .btn-stack-sm > i {
	margin: 0 0 0.25rem !important;
	padding: 0 !important;
}
.table-toolbar-row[data-compact] .btn-stack-sm .indicator-label {
	font-size: 0.85rem !important;
}
/* Small screens only: compact buttons so Filters and the table buttons share
   one row on a phone. They rely on the other phone-only toolbar rules. */
@media (max-width: 767.98px) {
	.table-toolbar-row [data-facets-toggle],
	.table-toolbar-row [data-clear-all-filters],
	.table-toolbar-row #table-actions .btn {
		height: 44px;
		min-width: 0;
		padding: 0.3rem 0.5rem !important;
		justify-content: center;
		font-size: 0.75rem;
	}
	.table-toolbar-row #table-actions {
		gap: 0.35rem !important;
	}
	.table-toolbar-row [data-facets-toggle] .indicator-label,
	.table-toolbar-row #table-actions .btn small {
		font-size: 0.7rem !important;
		margin: 0 !important;
	}
	.table-toolbar-row [data-facets-toggle] i,
	.table-toolbar-row #table-actions .btn i {
		margin: 0 0 0.2rem !important;
	}
	/* The filter menu opens under the buttons row, as wide as the toolbar, so
	   it never runs off the screen. */
	.table-toolbar-row {
		position: relative;
	}
	.table-toolbar-row .table-facets-wrap {
		position: static !important;
	}
	.table-toolbar-row .table-facets {
		left: 0;
		right: 0;
		width: auto;
	}
}
/* The breadcrumbs keep their own width on one line (never squeezed into a
   narrow column); the header buttons are what moves when space runs out.
   A trail wider than the bar still ends in "…" (small-screen rules below). */
.page-breadcrumb-bar > .breadcrumb-wrapper,
.page-breadcrumb-bar > #page-toolbar-content {
	flex-shrink: 0;
	max-width: 100%;
}
.page-breadcrumb-bar .breadcrumb {
	flex-wrap: nowrap;
	white-space: nowrap;
}
/* Header buttons that don't fit beside the breadcrumbs (data-stacked, the fit
   script in pages/header.php) take their own row below, right-aligned, and wrap. */
.page-breadcrumb-bar[data-stacked] {
	flex-wrap: wrap;
	row-gap: 0.5rem;
}
.page-breadcrumb-bar[data-stacked] > .page-head-buttons {
	flex: 1 1 100%;
	display: flex;
	flex-wrap: wrap;
	justify-content: flex-end;
	margin-left: 0;
}
/* Still crowded on their own row (data-compact): the phone look at any width,
   the icon over a small caption instead of the full label (form_header.php). */
.page-breadcrumb-bar[data-compact] .btn[data-head-button] {
	padding-top: 0.5rem !important;
	padding-bottom: 0.5rem !important;
}
.page-breadcrumb-bar[data-compact] .btn[data-head-button] span.d-md-inline {
	display: none !important;
}
.page-breadcrumb-bar[data-compact] .btn[data-head-button] small.d-md-none {
	display: inline !important;
}
.page-breadcrumb-bar[data-compact] .btn[data-head-button] i {
	margin-left: 0 !important;
	margin-right: 0 !important;
	padding-right: 0 !important;
}
/* Page header on small screens: breadcrumbs on one line of their own
   (scrolling sideways when long), header buttons on the row below, right. */
@media (max-width: 767.98px) {
	.page-breadcrumb-bar {
		flex-wrap: wrap;
		row-gap: 0.25rem;
	}
	/* Takes its own width without shrinking, so the buttons only move to the
	   next row when the breadcrumbs don't leave room; a very long trail scrolls. */
	.page-breadcrumb-bar > .breadcrumb-wrapper,
	.page-breadcrumb-bar > #page-toolbar-content {
		flex: 1 0 auto;
		max-width: 100%;
		min-width: 0;
		overflow: hidden !important;
	}
	.page-breadcrumb-bar .breadcrumb {
		flex-wrap: nowrap;
		white-space: nowrap;
		min-width: 0;
	}
	/* Only the last two crumbs; the "…" crumb stands in for the rest. */
	.page-breadcrumb-bar .breadcrumb-early {
		display: none !important;
	}
	/* A trail still too long cuts its last crumb with "…" instead of widening the page. */
	.page-breadcrumb-bar .breadcrumb-item:last-child {
		min-width: 0;
		overflow: hidden;
		text-overflow: ellipsis;
	}
	.page-breadcrumb-bar .breadcrumb-item:last-child > span {
		display: block;
		overflow: hidden;
		text-overflow: ellipsis;
	}
	.page-breadcrumb-bar > .page-head-buttons {
		margin-left: auto;
	}
}
/* Dropdown menus (header More/Settings, Global Filters, profile...) never
   wider than the screen on small devices. */
@media (max-width: 767.98px) {
	.menu-sub-dropdown {
		max-width: calc(100vw - 1rem);
	}
}
/* Page header, last line of defence on small screens: no level of the header
   may grow past the screen, and every crumb can shrink and end in "…", so any
   title, however long, stays inside the bar. */
@media (max-width: 767.98px) {
	#kt_app_toolbar,
	#kt_app_toolbar_container,
	#kt_app_toolbar .page-title,
	.page-breadcrumb-bar {
		min-width: 0;
		max-width: 100%;
	}
	.page-breadcrumb-bar {
		overflow: hidden;
	}
	.page-breadcrumb-bar .breadcrumb {
		max-width: 100%;
		overflow: hidden;
	}
	.page-breadcrumb-bar .breadcrumb-item {
		flex: 0 1 auto;
		min-width: 0;
		overflow: hidden;
	}
	.page-breadcrumb-bar .breadcrumb-item > a,
	.page-breadcrumb-bar .breadcrumb-item > span {
		display: block;
		min-width: 0;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	/* The "•" after each crumb keeps its size while the text shrinks. */
	.page-breadcrumb-bar .breadcrumb-item::after {
		flex-shrink: 0;
	}
	.page-breadcrumb-bar .breadcrumb-ellipsis {
		flex: 0 0 auto;
	}
	/* The page title itself too. */
	#kt_app_toolbar .page-heading {
		max-width: 100%;
		overflow-wrap: anywhere;
	}
}
/* Small screens: the header buttons leave the breadcrumb bar and sit on their
   own row below it, right-aligned. Only the breadcrumbs keep the bar's look
   (a theme's Breadcrumb Bar colors reach them through --page-crumb-*). */
@media (max-width: 767.98px) {
	html[data-theme] #kt_app_toolbar .page-breadcrumb-bar {
		background-color: transparent !important;
		border: 0 !important;
		padding: 0 !important;
		flex-direction: column;
		align-items: stretch !important;
		overflow: visible;
		row-gap: 0.75rem;
	}
	html[data-theme] #kt_app_toolbar .page-breadcrumb-bar > .breadcrumb-wrapper,
	html[data-theme] #kt_app_toolbar .page-breadcrumb-bar > #page-toolbar-content {
		flex: 0 0 auto;
		width: 100%;
		margin: 0 !important;
		padding: 0.5rem 1rem;
		border-radius: 0.475rem;
		background-color: var(--page-crumb-bg, var(--kt-light));
		border: var(--page-crumb-border, 0);
	}
	html[data-theme] #kt_app_toolbar .page-breadcrumb-bar > .page-head-buttons {
		align-self: flex-end;
		margin-left: 0;
		flex-wrap: wrap;
		justify-content: flex-end;
	}
}
@media (max-width: 767.98px) {
	/* Parents give up space before the current page's name does. */
	.page-breadcrumb-bar .breadcrumb-item:not(:last-child):not(.breadcrumb-ellipsis) {
		flex-shrink: 4;
		min-width: 4.5em;
	}
}
/* The small-screen Filters button wears the filter buttons' dashed edge. */
[data-facets-toggle] {
	border: 1px dashed transparent;
}
/* Record tables' search/filter bar (table_toolkit.mjs) on small screens:
   search on its own row, then Filters on the left and Rows per page on the
   right; the filter dropdowns open in a menu as wide as the bar. */
@media (max-width: 767.98px) {
	.table-toolkit-bar {
		position: relative;
		row-gap: 0.75rem !important;
	}
	.table-toolkit-bar > .table-toolkit-search {
		flex: 1 1 100%;
	}
	.table-toolkit-bar > .table-toolkit-search input {
		width: 100% !important;
	}
	.table-toolkit-bar > .table-facets-wrap {
		position: static !important;
	}
	.table-toolkit-bar .table-facets {
		left: 1.5rem;
		right: 1.5rem;
		width: auto;
	}
	.table-toolkit-bar .table-facets > select {
		width: 100% !important;
	}
	.table-toolkit-bar [data-facets-toggle] {
		height: 52px;
		min-width: 64px;
		padding: 0.4rem 0.75rem !important;
		justify-content: center;
	}
}
/* The empty-image picture (image_default) is black line art, lost on a dark card.
   Dark mode swaps in a light-line copy with a see-through background, so only the
   lines change color, never the box behind them; real uploads are untouched. */
[data-theme="dark"] img[src$="media/default.png"] {
	content: url("../../dist/media/default-dark.png");
}
/* Same for the broken-image picture a missing file falls back to (pages/header.php). */
[data-theme="dark"] img[src$="media/broken.svg"] {
	content: url("../../dist/media/broken-dark.svg");
}
/* Counter badges (Form::countBadge()): Danger red by default; a theme's
   count badge items override per kind. */
.count-badge {
	background-color: var(--kt-danger);
	color: var(--kt-danger-inverse, #fff);
	/* A ring in the page color, so the counter stands out on any button color (light and dark). */
	box-shadow: 0 0 0 1.5px var(--kt-card-bg, var(--kt-body-bg, #fff));
	/* Whole-pixel circle with the digits trimmed to their visible height, so the
	   number stays centered at every zoom (fractional sizes round differently per zoom). */
	height: 18px;
	min-width: 18px;
	padding: 0 5px;
	justify-content: center;
	line-height: 1;
	text-box: trim-both cap alphabetic;
}
/* Counters on buttons sit mostly on the button's corner; on the small bell
   icon the same look needs it slightly further out. */
.btn > .count-badge.translate-middle {
	transform: translate(-65%, -35%) !important;
	/* Above the next button in the row, which would otherwise paint over the overhang. */
	z-index: 3;
}
.notif-bell .count-badge {
	transform: translate(-35%, -65%) !important;
}
/* FILTERS_MENU_ALWAYS (config/const.php): the small-screen "Filters" menu on
   every screen size. */
html[data-filters-menu="always"] [data-facets-wrap]:not([data-facets-single]) [data-facets-toggle] {
	display: flex !important;
}
html[data-filters-menu="always"] [data-facets-wrap]:not([data-facets-single]) .table-facets {
	display: none !important;
	position: absolute;
	top: calc(100% + 0.5rem);
	left: 0;
	/* Below the top bar (100), as on small screens above. */
	z-index: 99;
	width: min(360px, calc(100vw - 2rem));
	max-height: 70vh;
	overflow-y: auto;
	flex-direction: column;
	flex-wrap: nowrap !important;
	align-items: stretch !important;
	padding: 0.75rem;
	background-color: var(--kt-card-bg, var(--kt-body-bg));
	border: 1px solid var(--bs-gray-400);
	border-radius: 0.65rem;
	box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.15);
}
html[data-filters-menu="always"] [data-facets-wrap]:not([data-facets-single]) .table-facets.is-open {
	display: flex !important;
}
html[data-filters-menu="always"] [data-facets-wrap]:not([data-facets-single]) .table-facets > [data-filter-type] {
	flex-direction: column;
	align-items: stretch !important;
	width: 100%;
}
html[data-filters-menu="always"] [data-facets-wrap]:not([data-facets-single]) .table-facets > [data-filter-type] > button,
html[data-filters-menu="always"] [data-facets-wrap]:not([data-facets-single]) .table-facets > select {
	width: 100% !important;
}
html[data-filters-menu="always"] [data-facets-wrap]:not([data-facets-single]) .table-facets .item-filter-content {
	position: static !important;
	left: auto !important;
	top: auto !important;
	width: 100%;
	min-width: 0 !important;
	margin-top: 0.5rem;
	box-shadow: none !important;
	border: 1px solid var(--bs-gray-300);
}
/* Filters inside the single "Filters" menu (FILTERS_MENU_ALWAYS, and small
   screens): plain rows in the page text color, no icon or fill; the border and
   the picked value keep their normal (or theme) colors. Extra attribute selectors outrank the built-in
   and color-theme filter rules. */
html[data-filters-menu="always"] body [data-facets-wrap]:not([data-facets-single]) [data-table-facets] [data-filter-type][data-filter-type] > button[type][class]:first-of-type i,
html[data-filters-menu="always"] body [data-facets-wrap]:not([data-facets-single]) [data-table-facets] [data-filter-type][data-filter-type] > button[type][class]:first-of-type .vr {
	display: none !important;
}
html[data-filters-menu="always"] body [data-facets-wrap]:not([data-facets-single]) [data-table-facets] [data-filter-type][data-filter-type] > button[type][class]:first-of-type,
html[data-filters-menu="always"] body [data-facets-wrap]:not([data-facets-single]) [data-table-facets][data-table-facets] > select {
	background-color: transparent !important;
	box-shadow: none !important;
	color: var(--kt-body-color) !important;
	justify-content: space-between;
}
@media (max-width: 767.98px) {
	html body [data-table-facets] [data-filter-type][data-filter-type] > button[type][class]:first-of-type i,
	html body [data-table-facets] [data-filter-type][data-filter-type] > button[type][class]:first-of-type .vr {
		display: none !important;
	}
	html body [data-table-facets] [data-filter-type][data-filter-type] > button[type][class]:first-of-type,
	html body [data-table-facets][data-table-facets] > select {
		background-color: transparent !important;
		box-shadow: none !important;
		color: var(--kt-body-color) !important;
		justify-content: space-between;
	}
}
/* Picked value in the Filters menu: no badge fill, text color unchanged. */
html[data-filters-menu="always"] body [data-facets-wrap]:not([data-facets-single]) [data-table-facets] [data-filter-type][data-filter-type] > button[type][class]:first-of-type span[data-facet-value] {
	background-color: transparent !important;
}
@media (max-width: 767.98px) {
	html body [data-table-facets] [data-filter-type][data-filter-type] > button[type][class]:first-of-type span[data-facet-value] {
		background-color: transparent !important;
	}
}
/* Calendar (FullCalendar) navigation buttons follow the theme's Primary
   color: light shade at rest, solid when hovered or selected. */
.fc {
	--fc-button-text-color: var(--kt-primary);
	--fc-button-bg-color: var(--kt-primary-light);
	--fc-button-border-color: var(--kt-primary-light);
	--fc-button-hover-bg-color: var(--kt-primary);
	--fc-button-hover-border-color: var(--kt-primary);
	--fc-button-active-bg-color: var(--kt-primary);
	--fc-button-active-border-color: var(--kt-primary);
}
/* The calendar's own page color is a fixed white: use the card color, so dark
   mode and themes apply. Its weekday header row matches table headers (the
   theme's Table Header slot sets both). */
.fc {
	--fc-page-bg-color: var(--kt-card-bg);
	--fc-neutral-bg-color: var(--kt-gray-100);
}
.fc .fc-col-header-cell {
	background-color: var(--kt-card-bg);
}
.fc .fc-col-header-cell .fc-col-header-cell-cushion {
	color: inherit;
	font-weight: 600;
}
.fc .fc-button-primary:not(:disabled):not(.fc-button-active):hover,
.fc .fc-button-primary:not(:disabled):not(.fc-button-active):focus,
.fc .fc-button-primary:not(:disabled):not(.fc-button-active):active,
.fc .fc-button-primary:not(:disabled).fc-button-active,
.fc .fc-button-primary:not(:disabled):not(.fc-button-active):hover .fc-icon,
.fc .fc-button-primary:not(:disabled):not(.fc-button-active):focus .fc-icon,
.fc .fc-button-primary:not(:disabled):not(.fc-button-active):active .fc-icon,
.fc .fc-button-primary:not(:disabled).fc-button-active .fc-icon {
	color: var(--kt-primary-inverse);
}
.fc .fc-button-primary:not(:disabled):not(.fc-button-active):hover {
	background-color: var(--kt-primary);
}
.fc .fc-button-primary .fc-icon {
	color: inherit;
}
/* Buttons that belong together (e.g. Approve + Disapprove) stay on one line
   when their row wraps; the group itself only breaks if it can't fit. */
.btn-keep-group {
	display: inline-flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	max-width: 100%;
}
/* A switch in a row of labelled fields lines up with their input boxes, not
   their labels: its column drops to the bottom and centres on an input's
   height (.form-control: 1.1rem x 1.5 + 0.775rem padding each side + 2px). */
.row:has(> .fv-row > .form-label) > .fv-row:not(:has(> .form-label)):has(.form-switch) {
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
}
.row:has(> .fv-row > .form-label) > .fv-row:not(:has(> .form-label)) .form-switch {
	display: flex;
	align-items: center;
	min-height: calc(1.1rem * 1.5 + 1.55rem + 2px);
	margin-bottom: 0;
	gap: 0.5rem;
}
/* Themes preview: a small box that always shows a horizontal scrollbar. */
.theme-sample-scroll {
	overflow-x: scroll;
	white-space: nowrap;
	padding-bottom: 0.25rem;
}
.theme-sample-scroll > div {
	width: 200%;
}
/* Muted text everywhere: the page's own text color (so it follows the theme
   and the light/dark mode), just a little lighter, never faint. Metronic's
   fixed grays were barely visible in dark mode. */
.text-muted {
	color: color-mix(in srgb, var(--kt-body-color) 70%, transparent) !important;
}

/* Stacked table view (src/mjs/grid_stack.mjs): a headline cell carries the
   group's other columns as small lines; header sorting moves to the Sort menu. */
[data-view="stacked"] .grid-stack {
	min-width: 12rem;
	text-align: start;
}
[data-view="stacked"] .grid-stack-head {
	font-weight: 600;
	color: var(--bs-gray-900);
	margin-bottom: 0.15rem;
}
[data-view="stacked"] .grid-stack-media {
	display: flex;
	align-items: center;
	gap: 0.75rem;
}
[data-view="stacked"] .grid-stack-line {
	font-size: 0.85rem;
	color: var(--bs-text-muted);
	line-height: 1.4;
}
[data-view="stacked"] .grid-stack-label {
	/* The body color, dimmed: a fixed gray vanished on dark themes. */
	color: var(--bs-body-color);
	opacity: 0.7;
	font-weight: 600;
	white-space: nowrap;
}
/* A value drawn as a block (e.g. a tags_ list) stays on its label's line. */
[data-view="stacked"] .grid-stack-line:has(> div) {
	display: flex;
	align-items: baseline;
	gap: 0.25rem;
}
[data-view="stacked"][data-stack-labels="off"] .grid-stack-label {
	display: none;
}
[data-view="stacked"] .gridjs-th .gridjs-sort {
	display: none;
}
[data-view="stacked"] .gridjs-th-sort {
	pointer-events: none;
	cursor: default;
}
[data-view="stacked"] [data-stack-member] {
	display: none;
}
[data-view="stacked"] th[data-stack-title] .gridjs-th-content {
	display: none;
}
[data-view="stacked"] th[data-stack-title]::before {
	content: attr(data-stack-title);
}
/* "Enable notification sound" prompt: above the page header bar (100) and its
   buttons, below the theme's dropdown menus (107) and popups. */
#enable-notifs {
	z-index: 104;
}
/* A row of page buttons that stays on one line on a phone: the buttons share
   the width equally, with less side padding (pair with short_label captions). */
@media (max-width: 767.98px) {
	.btn-row-fit {
		flex-wrap: nowrap !important;
		gap: 0.4rem !important;
	}
	.btn-row-fit > .btn {
		flex: 1 1 0;
		min-width: 0;
		padding-left: 0.4rem !important;
		padding-right: 0.4rem !important;
		justify-content: center;
		white-space: nowrap;
	}
}
/* Date-range filter: today is marked only by the theme primary color. */
.daterangepicker td.today:not(.active):not(.in-range):not(:hover) {
	background: transparent !important;
	border-color: transparent !important;
	color: var(--kt-primary) !important;
}
/* Listing toolbar buttons (Columns, Export, Table/Stacked, Labels, Sort...):
   one width and centered. The markup's one-sided icon/caption margins and
   smaller right padding would push the content off center. */
#table-actions > .d-inline-block > .btn {
	min-width: 64px;
	padding-left: 0.5rem !important;
	padding-right: 0.5rem !important;
}
#table-actions > .d-inline-block > .btn > i,
#table-actions > .d-inline-block > .btn > small {
	margin-left: 0 !important;
	margin-right: 0 !important;
}
@media (max-width: 767.98px) {
	#table-actions > .d-inline-block > .btn {
		min-width: 52px;
	}
}
/* Phones: a compact, uniform set (Filters included) that fits one row at 320px. */
@media (max-width: 767.98px) {
	.table-toolbar-row [data-facets-toggle],
	.table-toolbar-row [data-clear-all-filters],
	.table-toolbar-row #table-actions > .d-inline-block > .btn {
		height: 40px;
		min-width: 45px !important;
		padding: 0.25rem 0.3rem !important;
	}
	.table-toolbar-row [data-facets-toggle] .indicator-label,
	.table-toolbar-row [data-clear-all-filters] .indicator-label,
	.table-toolbar-row #table-actions .btn small {
		font-size: 0.7rem !important;
	}
	.table-toolbar-row [data-facets-toggle] i,
	.table-toolbar-row [data-clear-all-filters] i,
	.table-toolbar-row #table-actions .btn i {
		font-size: 0.85rem;
		margin-bottom: 0.15rem !important;
	}
}
@media (max-width: 767.98px) {
	.table-toolbar-row {
		column-gap: 0.4rem !important;
	}
	.table-toolbar-row #table-actions {
		gap: 0.3rem !important;
	}
}
/* The theme gives a button's icon a right padding (for icon + text side by
   side); in these stacked buttons it shifts the icon off center. */
#table-actions > .d-inline-block > .btn > i {
	padding-right: 0 !important;
}
/* Filters / Clear stack their icon only below md; beside the text they keep the gap. */
@media (max-width: 767.98px) {
	.table-toolbar-row [data-facets-toggle] > i,
	.table-toolbar-row [data-clear-all-filters] > i {
		padding-right: 0 !important;
	}
}
/* Stacked view Sort menu: the direction buttons take the menu's (theme) text
   color; the picked one is primary. */
/* The menu sits inside #table-actions, whose built-in "first button" fill
   (dark/light toolbar look) would otherwise color the first arrow. */
[data-theme] #table-actions .btn.grid-stack-sort-dir,
.btn.grid-stack-sort-dir {
	background-color: transparent !important;
	color: inherit !important;
	border: 1px solid currentColor !important;
}
[data-theme] #table-actions .btn.grid-stack-sort-dir.active,
.btn.grid-stack-sort-dir.active {
	background-color: var(--kt-primary-light) !important;
	color: var(--kt-primary) !important;
	border-color: transparent !important;
}
.btn.grid-stack-sort-dir i {
	color: inherit !important;
	padding-right: 0 !important;
}
/* Their icons are flex boxes (fixed width): center the glyph inside, since
   text-align doesn't apply to it there. */
#table-actions > .d-inline-block > .btn > i,
.table-toolbar-row [data-facets-toggle] > i,
.table-toolbar-row [data-clear-all-filters] > i,
.btn-row-fit > .btn > i,
.btn.grid-stack-sort-dir i {
	justify-content: center;
}
/* Date-range filter: the library's own "today" and other-month colors outrank
   the theme rules, so state them from the theme variables here. A theme's
   Dropdown Active slot (more specific) still wins for picked days. */
.daterangepicker .drp-calendar td.available.active,
.daterangepicker .drp-calendar td.available.active:hover {
	background-color: var(--kt-primary) !important;
	color: var(--kt-primary-inverse) !important;
	border-color: transparent !important;
}
.daterangepicker .drp-calendar td.off,
.daterangepicker .drp-calendar td.off.in-range,
.daterangepicker .drp-calendar td.off.start-date,
.daterangepicker .drp-calendar td.off.end-date {
	background-color: transparent !important;
	color: inherit !important;
	opacity: 0.35;
}
/* Date-range filter: previous/next month arrows in the menu's (theme) text
   color, hover like the days. */
.daterangepicker .drp-calendar th.prev span,
.daterangepicker .drp-calendar th.next span {
	border-color: currentColor !important;
}
.daterangepicker .drp-calendar th.prev,
.daterangepicker .drp-calendar th.next {
	color: inherit !important;
	background-color: transparent !important;
}
.daterangepicker .drp-calendar th.prev:hover,
.daterangepicker .drp-calendar th.next:hover {
	background-color: var(--kt-primary-light) !important;
	color: var(--kt-primary) !important;
}

/* Settings > Appearance: previews on the left, the clicked part's settings
   in the panel on the right (pages/admin/settings/management/appearance.php). */
.appr-panel {
	position: sticky;
	top: 90px;
}
.appr-stage [data-appr-part] {
	cursor: pointer;
	outline: 1px dashed transparent;
	outline-offset: 2px;
	transition: outline-color 0.15s;
}
.appr-stage [data-appr-part]:hover,
.appr-stage [data-appr-part]:focus-visible {
	outline-color: var(--kt-primary);
}
/* Dashed, so the marker for the part being edited never reads as a real border. */
.appr-stage [data-appr-part].appr-picked {
	outline: 2px dashed var(--kt-primary);
}
/* Preview mock-ups: layout only. Colors come from the theme: Metronic's own
   variables for the stage's data-theme, and the viewer's color theme scoped to
   the stage (themePreviewCss()) on the data-theme-sample parts. */
.appr-stage {
	color: var(--kt-body-color);
}
/* The preview fills the card edge to edge; only the hint text above it keeps a margin. */
.card.appr-previews > .card-header {
	padding: 0;
	min-height: 0;
}
.card.appr-previews > .card-body {
	padding: 0;
	overflow: hidden;
	border-bottom-left-radius: inherit;
	border-bottom-right-radius: inherit;
}
/* Small, even space around the hint and switches at every width. */
.appr-hint {
	padding: 0.4rem 0.75rem;
}
/* Flush with the card, so its hover/picked outline goes inside instead of being cut off. */
.appr-stage .appr-signin {
	outline-offset: -3px;
}
.appr-empty {
	opacity: 0.6;
	font-style: italic;
}
.appr-signin {
	display: flex;
	align-items: center;
	gap: 24px;
	min-height: 360px;
	padding: 24px;
	background: url("/dist/media/default-bg.jpg") center / cover no-repeat;
}
.appr-signin-brand {
	flex: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 8px;
	padding: 8px;
	text-align: center;
}
.appr-signin-brand.is-logo-top .appr-signin-icon {
	order: -1;
}
.appr-signin-name {
	font-size: 1.6rem;
	font-weight: 700;
	filter: drop-shadow(0 0 1px rgba(100, 100, 100, 0.85));
}
.appr-signin-icon {
	max-height: 110px;
	max-width: 70%;
}
.appr-signin-banner {
	max-height: 90px;
	max-width: 100%;
}
.appr-signin-card {
	flex: 1;
	max-width: 300px;
	padding: 24px;
	border-radius: 10px;
	text-align: center;
}
.appr-signin-title {
	font-size: 1.3rem;
	font-weight: 700;
	margin-bottom: 4px;
}
.appr-signin-sub,
.appr-signin-foot {
	font-size: 0.85rem;
	opacity: 0.75;
	margin-bottom: 12px;
}
.appr-signin-foot {
	margin: 12px 0 0;
}
.appr-app {
	display: flex;
	gap: 8px;
	min-height: 320px;
	padding: 8px;
	font-size: 0.8rem;
}
.appr-sidebar {
	width: 170px;
	flex-shrink: 0;
	padding: 10px 8px;
}
.appr-sidebar.is-collapsed {
	width: 48px;
	text-align: center;
}
.appr-sidebar-brand {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	min-height: 36px;
	margin-bottom: 10px;
}
.appr-menu-icon {
	height: 28px;
	max-width: 60px;
}
.appr-menu-banner {
	max-height: 32px;
	max-width: 100%;
}
.appr-menu-name {
	font-weight: 600;
	font-size: 0.85rem;
}
.appr-main {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.appr-topbar {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 8px 12px;
}
.appr-content {
	flex: 1;
	padding: 10px;
}
.appr-filters {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-bottom: 10px;
}
.appr-table td,
.appr-table th {
	white-space: nowrap;
	padding: 6px 8px !important;
}
@media (max-width: 767.98px) {
	.appr-signin {
		flex-direction: column;
	}
	.appr-sidebar:not(.is-collapsed) {
		display: none;
	}
}
/* Report header preview: the top of an A4 page, always on white paper. */
.appr-paper {
	max-width: 640px;
	min-height: 420px;
	margin: 0 auto;
	padding: 32px 36px 20px;
	display: flex;
	flex-direction: column;
	background: #ffffff;
	color: #181c32;
	border: 1px solid #e4e6ef;
	box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
	font-size: 0.8rem;
}
.appr-paper-table {
	width: 100%;
	border-collapse: collapse;
	margin-bottom: 16px;
}
.appr-paper-table th,
.appr-paper-table td {
	border: 1px solid #181c32;
	padding: 4px 8px;
}
.appr-paper-foot {
	margin-top: auto;
}
.appr-paper-foot .rh-footer {
	margin-top: 0;
}
.appr-paper-page {
	text-align: center;
	font-size: 9pt;
	font-style: italic;
	color: #5e6278;
}
/* The settings panel: one field per row, kept in view and scrolling inside. */
.appr-panel-body {
	max-height: calc(100vh - 260px);
	overflow-y: auto;
}
/* Compact uploads: a small thumbnail beside the file button, the name on one line. */
.appr-files [id$="-container"] {
	display: inline-block;
	margin: 0 0.75rem 0 0 !important;
	vertical-align: middle;
}
.appr-files [id$="-container"] img {
	max-height: 56px !important;
	max-width: 120px !important;
	padding: 2px;
}
.appr-files [id$="-container"] audio {
	height: 32px;
}
.appr-files input[type="file"] {
	font-size: 0.85rem;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
@media (max-width: 991.98px) {
	.appr-panel {
		position: static;
	}
	.appr-panel-body {
		max-height: none;
	}
}
/* Image uploads in the panel: the label on its own line, then one row with a
   fixed thumbnail box and the file field beside it. */
.appr-file {
	display: grid;
	grid-template-columns: 64px minmax(0, 1fr);
	column-gap: 12px;
	align-items: center;
}
.appr-file > label,
.appr-file > .form-text,
.appr-file > .fv-plugins-message-container {
	grid-column: 1 / -1;
}
.appr-file > [id$="-container"] {
	grid-column: 1;
	width: 64px;
	height: 56px;
	margin: 0 !important;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
	border: 1px solid var(--kt-input-border-color);
	border-radius: 0.475rem;
	background-color: var(--kt-input-bg);
}
.appr-file > [id$="-container"] img {
	max-width: 100% !important;
	max-height: 100% !important;
	object-fit: contain;
	border: 0;
	padding: 2px;
	background: transparent;
}
.appr-file > [id$="-container"] img.file-preview-pick {
	opacity: 0.35;
}
.appr-file > .d-flex {
	grid-column: 2;
}
/* The menu brand in the preview: the site's own markup; the expanded sidebar
   shows the full logo, the collapsed one the small icon. */
/* Only the chosen version (icon or banner); [hidden] must win over display. */
.appr-sidebar .d-contents:not([hidden]) {
	display: contents;
}
.appr-sidebar .d-contents[hidden] {
	display: none !important;
}
.appr-app {
	background-color: var(--kt-app-bg-color);
}
.appr-sidebar:not(.is-collapsed) .app-sidebar-logo-minimize,
.appr-sidebar.is-collapsed .app-sidebar-logo-default {
	display: none !important;
}
/* Metronic shows the small logo only under the body's minimized-sidebar state,
   which the preview doesn't have. */
.appr-sidebar.is-collapsed .app-sidebar-logo-minimize {
	display: inline-block !important;
}
.appr-sidebar.is-collapsed .appr-sidebar-brand {
	justify-content: center;
}
/* The preview menus are the real ones at about 64% (170px for the real 265px,
   48px for the collapsed 75px): the logo row keeps the real padding (px-6) and
   height (70px), and the logo sizes (h-50px, mh-50px mw-200px, collapsed h-30px)
   scale with it. */
.appr-sidebar .appr-sidebar-brand {
	min-height: 45px;
	margin: -10px -8px 8px;
	padding: 0 13px;
}
.appr-sidebar:not(.is-collapsed) .appr-sidebar-brand {
	justify-content: flex-start;
}
/* Logo Position: Center (the collapsed menu keeps its small icon as is). */
.appr-sidebar:not(.is-collapsed) .appr-sidebar-brand.brand-center,
body:not([data-kt-app-sidebar-minimize="on"]) .app-sidebar-logo.brand-center,
[data-kt-app-sidebar-minimize="on"] .app-sidebar:hover .app-sidebar-logo.brand-center {
	justify-content: center;
}
.appr-sidebar.is-collapsed .appr-sidebar-brand {
	padding: 0;
}
.appr-sidebar .appr-sidebar-brand .h-50px {
	height: 32px !important;
}
.appr-sidebar .appr-sidebar-brand .mh-50px {
	max-height: 32px !important;
}
.appr-sidebar .appr-sidebar-brand .mw-200px {
	max-width: 128px !important;
}
.appr-sidebar .appr-sidebar-brand .h-30px {
	height: 19px !important;
}

/* Icon buttons (the form footer's and other stacked buttons): the icon gets a fixed,
   centred box, so glyphs of different widths sit centred over or beside the label. */
/* Metronic draws button icons as inline-flex (.btn i), where text-align does
   nothing: justify-content is what centres the glyph in its fixed-width box. */
.btn.btn-stack-sm > i {
	width: 1.25em;
	text-align: center;
	justify-content: center;
}

/* Appearance: a logo size being set shows in the preview (scaled in the page script). */
.appr-stage.has-menu-size .appr-sidebar:not(.is-collapsed) .appr-sidebar-brand img.app-sidebar-logo-default {
	height: var(--appr-menu-h) !important;
	max-height: none !important;
	width: auto;
	object-fit: contain;
}
.appr-stage.has-front-size .appr-signin-brand img {
	height: var(--appr-front-h) !important;
	max-height: none !important;
	width: auto;
	object-fit: contain;
}
.appr-stage.has-front-size .appr-signin-mbrand img {
	height: var(--appr-front-m-h);
	max-height: none;
	width: auto;
	object-fit: contain;
}
.appr-stage.has-phone-size .appr-signin-mbrand img {
	height: var(--appr-phone-h);
	max-height: none;
	width: auto;
	object-fit: contain;
}
/* Appearance: the Light / Dark preview switch is one pill (the theme's input
   background and border, via .form-control); the chosen side uses the
   Primary colors like the top bar's Mode menu. */
.appr-switch {
	border-radius: 0.65rem;
	padding: 2px !important;
}
.appr-switch .menu-item {
	padding: 0 !important;
}
.appr-switch .menu-link {
	border-radius: 0.5rem;
}
/* Sign-in preview at phone width: the real page hides the big brand side and puts
   a small brand row (banner, or icon beside the name) at the top of the card. */
.appr-signin-mbrand {
	display: none;
}
/* Measured from the real sign-in page on a 390px phone: the card is ~278px wide
   with 30px inside, so a logo there has ~218px; the preview gives it the same. */
.appr-stage.is-phone .appr-signin {
	width: 390px;
	max-width: 100%;
	margin: 0 auto;
	padding: 30px 56px;
	justify-content: center;
}
.appr-stage.is-phone .appr-signin-brand {
	display: none;
}
.appr-stage.is-phone .appr-signin-card {
	max-width: none;
	width: 100%;
	padding: 21px 30px 30px; /* pages/front.php: pt-7, p-10 (1.75rem / 2.5rem at the phone's 12px rem) */
}
.appr-stage.is-phone .appr-signin-mbrand {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	padding-bottom: 21px; /* the same as the space above it (front.php mb-7) */
	margin-bottom: 12px;
	border-bottom: 1px solid var(--kt-gray-300);
}
/* The real page's defaults: a 45px logo (at most 100px wide for the icon, 250px for a
   banner, until a size is set) and an 18px name. */
.appr-signin-mbrand img {
	height: 45px;
	max-width: 100px;
	min-width: 0;
}
.appr-signin-mbrand img[data-appr-img="banner"] {
	max-width: 250px;
}
.appr-stage.has-front-size .appr-signin-mbrand img,
.appr-stage.has-phone-size .appr-signin-mbrand img {
	max-width: 100%;
}
.appr-signin-mbrand .appr-site-name {
	font-weight: 700;
	font-size: 18px;
}
/* Appearance switches show their words only when the preview card itself is wide
   (a container query, so zoom and the panel beside it count too); icons otherwise. */
.appr-previews {
	container-type: inline-size;
}
@container (max-width: 719.98px) {
	/* As specific as the theme's .menu-item .menu-link .menu-title, which would show it again. */
	.appr-switch .menu-item .menu-link .menu-title {
		display: none;
	}
	.appr-switch .menu-item .menu-link .menu-icon {
		margin: 0 !important;
	}
}
/* Appearance below desktop width (phones, tablets, small laptops): narrow
   margins on the preview and the settings panel so the content gets the width;
   the three tabs and the Light / Dark pill fit on one compact row. */
@media (max-width: 1199.98px) {
	.appr-panel > .card-header {
		padding: 0.5rem 0.75rem;
		min-height: 0;
	}
	.appr-panel > .card-body {
		padding: 0.75rem;
	}
	.appr-previews .nav-line-tabs {
		flex-wrap: nowrap;
		font-size: 0.85rem !important;
	}
	.appr-previews .nav-line-tabs .nav-link {
		padding: 0.4rem 0.6rem;
		white-space: nowrap;
	}
	.appr-switch .menu-link {
		padding: 0.25rem 0.55rem !important;
	}
	.appr-signin {
		min-height: 0;
		padding: 16px 12px;
		gap: 12px;
	}
	.appr-signin-card {
		max-width: none;
		width: 100%;
		padding: 16px;
	}
}
/* Appearance App preview: the content column may shrink beside the two menus;
   the sample table scrolls sideways inside it instead of overflowing. */
.appr-main {
	min-width: 0;
}
.appr-content {
	overflow-x: auto;
}
.appr-topbar .theme-sample-crumbs {
	white-space: nowrap;
}
[data-appr-editing] > span {
	margin: 0 0.25em;
}
/* Appearance App preview below desktop width: smaller menus (expanded 120px,
   collapsed 40px) so the sample page beside them keeps room. */
@media (max-width: 1199.98px) {
	.appr-sidebar {
		width: 120px;
		padding: 8px 6px;
		font-size: 0.7rem;
	}
	.appr-sidebar.is-collapsed {
		width: 40px;
	}
	.appr-sidebar .appr-sidebar-brand {
		min-height: 36px;
		margin: -8px -6px 6px;
		padding: 0 8px;
	}
	.appr-sidebar.is-collapsed .appr-sidebar-brand {
		padding: 0;
	}
	.appr-sidebar .theme-sample-sidebar-item {
		padding-top: 0.35rem !important;
		padding-bottom: 0.35rem !important;
	}
	.appr-sidebar .theme-sample-sidebar-item i {
		font-size: 0.75rem;
	}
	.appr-sidebar .appr-sidebar-brand .h-50px,
	.appr-sidebar .appr-sidebar-brand .mh-50px {
		height: auto !important;
		max-height: 24px !important;
	}
	.appr-sidebar .appr-sidebar-brand .mw-200px {
		max-width: 96px !important;
	}
	.appr-sidebar .appr-sidebar-brand .h-30px {
		height: 16px !important;
	}
}

/* With a logo size set, the narrow preview menus scale it like their own caps (120px for 170px). */
@media (max-width: 1199.98px) {
	.appr-stage.has-menu-size .appr-sidebar:not(.is-collapsed) .appr-sidebar-brand img.app-sidebar-logo-default {
		height: calc(var(--appr-menu-h) * 0.7) !important;
	}
}
/* Table footer (summary, page buttons, rows per page), laid out by the table's
   own width, not the screen's, so a table in a narrow card never overlaps:
   one row from 992px, two rows from 576px, stacked below with a short summary. */
.gridjs-container {
	container-type: inline-size;
}
.gridjs-footer {
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	grid-template-areas: "summary pages size";
	align-items: center;
	gap: 0.5rem 1rem;
	/* No side padding: the summary and the select line up with the table's edges. */
	padding: 0.5rem 0;
}
.gridjs-footer .gridjs-pagination {
	display: contents;
}
.gridjs-footer .gridjs-summary {
	grid-area: summary;
	white-space: nowrap;
}
.gridjs-footer .gridjs-pages {
	grid-area: pages;
	display: flex;
	flex-wrap: nowrap;
	justify-content: center;
	gap: 0.25rem;
}
/* Set by fitFooters() (grid_comp.mjs) only when one row doesn't fit: the
   page numbers move to their own row under the summary and the select. */
.gridjs-footer.grid-footer-two-rows {
	grid-template-columns: 1fr auto;
	grid-template-areas: "summary size" "pages pages";
}
.gridjs-footer.grid-footer-two-rows .gridjs-pages {
	flex-wrap: wrap;
}
.gridjs-footer > .grid-page-size {
	grid-area: size;
	justify-self: end;
	white-space: nowrap;
}
@container (width < 992px) {
	/* Just the select: its options say what it is. */
	.gridjs-footer > .grid-page-size > span {
		display: none;
	}
}
.grid-server-pager-wrap {
	container-type: inline-size;
}
/* A large listing loading a page (grid.mjs initServerPager). */
.grid-is-loading {
	opacity: 0.5;
	pointer-events: none;
	transition: opacity 0.15s;
}
.grid-loading {
	position: fixed;
	/* Above a popup (1055) loading its form, below a confirm (1065) and messages (1075). */
	z-index: 1060;
	transform: translate(-50%, -50%);
}
/* Every loader's spinner and "Loading..." (the page loader and a large
   listing's): stacked and centred on a small card, so the text behind never
   shows through it. */
.loading-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	/* A square, the spinner and text centred in it: equal space on every side. */
	justify-content: center;
	aspect-ratio: 1;
	/* A little more on top (about a third of the text's height, on purpose
	   not all of it): the group moves down a touch, so the spinner reads
	   nearer the middle. */
	padding: 2.1rem 1.5rem 1.5rem;
	border-radius: 0.625rem;
	background: var(--kt-body-bg, var(--bs-body-bg, #fff));
	box-shadow: var(--kt-box-shadow, 0 0.5rem 1.5rem rgba(0, 0, 0, 0.15));
}
/* The text's line box trimmed to its letters (line-height 1) and a tighter gap:
   the visible spinner-and-text group is what gets centred, not empty line space
   under the text, which made the group look high. */
/* An empty listing still loading is shorter than the loader's card (and its
   shadow): room for the whole card until the rows arrive. */
#grid_body[data-kt-app-page-loading="on"] {
	/* !important: the element's min-h-100px utility is !important too. */
	min-height: 12rem !important;
}
/* The theme fades the whole page loader (opacity 0.7), card included: fade
   only its background, so the card looks the same as a large listing's. */
.page-loader {
	opacity: 1;
	background-color: color-mix(in srgb, var(--kt-body-bg, #fff) 70%, transparent);
}
/* Large listings sort on the server by header click (grid.mjs initServerPager). */
th.grid-server-sortable {
	cursor: pointer;
}
th[data-server-sort] .gridjs-th-content::after {
	margin-left: 0.35rem;
	font-size: 0.75em;
	color: var(--kt-primary, var(--bs-primary));
}
th[data-server-sort="asc"] .gridjs-th-content::after {
	content: "▲";
}
th[data-server-sort="desc"] .gridjs-th-content::after {
	content: "▼";
}

/* LOADING_STYLE "skeleton" (src/mjs/loading.mjs, include/helpers/loading.php):
   shimmering bars in the shape of what is coming. */
.skeleton-cover {
	position: absolute;
	inset: 0;
	z-index: 6;
	overflow: hidden;
	background: var(--kt-body-bg, var(--bs-body-bg, #fff));
}
/* In place of a screen or popup's content (its own content hidden meanwhile). */
.skeleton-cover.skeleton-flow {
	position: static;
	background: none;
	overflow: visible;
}
/* Only while the skeleton is still in: new content replaces it before the marker
   comes off (after its scripts load), and would otherwise be hidden meanwhile. */
.skeleton-active:has(> .skeleton-cover) > :not(.skeleton-cover) {
	display: none !important;
}
.skeleton-bar {
	display: block;
	height: 0.85rem;
	border-radius: 0.35rem;
	/* Tints of the text colour over whatever is behind, so bars follow every theme and mode. */
	background: linear-gradient(90deg, color-mix(in srgb, currentColor 9%, transparent) 25%, color-mix(in srgb, currentColor 4%, transparent) 37%, color-mix(in srgb, currentColor 9%, transparent) 63%);
	background-size: 400% 100%;
	animation: skeleton-shimmer 1.4s ease infinite;
}
/* Bar widths as classes, not inline styles: the page's security policy blocks style
   attributes. !important keeps them winning like the inline width did.
   Same order as the widths in loading.mjs / loading.php. */
.skeleton-bar.sk-w-0 { width: 72% !important; }
.skeleton-bar.sk-w-1 { width: 54% !important; }
.skeleton-bar.sk-w-2 { width: 88% !important; }
.skeleton-bar.sk-w-3 { width: 40% !important; }
.skeleton-bar.sk-w-4 { width: 64% !important; }
.skeleton-bar.sk-w-5 { width: 80% !important; }
.skeleton-bar.sk-w-6 { width: 48% !important; }
@keyframes skeleton-shimmer {
	from { background-position: 100% 50%; }
	to { background-position: 0 50%; }
}
/* A screen: the card the real screen sits on. */
.skeleton-card {
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
	padding: 1.5rem 2rem 2rem;
	border-radius: 0.625rem;
	background: var(--kt-card-bg, var(--bs-body-bg, #fff));
	box-shadow: var(--kt-card-box-shadow, 0 0.1rem 1rem rgba(0, 0, 0, 0.05));
}
.sk-title {
	width: 12rem !important;
	max-width: 40%;
	height: 1.25rem;
}
.sk-crumb,
.sk-toolbar {
	display: flex;
	align-items: center;
	gap: 0.75rem;
}
.sk-grow {
	flex: 1;
}
.sk-crumb > .skeleton-bar {
	height: 2.4rem;
}
.sk-btn {
	width: 7rem;
}
.sk-btn-sm {
	width: 4.5rem;
}
.sk-search {
	width: 14rem;
	max-width: 40%;
	height: 2.6rem;
}
.sk-count {
	width: 4rem;
}
.sk-icon {
	width: 3.25rem;
	height: 2.6rem;
}
/* A table: a darker header row, then rows at a data row's height. */
.sk-table {
	display: flex;
	flex-direction: column;
}
.sk-thead,
.sk-tr {
	display: flex;
	gap: 1.25rem;
	padding: 0 0.75rem;
}
.sk-thead {
	align-items: center;
	height: 2.75rem;
	border-radius: 0.35rem 0.35rem 0 0;
	background: color-mix(in srgb, currentColor 4%, transparent);
}
.sk-thead .skeleton-bar {
	width: 60%;
	background: color-mix(in srgb, currentColor 13%, transparent);
	animation: none;
}
.sk-tr {
	align-items: center;
	height: 4.5rem;
	border-bottom: 1px solid color-mix(in srgb, currentColor 9%, transparent);
}
.sk-td {
	flex: 1;
	min-width: 0;
}
/* A form: two columns of label and field, then the button. */
.sk-form {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
	gap: 1.5rem 1.25rem;
}
.skeleton-field {
	display: flex;
	flex-direction: column;
	gap: 0.6rem;
}
.skeleton-label {
	height: 0.7rem;
	max-width: 30%;
}
.skeleton-input {
	height: 2.6rem;
}
.sk-form > .sk-tall .skeleton-input {
	height: 5.5rem;
}
.sk-form > .sk-wide {
	grid-column: 1 / -1;
}
/* A form's records under its fields (like a sub-table card). */
.sk-section {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	padding: 1rem 1.25rem 1.25rem;
	border: 1px solid color-mix(in srgb, currentColor 9%, transparent);
	border-radius: 0.625rem;
}
.sk-subtitle {
	width: 9rem;
	height: 1.1rem;
}
.sk-section .sk-crumb > .skeleton-bar {
	height: 1.1rem;
}
.sk-section .sk-crumb > .sk-btn {
	height: 2.2rem;
}
.sk-submit {
	width: 6rem;
	height: 2.5rem;
}
/* The lighter Save & Close beside Save & New: in light mode the theme's green
   text on its pale shade is too faint, so the text is a darker shade of it. */
[data-theme="light"] #form_submit[data-after-save="close"]:not(:hover):not(:focus),
[data-theme="light"] #form_submit[data-after-save="close"]:not(:hover):not(:focus) i {
	color: color-mix(in srgb, var(--bs-success) 55%, #000) !important;
}
/* The sign-in form: centred heading, full-width button, no card of its own
   (the login card already is one). Sizes as the loaded form (TPL #996): the
   heading 25px tall, 12px down with 12px under it; fields and button 40px,
   24px apart; 30px after the button. */
.skeleton-card.sk-login {
	gap: 24px;
	padding: 12px 0 30px;
	background: none;
	box-shadow: none;
}
.sk-login > .sk-title {
	align-self: center;
	height: 25px;
	margin-bottom: -12px;
}
.sk-login > .skeleton-input,
.sk-login > .sk-submit {
	height: 40px;
}
.sk-login > .sk-submit {
	width: 100%;
}
/* The spinner card centred in an area that is still empty. */
.loading-center {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 12rem;
}
/* A screen's first load in skeleton style: the shape fills the page loader from the top. */
.page-loader:has(.skeleton-cover) {
	align-items: flex-start !important;
	justify-content: stretch !important;
	background: var(--kt-app-bg-color, var(--bs-body-bg, #fff));
}
.page-loader > .skeleton-cover {
	flex: 1;
	background: var(--kt-app-bg-color, var(--bs-body-bg, #fff));
}
/* Room for the whole table shape (header and six rows) while a listing first loads. */
#grid_body[data-kt-app-page-loading="on"]:has(.skeleton-cover) {
	min-height: 32rem !important;
}
/* A box view (Tree, Levels) showing instead of the list: the hidden list's
   loader and its room stay out of sight; the box view loads in its own area (TPL #749). */
#grid_body.box-view > .page-loader {
	display: none !important;
}
#grid_body.box-view[data-kt-app-page-loading="on"] {
	min-height: 0 !important;
}
.loading-card > span + span {
	margin-top: 0.875rem !important;
	line-height: 1;
	/* As wide as the "..." after it, so the word itself sits centred. */
	padding-left: 0.6em;
}
.grid-loading.d-none {
	display: none !important;
}
@container (width < 576px) {
	.gridjs-footer,
	.gridjs-footer.grid-footer-two-rows {
		grid-template-columns: 1fr;
		grid-template-areas: "summary" "pages" "size";
		justify-items: center;
	}
	.gridjs-footer > .grid-page-size {
		justify-self: center;
	}
	/* "1 – 20 of 177,124" -> "1–20": just the range on a phone */
	.gridjs-footer .gridjs-summary {
		font-size: 0 !important;
	}
	.gridjs-footer .gridjs-summary b {
		font-size: 0.85rem;
	}
	.gridjs-footer .gridjs-summary b:nth-of-type(1)::after {
		content: "–";
	}
	.gridjs-footer .gridjs-summary b:nth-of-type(3) {
		display: none;
	}
}
/* A popup form's own card blends into the popup: no card inside a card (TPL #1008). */
#kt_modal_content_container > .row > .col-12 > .card {
	background: transparent;
	border: 0;
	/* The card style puts its shadow on with !important. */
	box-shadow: none !important;
}
/* A confirm popup (#modalAlert) opened from inside another popup stays on top
   of it, and its backdrop dims the popup underneath. */
#modalAlert {
	z-index: 1065;
}
.modal-backdrop ~ .modal-backdrop {
	z-index: 1060;
}
/* Success / error messages stay above every popup, the confirm included. */
.swal2-container {
	z-index: 1075 !important;
}
/* The theme caps popup content at 200px, so a few option blocks already scroll:
   grow with the content and scroll only past most of the screen. Same selector
   as the theme's rule (plugins.bundle.css), which loads earlier, so this wins. */
.swal2-container .swal2-html-container {
	max-height: min(70vh, 640px);
}
/* Corner messages: the toast library's id rule (#toastr-container>div) forces white
   text at 80% opacity, beating the theme's text colour, so a light message colour
   showed white text with the page bleeding through (TPL #1091). Solid, theme text. */
#toastr-container > .toastr {
	opacity: 1;
}
#toastr-container > .toastr-info { color: var(--kt-info-inverse); }
#toastr-container > .toastr-success { color: var(--kt-success-inverse); }
#toastr-container > .toastr-warning { color: var(--kt-warning-inverse); }
#toastr-container > .toastr-error { color: var(--kt-danger-inverse); }
/* Option blocks (form type option_block): pick-one cards; the chosen one gets
   the theme's primary border and a light fill, in light and dark mode. */
.option-block {
	cursor: pointer;
	border-color: var(--bs-gray-300) !important;
	transition: border-color 0.15s, background-color 0.15s;
}
.option-block:hover {
	border-color: var(--bs-primary) !important;
}
.option-block:has(input:checked) {
	border-color: var(--bs-primary) !important;
	background-color: color-mix(in srgb, var(--bs-primary) 12%, transparent);
}
/* The chosen radio shows its dot in light mode too: the read-only field rule
   (grey, !important) also matches radios and is more specific, so this one
   needs !important and a selector that outranks it. */
.option-blocks .option-block.option-block input.form-check-input[type="radio"]:checked:checked {
	background-color: var(--bs-primary) !important;
	border-color: var(--bs-primary);
}
/* Date range quick picks (TPL #540): a list on the left of the calendar,
   the chosen one in the theme's primary color. */
.flatpickr-calendar.flatpickr-has-presets {
	width: calc(307.875px + 140px) !important;
	padding-left: 140px;
	min-height: 370px;
}
.flatpickr-presets {
	position: absolute;
	top: 0;
	bottom: 0;
	left: 0;
	width: 140px;
	padding: 0.5rem;
	overflow-y: auto;
	border-right: 1px solid var(--bs-border-color);
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
}
.flatpickr-preset {
	border: 0;
	background: transparent;
	color: var(--kt-body-color);
	text-align: left;
	padding: 0.4rem 0.6rem;
	border-radius: 0.475rem;
	font-size: 0.9rem;
}
.flatpickr-preset:hover {
	background: color-mix(in srgb, var(--bs-primary) 10%, transparent);
	color: var(--bs-gray-900);
}
.flatpickr-preset.active {
	/* A darker shade keeps white text readable on a light theme color. */
	background: color-mix(in srgb, var(--bs-primary) 78%, #000);
	color: #fff;
}
/* The listing date filter's quick picks (vendored picker): readable text. */
.daterangepicker .ranges li:not(.active) {
	color: var(--bs-gray-800);
}
.daterangepicker .ranges li.active {
	background-color: color-mix(in srgb, var(--bs-primary) 78%, #000);
	color: #fff;
}

/* A top-bar icon button that keeps its card at rest (e.g. the sign-in page gear);
   a color theme replaces this with its Top Bar Icons background. */
.btn-custom-card {
	background-color: var(--bs-light);
}

/* Top bar icons hover like a notification row: the icon takes the Primary color.
   html[data-theme] + .btn outranks the theme's Top Bar Icons text color (also !important). */
html[data-theme] .app-navbar .btn.btn-custom:hover i,
html[data-theme] .app-navbar .btn.btn-custom.show i,
html[data-theme] .app-navbar .btn.btn-custom:hover .svg-icon,
html[data-theme] .app-navbar .btn.btn-custom.show .svg-icon {
	color: var(--bs-primary) !important;
}

/* Idle lock cover (TPL #289, lockscreen.mjs): the page behind stays loaded but unreadable. */
body.screen-locked .modal-backdrop.show {
	opacity: 1;
}
