mirror of
https://github.com/itflow-org/itflow
synced 2026-11-11 15:07:24 +00:00
4454 lines
135 KiB
CSS
4454 lines
135 KiB
CSS
/*
|
||
For screens below 576px (xs):
|
||
- Make the button full-width, display:block
|
||
*/
|
||
@media (max-width: 575.98px) {
|
||
.btn-responsive {
|
||
display: block;
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
/*
|
||
For screens 576px (sm) and above:
|
||
- Revert to an inline style
|
||
*/
|
||
@media (min-width: 576px) {
|
||
.btn-responsive {
|
||
display: inline-block;
|
||
width: auto;
|
||
}
|
||
}
|
||
|
||
.drag-handle {
|
||
cursor: grab !important;
|
||
}
|
||
|
||
.drag-handle:active {
|
||
cursor: grabbing !important;
|
||
}
|
||
|
||
.star-toggle input {
|
||
display: none;
|
||
}
|
||
|
||
.star-toggle i {
|
||
cursor: pointer;
|
||
font-size: 1.2rem;
|
||
color: #adb5bd;
|
||
}
|
||
|
||
.star-toggle input:checked + i {
|
||
color: #f1c40f;
|
||
font-weight: 900;
|
||
}
|
||
|
||
.checkbox-column {
|
||
width: 40px;
|
||
min-width: 40px;
|
||
text-align: center;
|
||
}
|
||
|
||
.checkbox-column .form-check {
|
||
padding-left: 0;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.checkbox-column .form-check-input {
|
||
/* BS5 uses float:left here where BS4 used position:absolute - without
|
||
float:none the box ignores the cell's text-align and pins to the left */
|
||
float: none;
|
||
position: static;
|
||
margin: 0;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------
|
||
AdminLTE 4 compatibility.
|
||
v4 dropped .text-sm and the accent-* / navbar-* / sidebar-dark-*
|
||
trio that ITFlow's 16 theme choices were built on. In v3 the theme
|
||
colour drove the TOP NAVBAR, link colours and the active sidebar
|
||
item - the sidebar itself was always dark. That is reproduced here.
|
||
--------------------------------------------------------------- */
|
||
/* Font stack. v3 led with "Source Sans Pro" then -apple-system; v4/BS5 leads
|
||
with "Source Sans 3" then system-ui. ITFlow loads neither webfont, so the
|
||
two stacks fall through to different system fonts. Pinned to v3's. */
|
||
:root {
|
||
--bs-font-sans-serif: "Source Sans Pro", -apple-system, BlinkMacSystemFont,
|
||
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif,
|
||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||
}
|
||
|
||
/* The full .text-sm cascade. AdminLTE 3 shipped ~100 rules for this; these are
|
||
the ones that actually set sizing, and without them the app renders a step
|
||
larger everywhere than it used to. */
|
||
.text-sm {
|
||
font-size: .875rem;
|
||
}
|
||
.text-sm .form-control,
|
||
.text-sm .form-select,
|
||
.text-sm .input-group-text,
|
||
.text-sm table {
|
||
font-size: .875rem;
|
||
}
|
||
.text-sm .btn,
|
||
.text-sm .dropdown-menu {
|
||
font-size: .875rem !important;
|
||
}
|
||
.text-sm .card-title {
|
||
font-size: 1rem;
|
||
}
|
||
.text-sm .content-header h1,
|
||
.text-sm .app-content-header h1 {
|
||
font-size: 1.5rem;
|
||
}
|
||
.text-sm .brand-link {
|
||
font-size: inherit;
|
||
}
|
||
|
||
/* Sidebar density. v3 tightened nav-link padding under .text-sm to .4rem .8rem;
|
||
BS5's default is .5rem 1rem, which is what made the menu items taller. */
|
||
.text-sm .nav-link {
|
||
padding: .4rem .8rem;
|
||
}
|
||
/* v3 nav-icon metrics - v4 switched these to a 1.5rem flex box */
|
||
.sidebar-menu .nav-icon {
|
||
min-width: 1.6rem;
|
||
max-width: 1.6rem;
|
||
margin-left: .05rem;
|
||
margin-right: .2rem;
|
||
font-size: 1.2rem;
|
||
}
|
||
|
||
/* one accent per theme - v3 used the same hex for navbar, links and
|
||
the active sidebar item */
|
||
/* black has no upstream token - AdminLTE calls its darkest neutral
|
||
gray-dark (#343a40), which is a different colour */
|
||
.theme-black { --itflow-accent: #000000; }
|
||
.theme-blue { --itflow-accent: var(--bs-blue, #007bff); }
|
||
.theme-cyan { --itflow-accent: var(--bs-cyan, #17a2b8); }
|
||
.theme-fuchsia { --itflow-accent: var(--bs-fuchsia, #f012be); }
|
||
.theme-gray { --itflow-accent: var(--bs-gray, #6c757d); }
|
||
.theme-green { --itflow-accent: var(--bs-green, #28a745); }
|
||
.theme-indigo { --itflow-accent: var(--bs-indigo, #6610f2); }
|
||
.theme-lightblue { --itflow-accent: var(--bs-lightblue, #3c8dbc); }
|
||
.theme-maroon { --itflow-accent: var(--bs-maroon, #d81b60); }
|
||
.theme-navy { --itflow-accent: var(--bs-navy, #001f3f); }
|
||
.theme-olive { --itflow-accent: var(--bs-olive, #3d9970); }
|
||
.theme-orange { --itflow-accent: var(--bs-orange, #fd7e14); }
|
||
.theme-pink { --itflow-accent: var(--bs-pink, #e83e8c); }
|
||
.theme-purple { --itflow-accent: var(--bs-purple, #6f42c1); }
|
||
.theme-red { --itflow-accent: var(--bs-red, #dc3545); }
|
||
.theme-teal { --itflow-accent: var(--bs-teal, #20c997); }
|
||
.theme-yellow { --itflow-accent: var(--bs-yellow, #ffc107); }
|
||
|
||
/* top navbar takes the theme colour */
|
||
.app-header.navbar {
|
||
background-color: var(--itflow-accent, #007bff) !important;
|
||
}
|
||
|
||
/* Navbar height. v4 pairs .app-header{max-height:2.75rem} with 0.5rem of
|
||
navbar padding, but the tallest children want 47-48px and overflow that
|
||
44px cap. Everything below is sized so it fits inside a 3rem bar - the
|
||
.navbar-search field is ~33px and clears it without help.
|
||
NOTE: .sidebar-brand is the matching half of this pair and is set to the
|
||
same height further down - change both or they stop lining up. */
|
||
.app-header.navbar {
|
||
--bs-navbar-padding-y: .4rem;
|
||
min-height: 3rem;
|
||
max-height: 3rem;
|
||
}
|
||
/* Every item stretches the full bar and centres its own contents, so icons,
|
||
the avatar and the search box all sit on the same optical line regardless of
|
||
their individual heights. */
|
||
.app-header .navbar-nav {
|
||
align-items: stretch;
|
||
}
|
||
.app-header .navbar-nav > .nav-item {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
.app-header .navbar-nav .nav-link {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 100%;
|
||
max-height: none;
|
||
padding-top: 0;
|
||
padding-bottom: 0;
|
||
}
|
||
/* v4 sizes the avatar 2rem with a -2px nudge, which is too tall for this bar */
|
||
.app-header .navbar-nav > .user-menu .user-image {
|
||
width: 1.6rem;
|
||
height: 1.6rem;
|
||
margin-top: 0;
|
||
}
|
||
/* .navbar-badge positions itself (top:9px/right:5px) for a taller bar. Pinned
|
||
to the link's edge, it slid over the icon whenever the link got narrower -
|
||
on phones the link is 28px wide and the badge covered the bell completely.
|
||
The icon is centred in the link, so anchoring to the centre puts the badge
|
||
on the icon's top-right corner at every width. */
|
||
.app-header .navbar-badge {
|
||
top: 50%;
|
||
right: auto;
|
||
left: calc(50% + .2rem);
|
||
margin-top: -.9rem;
|
||
font-size: .65rem;
|
||
}
|
||
|
||
/* sidebar stays dark; only the ACTIVE item picks up the theme.
|
||
|
||
The :hover and :focus variants are load-bearing, not decoration. A bare
|
||
`.nav-link.active` scores exactly the same specificity as the grey hover
|
||
rule further down this file (both 0,5,0), and that rule comes later - so
|
||
pointing at the current page's menu item repainted its accent grey.
|
||
AdminLTE 3 won this same tie on source order alone: its per-theme
|
||
`.sidebar-dark-<colour> ... .nav-link.active` rule was emitted AFTER the
|
||
`[class*=sidebar-dark-] ... .nav-item:hover > .nav-link` block, so the
|
||
accent held on hover. Spelling the states out here wins on specificity
|
||
instead, which survives any future reordering of this sheet.
|
||
|
||
v3 also pinned its treeview active item the same way
|
||
(`.nav-link.active, .nav-link.active:hover, .nav-link.active:focus`), so
|
||
both levels are covered. Deliberately NO separate hover shade for the
|
||
active item: v3 had none, and the current item is already the loudest
|
||
thing in the sidebar. */
|
||
.app-sidebar .sidebar-menu > .nav-item > .nav-link.active,
|
||
.app-sidebar .sidebar-menu > .nav-item:hover > .nav-link.active,
|
||
.app-sidebar .sidebar-menu > .nav-item > .nav-link.active:focus,
|
||
.app-sidebar .nav-treeview > .nav-item > .nav-link.active,
|
||
.app-sidebar .nav-treeview > .nav-item > .nav-link.active:hover,
|
||
.app-sidebar .nav-treeview > .nav-item > .nav-link.active:focus {
|
||
background-color: var(--itflow-accent, #007bff);
|
||
color: #fff;
|
||
}
|
||
|
||
/* Bootstrap 5's reboot underlines every link; Bootstrap 4 did not, and
|
||
AdminLTE 3 additionally suppressed the hover underline. Restored so links
|
||
read the way they did before the migration. */
|
||
a,
|
||
a:hover,
|
||
a:focus {
|
||
text-decoration: none;
|
||
}
|
||
|
||
/* links and accented controls */
|
||
/* Tabs and pills. Bootstrap points inactive nav links at --bs-link-color and
|
||
hardcodes --bs-nav-pills-link-active-bg to #0d6efd, so both states came out
|
||
blue regardless of theme. Scoped to .nav-tabs/.nav-pills rather than .nav:
|
||
.navbar-nav redefines these same variables for the top bar, and the sidebar
|
||
is protected by its own higher-specificity rules. */
|
||
.nav-tabs,
|
||
.nav-pills {
|
||
/* inactive sits at 70% so the active state still reads as the highlight */
|
||
--bs-nav-link-color: color-mix(in srgb, var(--itflow-accent, #007bff) 70%, transparent);
|
||
--bs-nav-link-hover-color: var(--itflow-accent, #007bff);
|
||
}
|
||
.nav-tabs {
|
||
--bs-nav-tabs-link-active-color: var(--itflow-accent, #007bff);
|
||
/* accent rule along the top edge marks the selected tab */
|
||
--bs-nav-tabs-link-active-border-color: var(--itflow-accent, #007bff) var(--bs-border-color) var(--bs-body-bg);
|
||
}
|
||
.nav-tabs .nav-link.active {
|
||
font-weight: 600;
|
||
}
|
||
/* Pagination. Driving Bootstrap's own variables rather than overriding
|
||
.page-link directly: a flat `.page-item .page-link{color:...}` ties with
|
||
BS5's `.active>.page-link` and `.disabled>.page-link` on specificity and,
|
||
because this sheet loads last, silently won both - so the active number was
|
||
accent-on-accent (invisible) and disabled Prev/Next lost their muted grey. */
|
||
/* Bootstrap's generic focus ring (buttons, links, .btn-check labels) */
|
||
.form-check-input:checked {
|
||
background-color: var(--itflow-accent, #007bff);
|
||
border-color: var(--itflow-accent, #007bff);
|
||
}
|
||
|
||
/* sidebar is ALWAYS dark - v4 gives .app-sidebar no background at all.
|
||
These are AdminLTE 3.2.0's [class*=sidebar-dark-] values verbatim. */
|
||
.app-sidebar {
|
||
background-color: #343a40;
|
||
}
|
||
/* Sidebar header. v4 scopes its brand rules under .sidebar-brand, and its own
|
||
defaults centre the brand in a fixed 2.75rem box. v3's .brand-link was a
|
||
left-aligned block, and several of these headers carry a "Back | ..." line
|
||
that is taller than that, so height is relaxed and alignment restored. */
|
||
.app-sidebar .sidebar-brand {
|
||
/* v4 deliberately pairs .sidebar-brand{height:2.75rem} with
|
||
.app-header{max-height:2.75rem} so the sidebar header lines up with the
|
||
top bar. Both are set to 3rem above - change them together or
|
||
they stop lining up. v4's .8125rem padding leaves too little room for
|
||
the heading, so it is cut right down. */
|
||
height: 3rem;
|
||
padding: .3rem .5rem;
|
||
justify-content: flex-start;
|
||
background-color: #343a40;
|
||
border-bottom: 1px solid #4b545c;
|
||
}
|
||
.app-sidebar .sidebar-brand .brand-link {
|
||
width: 100%;
|
||
color: #fff;
|
||
}
|
||
.app-sidebar .sidebar-brand .brand-text {
|
||
color: #fff;
|
||
line-height: 1.2;
|
||
}
|
||
/* Bootstrap sizes .h1-.h4 responsively (e.g. .h4 is calc(1.275rem + .3vw)), so
|
||
on a wide screen they outgrow the bar and v4's .sidebar-brand{overflow:hidden}
|
||
clips them. Cap ONLY those - .h5 (1.25rem) and .h6 (1rem) are fixed and must
|
||
keep their own size, or the "Back | ..." headers get blown up to h4. */
|
||
.app-sidebar .sidebar-brand .brand-text.h1,
|
||
.app-sidebar .sidebar-brand .brand-text.h2,
|
||
.app-sidebar .sidebar-brand .brand-text.h3,
|
||
.app-sidebar .sidebar-brand .brand-text.h4,
|
||
.app-sidebar .sidebar-brand h1.brand-text,
|
||
.app-sidebar .sidebar-brand h2.brand-text,
|
||
.app-sidebar .sidebar-brand h3.brand-text,
|
||
.app-sidebar .sidebar-brand h4.brand-text {
|
||
font-size: min(1.6rem, calc(1.275rem + .3vw));
|
||
}
|
||
|
||
/* Company brand - agent/includes/side_nav.php (main nav only; the client,
|
||
admin and reports navs keep their "Back | ..." headers above).
|
||
|
||
The logo or initials tile with the company name beside it, the whole row one
|
||
rounded hover target like a workspace switcher. Sized to sit inside the 3rem
|
||
header that lines up with the top bar. */
|
||
.app-sidebar .itflow-brand-link {
|
||
gap: .6rem;
|
||
height: 100%;
|
||
padding: .2rem .4rem;
|
||
border-radius: var(--bs-border-radius);
|
||
color: #fff;
|
||
transition: background-color .15s ease-in-out;
|
||
}
|
||
.app-sidebar .itflow-brand-link:hover {
|
||
background-color: rgba(255, 255, 255, .07);
|
||
color: #fff;
|
||
}
|
||
.app-sidebar .itflow-brand-link:focus-visible {
|
||
outline: 2px solid var(--itflow-accent, var(--bs-primary));
|
||
outline-offset: -2px;
|
||
}
|
||
.itflow-brand-mark {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 2rem;
|
||
border-radius: var(--bs-border-radius);
|
||
overflow: hidden;
|
||
}
|
||
/* Logos are drawn for a light page, so they get a light tile. A wide logo
|
||
widens the tile (up to a point) instead of shrinking to fit a square. */
|
||
.itflow-brand-mark-logo {
|
||
min-width: 2rem;
|
||
max-width: 5.5rem;
|
||
padding: .2rem .3rem;
|
||
background-color: #fff;
|
||
box-shadow: 0 1px 2px rgba(0, 0, 0, .35);
|
||
}
|
||
.itflow-brand-mark-logo img {
|
||
display: block;
|
||
width: auto;
|
||
height: 100%;
|
||
max-width: 100%;
|
||
object-fit: contain;
|
||
}
|
||
/* No logo: the company's initials on the theme colour. The inner ring keeps
|
||
the tile visible when the theme colour is close to the sidebar's (black,
|
||
navy, gray). */
|
||
.itflow-brand-mark-initials {
|
||
width: 2rem;
|
||
font-size: .7rem;
|
||
font-weight: 700;
|
||
letter-spacing: .03em;
|
||
color: #fff;
|
||
background-color: var(--itflow-accent, #007bff);
|
||
background-image: linear-gradient(135deg, rgba(255, 255, 255, .18), rgba(0, 0, 0, .18));
|
||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .18), 0 1px 2px rgba(0, 0, 0, .35);
|
||
}
|
||
.theme-yellow .itflow-brand-mark-initials {
|
||
color: #1f2d3d;
|
||
}
|
||
/* Two lines, then an ellipsis. v4's .sidebar-brand is nowrap, so wrapping
|
||
has to be switched back on here. */
|
||
.itflow-brand-name {
|
||
display: -webkit-box;
|
||
-webkit-box-orient: vertical;
|
||
-webkit-line-clamp: 2;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
font-size: .95rem;
|
||
font-weight: 600;
|
||
line-height: 1.15;
|
||
letter-spacing: -.01em;
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.app-sidebar .sidebar-menu .nav-link,
|
||
.app-sidebar .nav-header {
|
||
color: #c2c7d0;
|
||
}
|
||
.app-sidebar .sidebar-menu > .nav-item > .nav-link:focus,
|
||
.app-sidebar .sidebar-menu > .nav-item:hover > .nav-link,
|
||
.app-sidebar .sidebar-menu > .nav-item.menu-open > .nav-link {
|
||
background-color: rgba(255, 255, 255, .1);
|
||
color: #fff;
|
||
}
|
||
.app-sidebar .sidebar-menu > .nav-item > .nav-link.active {
|
||
color: #fff;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24);
|
||
}
|
||
.app-sidebar .nav-treeview {
|
||
background-color: transparent;
|
||
}
|
||
|
||
|
||
/* --- Bootstrap 5 unpaired background utilities -------------------------
|
||
BS4 + AdminLTE 3 shipped `.bg-dark,.card.bg-dark .card-body{color:#fff}`.
|
||
BS5 split the pair: .bg-* sets ONLY a background and you are expected to
|
||
use .text-bg-* for the pairing. ITFlow uses bg-dark 303 times (every modal
|
||
header, most card headers), so without this the header text is dark on
|
||
dark and effectively invisible. Contrast choices below mirror Bootstrap's
|
||
own .text-bg-* decisions. */
|
||
.bg-dark, .bg-primary, .bg-secondary, .bg-success, .bg-danger, .bg-purple {
|
||
color: #fff;
|
||
}
|
||
.bg-info, .bg-warning, .bg-light, .bg-olive, .bg-pink {
|
||
color: #000;
|
||
}
|
||
.card.bg-dark .card-body {
|
||
color: #fff;
|
||
}
|
||
/* keep close buttons visible on the dark headers they sit in */
|
||
.bg-dark > .btn-close, .modal-header.bg-dark .btn-close {
|
||
filter: var(--bs-btn-close-white-filter, invert(1) grayscale(100%) brightness(200%));
|
||
}
|
||
|
||
/* --- dashboard small-box ------------------------------------------------
|
||
AdminLTE 4 dropped v3's watermark icon. 4.8.5 restored it, but under a new
|
||
class name (.small-box-icon) that ITFlow's markup does not use - the boxes
|
||
still carry v3's <div class="icon"> wrapper - so these rules stay.
|
||
The inner padding and the p > small rule ARE back upstream at identical
|
||
values and have been dropped from here. */
|
||
.small-box .icon {
|
||
color: rgba(0, 0, 0, .15);
|
||
z-index: 0;
|
||
}
|
||
.small-box .icon > i,
|
||
.small-box .icon > svg {
|
||
position: absolute;
|
||
top: 15px;
|
||
right: 15px;
|
||
font-size: 90px;
|
||
transition: transform .3s linear;
|
||
}
|
||
.small-box:hover .icon > i,
|
||
.small-box:hover .icon > svg {
|
||
transform: scale(1.1);
|
||
}
|
||
/* Upstream sets z-index:5 on these but no `position`, which leaves it inert -
|
||
the icon is absolutely positioned at z-index 0, so the text needs a
|
||
stacking context of its own to stay above it. */
|
||
.small-box h3,
|
||
.small-box .h3,
|
||
.small-box p {
|
||
position: relative;
|
||
}
|
||
/* 4.8.5 sets this same 1rem, but also shrinks it to 12px below 576px. This
|
||
file loads last at equal specificity, so keeping the line deliberately
|
||
holds the desktop size on phones too; delete it to take their mobile
|
||
treatment instead. */
|
||
.small-box p {
|
||
font-size: 1rem;
|
||
}
|
||
.small-box hr {
|
||
opacity: .25;
|
||
}
|
||
.small-box > .small-box-footer {
|
||
color: rgba(255, 255, 255, .8);
|
||
}
|
||
.small-box > .small-box-footer:hover {
|
||
color: #fff;
|
||
}
|
||
|
||
/* --- utility classes AdminLTE 4 dropped -------------------------------- */
|
||
.text-bold, .text-bold.table td, .text-bold.table th {
|
||
font-weight: 700;
|
||
}
|
||
.text-xs {
|
||
font-size: .75rem !important;
|
||
}
|
||
.btn-default {
|
||
color: #6c757d;
|
||
background-color: #f8f9fa;
|
||
border-color: #ced4da;
|
||
}
|
||
.btn-default:hover {
|
||
color: #495057;
|
||
background-color: #e2e6ea;
|
||
border-color: #adb5bd;
|
||
}
|
||
.btn-flat {
|
||
border-radius: 0;
|
||
border-width: 1px;
|
||
box-shadow: none;
|
||
}
|
||
/* sidebar count badges - v3 positioned these absolutely */
|
||
.sidebar-menu .nav-link > .right,
|
||
.sidebar-menu .nav-link > p > .right {
|
||
position: absolute;
|
||
right: 1rem;
|
||
top: .7rem;
|
||
}
|
||
/* v3's softened box colours, kept for the three small-boxes that still use
|
||
them on the dashboard. The other fourteen were dropped - nothing in the
|
||
tree carries those classes, and the only class names PHP interpolates are
|
||
Bootstrap's seven contextual ones, so they were unreachable. adminlte-
|
||
colors-v3 defines all of them anyway if a page ever wants one back. */
|
||
.bg-olive { background-color: rgba(116,200,163,.9) !important; }
|
||
.bg-pink { background-color: rgba(232,62,140,.9) !important; }
|
||
.bg-purple { background-color: rgba(111,66,193,.9) !important; }
|
||
|
||
/* --- Flatpickr date-filter preset panel ---------------------------------
|
||
Flatpickr has no preset-ranges feature; js/date_filter.js injects this
|
||
panel to reproduce the nine ranges daterangepicker used to provide. */
|
||
/* The panel sits in a second column to the right of the month grid. This has to
|
||
be a GRID rather than flex: .flatpickr-calendar's own children are the month
|
||
nav and the day grid STACKED, so flexing the container would put those two
|
||
side by side as well. Two explicit columns keep flatpickr's own stacking in
|
||
column 1 and give the panel column 2, spanning both rows.
|
||
|
||
width:auto is safe in this layout but was NOT when the panel ran along the
|
||
bottom. The calendar is position:absolute, so auto means shrink-to-fit, which
|
||
resolves to max-content - and max-content for a horizontal row of nine nowrap
|
||
buttons is one long line that flex-wrap never breaks, while .flatpickr-days
|
||
keeps its own fixed 307.875px. Everything past the month grid then rendered as
|
||
an empty second pane. Stacked in a column the panel's max-content is just the
|
||
widest single label, so the container comes out calendar + panel wide.
|
||
|
||
The `> *` rule catches anything flatpickr adds later - an unplaced child would
|
||
otherwise auto-flow into column 2 and land on top of the panel. */
|
||
.flatpickr-calendar.itflow-fp-with-ranges.open {
|
||
display: grid;
|
||
grid-template-columns: max-content max-content;
|
||
grid-template-rows: auto auto;
|
||
align-items: start;
|
||
width: auto;
|
||
}
|
||
.flatpickr-calendar.itflow-fp-with-ranges > * {
|
||
grid-column: 1;
|
||
}
|
||
.flatpickr-calendar.itflow-fp-with-ranges > .itflow-fp-ranges {
|
||
grid-column: 2;
|
||
grid-row: 1 / span 2;
|
||
/* stretch so the divider runs the calendar's full height, not just the
|
||
height of the nine buttons */
|
||
align-self: stretch;
|
||
}
|
||
.itflow-fp-ranges {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: .125rem;
|
||
padding: .5rem;
|
||
border-left: 1px solid var(--bs-border-color, #dee2e6);
|
||
}
|
||
.itflow-fp-range {
|
||
padding: .2rem .5rem;
|
||
font-size: .8125rem;
|
||
/* .flatpickr-calendar sets line-height:24px and it inherits. Nine buttons at
|
||
that height overshoot the calendar's own ~296px; 1.4 brings the column back
|
||
under it so the two sides finish level. */
|
||
line-height: 1.4;
|
||
text-align: left;
|
||
color: var(--itflow-accent, #007bff);
|
||
white-space: nowrap;
|
||
background: none;
|
||
border: 1px solid transparent;
|
||
border-radius: .25rem;
|
||
}
|
||
.itflow-fp-range:hover,
|
||
.itflow-fp-range:focus,
|
||
.itflow-fp-range.active {
|
||
color: #fff;
|
||
background-color: var(--itflow-accent, #007bff);
|
||
border-color: var(--itflow-accent, #007bff);
|
||
}
|
||
/* The preset currently in effect - without this there is nothing on screen
|
||
telling you which range you are looking at once the calendar is open. */
|
||
.itflow-fp-range.active {
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* --- Flash toast ---------------------------------------------------------
|
||
Restores the four things toastr did that a plain Bootstrap toast does not:
|
||
sits top-right rather than centred over what you are reading, carries an
|
||
icon for its type, counts its own timer down, and dismisses on a click
|
||
anywhere instead of only on the small x.
|
||
|
||
It also appears on the FIRST PAINT. includes/inc_alert_feedback.php renders
|
||
the element with .show already set and every state change here is an
|
||
animation, so nothing waits on bootstrap.Toast - which used to mean waiting
|
||
for DOMContentLoaded, and therefore for every parser-blocking script in the
|
||
document. The inline script in that file only handles the click and removes
|
||
the node; neither gates the appearance.
|
||
|
||
Durations are toastr's own: 300ms in, 5s on screen, 1s out. */
|
||
.itflow-toast {
|
||
position: relative;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
animation:
|
||
itflow-toast-in 300ms ease-out both,
|
||
itflow-toast-out 1s ease-in 5s both;
|
||
}
|
||
|
||
/* Hovering holds it open, so a long message cannot disappear mid-sentence.
|
||
The progress bar needs its own rule - animation-play-state does not
|
||
cascade to a child's separate animation. */
|
||
.itflow-toast:hover,
|
||
.itflow-toast:hover .itflow-toast-progress {
|
||
animation-play-state: paused;
|
||
}
|
||
|
||
/* currentColor, so the bar inherits whichever contrast text-bg-* already
|
||
picked - white on success and danger, dark on warning and info - rather
|
||
than needing a rule per type. */
|
||
.itflow-toast-progress {
|
||
position: absolute;
|
||
right: 0;
|
||
bottom: 0;
|
||
left: 0;
|
||
height: 3px;
|
||
background: currentColor;
|
||
opacity: .4;
|
||
transform-origin: left;
|
||
animation: itflow-toast-progress 5s linear both;
|
||
}
|
||
|
||
@keyframes itflow-toast-in {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateX(100%);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
}
|
||
}
|
||
|
||
/* visibility flips on the last one percent rather than at the end, because a
|
||
discrete property switches at the MIDPOINT of its keyframe interval - put
|
||
it only on the 100% stop and the toast would vanish halfway through its own
|
||
fade. This is the fallback that matters if the inline remover never runs:
|
||
without it an invisible toast keeps sitting over the user menu, eating
|
||
clicks. */
|
||
@keyframes itflow-toast-out {
|
||
0% {
|
||
opacity: 1;
|
||
visibility: visible;
|
||
}
|
||
99% {
|
||
opacity: 0;
|
||
visibility: visible;
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
}
|
||
}
|
||
|
||
@keyframes itflow-toast-progress {
|
||
from {
|
||
transform: scaleX(1);
|
||
}
|
||
to {
|
||
transform: scaleX(0);
|
||
}
|
||
}
|
||
|
||
/* Reduced motion drops the slide and the moving bar but keeps the timed
|
||
dismissal - that is a timeout, not an animation the user is watching. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.itflow-toast {
|
||
animation: itflow-toast-out 1s linear 5s both;
|
||
}
|
||
.itflow-toast-progress {
|
||
animation: none;
|
||
transform: scaleX(0);
|
||
}
|
||
}
|
||
|
||
/* The JS-raised toasts from itflowToast() still go through bootstrap.Toast,
|
||
so they keep the slower fade. */
|
||
.itflow-toast-js .toast.fade {
|
||
transition: opacity .5s linear;
|
||
}
|
||
|
||
/* --- Filter footer -------------------------------------------------------
|
||
The pagination row wants a touch more breathing room than .py-2 gives, but
|
||
.py-3 is a 16px jump that makes the bar noticeably chunky. Bootstrap has no
|
||
step between the two, hence the named rule. */
|
||
.card-footer.itflow-filter-footer {
|
||
padding-top: .5rem;
|
||
padding-bottom: .5rem;
|
||
}
|
||
|
||
/* --- Primary button follows the theme -----------------------------------
|
||
Bootstrap 5 buttons are entirely variable driven, so these set --bs-btn-*
|
||
rather than overriding background-color. That keeps hover, active,
|
||
disabled and the focus ring consistent instead of only recolouring the
|
||
resting state.
|
||
|
||
The shade steps are Bootstrap's own: hover is shade 15%, active shade 20%,
|
||
and shade(c, n%) is literally mix(black, c, n%) - which color-mix
|
||
reproduces exactly. */
|
||
|
||
/* Focus ring has to be an RGB triplet, so it is per theme. */
|
||
|
||
/* Text colour on a solid primary button. Threshold derived from Bootstrap's
|
||
own palette (white up to L=0.183, black from L=0.486), so only these two
|
||
themes are light enough to need dark text - the rest keep white. */
|
||
|
||
/* Same L>0.335 contrast cut as the primary button. */
|
||
.theme-orange .nav-pills,
|
||
.theme-teal .nav-pills,
|
||
.theme-yellow .nav-pills {
|
||
--bs-nav-pills-link-active-color: #000;
|
||
}
|
||
|
||
|
||
/* --- Theme picker (admin/settings_theme.php) -----------------------------
|
||
Each choice is a miniature of the app rather than a colour circle, so you
|
||
can see where the colour actually lands before committing to it. Structure
|
||
and proportions are lifted from AdminLTE 4's own theme generator
|
||
(dist/generate/theme.html) - note those .lte-skin-preview-* rules live in
|
||
that page's inline <style> and are NOT in adminlte.min.css, so they are
|
||
reproduced here under ITFlow-owned names to avoid colliding with anything
|
||
upstream may ship later.
|
||
|
||
The mini-app reflects how ITFlow actually applies a theme, not AdminLTE's
|
||
presets: the sidebar is always dark with the accent on its active item, the
|
||
top bar takes the accent, and the content area shows an accent widget
|
||
beside a neutral one.
|
||
|
||
The preview carries .theme-<name>, so --itflow-accent is re-scoped for that
|
||
subtree and every swatch paints itself from the same variable the theme
|
||
will apply - add a theme to the stylesheet and its preview is correct with
|
||
no extra rule.
|
||
|
||
The radio is a visually hidden .btn-check and the label is the control.
|
||
Selection reads as a ring around the preview plus a bolder name. */
|
||
.itflow-theme-swatch {
|
||
display: block;
|
||
padding: .5rem .25rem;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
border-radius: .5rem;
|
||
transition: background-color .15s ease;
|
||
}
|
||
.itflow-theme-swatch:hover {
|
||
background-color: var(--bs-secondary-bg);
|
||
}
|
||
.itflow-theme-preview {
|
||
display: inline-flex;
|
||
width: 6rem;
|
||
height: 3.75rem;
|
||
overflow: hidden;
|
||
vertical-align: middle;
|
||
background-color: var(--bs-tertiary-bg);
|
||
border: 1px solid var(--bs-border-color);
|
||
border-radius: var(--bs-border-radius);
|
||
transition: border-color .15s ease, box-shadow .15s ease;
|
||
}
|
||
/* the sidebar is always dark in ITFlow, whatever the theme */
|
||
.itflow-theme-preview-sidebar {
|
||
display: flex;
|
||
flex: 0 0 30%;
|
||
flex-direction: column;
|
||
gap: 9%;
|
||
padding: 10% 12%;
|
||
color: #fff;
|
||
background-color: #343a40;
|
||
}
|
||
.itflow-theme-preview-brand,
|
||
.itflow-theme-preview-item {
|
||
display: block;
|
||
flex: none;
|
||
height: 7%;
|
||
min-height: 2px;
|
||
background: currentColor;
|
||
border-radius: 99px;
|
||
opacity: .45;
|
||
}
|
||
.itflow-theme-preview-brand {
|
||
width: 70%;
|
||
height: 11%;
|
||
min-height: 3px;
|
||
margin-bottom: 6%;
|
||
opacity: .9;
|
||
}
|
||
/* only the active sidebar item takes the accent - same as the real sidebar */
|
||
.itflow-theme-preview-active {
|
||
background: var(--itflow-accent, #007bff);
|
||
opacity: 1;
|
||
}
|
||
.itflow-theme-preview-main {
|
||
display: flex;
|
||
flex: auto;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
}
|
||
.itflow-theme-preview-header {
|
||
display: flex;
|
||
flex: 0 0 24%;
|
||
gap: 6%;
|
||
align-items: center;
|
||
padding: 0 8%;
|
||
color: #fff;
|
||
background-color: var(--itflow-accent, #007bff);
|
||
}
|
||
.itflow-theme-preview-header .itflow-theme-preview-item {
|
||
width: 24%;
|
||
height: 22%;
|
||
opacity: .55;
|
||
}
|
||
.itflow-theme-preview-avatar {
|
||
display: block;
|
||
width: 14%;
|
||
margin-left: auto;
|
||
aspect-ratio: 1;
|
||
background: currentColor;
|
||
border-radius: 50%;
|
||
opacity: .55;
|
||
}
|
||
.itflow-theme-preview-body {
|
||
display: grid;
|
||
flex: auto;
|
||
grid-template-rows: 1fr 1fr;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 8%;
|
||
padding: 10%;
|
||
}
|
||
.itflow-theme-preview-box {
|
||
display: block;
|
||
background-color: var(--itflow-accent, #007bff);
|
||
border-radius: 2px;
|
||
}
|
||
.itflow-theme-preview-box-muted {
|
||
background-color: var(--bs-secondary-bg);
|
||
}
|
||
.itflow-theme-preview-card {
|
||
display: block;
|
||
grid-column: 1 / -1;
|
||
background-color: var(--bs-body-bg);
|
||
border: 1px solid var(--bs-border-color);
|
||
border-radius: 2px;
|
||
}
|
||
.itflow-theme-name {
|
||
display: block;
|
||
margin-top: .4rem;
|
||
font-size: .8125rem;
|
||
text-transform: capitalize;
|
||
}
|
||
.btn-check:checked + .itflow-theme-swatch .itflow-theme-preview {
|
||
border-color: var(--bs-body-color);
|
||
box-shadow: 0 0 0 1px var(--bs-body-color);
|
||
}
|
||
.btn-check:checked + .itflow-theme-swatch .itflow-theme-name {
|
||
font-weight: 600;
|
||
}
|
||
.btn-check:focus-visible + .itflow-theme-swatch {
|
||
outline: 2px solid var(--itflow-accent, #007bff);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* --- Dark mode ------------------------------------------------------------
|
||
Bootstrap treats .bg-light / .text-dark / .bg-white as ABSOLUTE colours, so
|
||
none of them flip under [data-bs-theme=dark] - 69 bg-light and 425 text-dark
|
||
usages stayed bright. Overriding the utilities rather than the underlying
|
||
--bs-light-rgb / --bs-dark-rgb variables, because those same two variables
|
||
also drive .text-light and .bg-dark, which must NOT flip. */
|
||
[data-bs-theme=dark] .bg-light {
|
||
background-color: var(--bs-secondary-bg) !important;
|
||
}
|
||
[data-bs-theme=dark] .bg-white {
|
||
background-color: var(--bs-body-bg) !important;
|
||
}
|
||
[data-bs-theme=dark] .text-dark {
|
||
color: var(--bs-body-color) !important;
|
||
}
|
||
/* .table-light hardcodes #f8f9fa and #000 outright */
|
||
[data-bs-theme=dark] .table-light {
|
||
--bs-table-bg: var(--bs-secondary-bg);
|
||
--bs-table-color: var(--bs-body-color);
|
||
--bs-table-border-color: var(--bs-border-color);
|
||
--bs-table-striped-bg: var(--bs-tertiary-bg);
|
||
--bs-table-striped-color: var(--bs-body-color);
|
||
--bs-table-hover-bg: var(--bs-tertiary-bg);
|
||
--bs-table-hover-color: var(--bs-body-color);
|
||
}
|
||
|
||
/* Secondary and light buttons are fixed greys - #6c757d and #f8f9fa - so in
|
||
dark mode one goes muddy and the other glares. 260 btn-light and 185
|
||
btn-secondary uses, so these matter more than most. */
|
||
[data-bs-theme=dark] .btn-secondary {
|
||
--bs-btn-color: #dee2e6;
|
||
--bs-btn-bg: #3d4349;
|
||
--bs-btn-border-color: #495057;
|
||
--bs-btn-hover-color: #fff;
|
||
--bs-btn-hover-bg: #495057;
|
||
--bs-btn-hover-border-color: #565e64;
|
||
--bs-btn-active-color: #fff;
|
||
--bs-btn-active-bg: #565e64;
|
||
--bs-btn-active-border-color: #6c757d;
|
||
--bs-btn-disabled-color: #adb5bd;
|
||
--bs-btn-disabled-bg: #3d4349;
|
||
--bs-btn-disabled-border-color: #495057;
|
||
}
|
||
[data-bs-theme=dark] .btn-light {
|
||
--bs-btn-color: #dee2e6;
|
||
--bs-btn-bg: #343a40;
|
||
--bs-btn-border-color: #454d55;
|
||
--bs-btn-hover-color: #fff;
|
||
--bs-btn-hover-bg: #454d55;
|
||
--bs-btn-hover-border-color: #565e64;
|
||
--bs-btn-active-color: #fff;
|
||
--bs-btn-active-bg: #565e64;
|
||
--bs-btn-active-border-color: #6c757d;
|
||
--bs-btn-disabled-color: #6c757d;
|
||
--bs-btn-disabled-bg: #343a40;
|
||
--bs-btn-disabled-border-color: #454d55;
|
||
}
|
||
/* .btn-dark is #212529 - EXACTLY --bs-body-bg in dark mode, so the button was
|
||
the same colour as the page. Its main use is the search button welded to an
|
||
input group, so it needs to read as a control next to a .form-control rather
|
||
than invert to a bright button. Sits a step above the input, and above the
|
||
other two neutrals, mirroring its light-mode role as the strongest of them:
|
||
btn-light #343a40 < btn-secondary #3d4349 < btn-dark #495057 */
|
||
[data-bs-theme=dark] .btn-dark {
|
||
--bs-btn-color: #dee2e6;
|
||
--bs-btn-bg: #495057;
|
||
--bs-btn-border-color: #545b62;
|
||
--bs-btn-hover-color: #fff;
|
||
--bs-btn-hover-bg: #545b62;
|
||
--bs-btn-hover-border-color: #6c757d;
|
||
--bs-btn-active-color: #fff;
|
||
--bs-btn-active-bg: #6c757d;
|
||
--bs-btn-active-border-color: #7d858c;
|
||
--bs-btn-disabled-color: #adb5bd;
|
||
--bs-btn-disabled-bg: #495057;
|
||
--bs-btn-disabled-border-color: #545b62;
|
||
}
|
||
[data-bs-theme=dark] .btn-outline-dark {
|
||
--bs-btn-color: #dee2e6;
|
||
--bs-btn-border-color: #6c757d;
|
||
--bs-btn-hover-color: #fff;
|
||
--bs-btn-hover-bg: #495057;
|
||
--bs-btn-hover-border-color: #6c757d;
|
||
--bs-btn-active-color: #fff;
|
||
--bs-btn-active-bg: #545b62;
|
||
--bs-btn-active-border-color: #6c757d;
|
||
}
|
||
[data-bs-theme=dark] .btn-outline-secondary {
|
||
--bs-btn-color: #adb5bd;
|
||
--bs-btn-border-color: #6c757d;
|
||
--bs-btn-hover-color: #fff;
|
||
--bs-btn-hover-bg: #6c757d;
|
||
--bs-btn-hover-border-color: #6c757d;
|
||
}
|
||
/* .btn-default is my own AdminLTE 3 shim and hardcodes a light grey, so it
|
||
glares in dark mode like the Bootstrap greys above. */
|
||
[data-bs-theme=dark] .btn-default {
|
||
color: #dee2e6;
|
||
background-color: #343a40;
|
||
border-color: #454d55;
|
||
}
|
||
[data-bs-theme=dark] .btn-default:hover {
|
||
color: #fff;
|
||
background-color: #454d55;
|
||
border-color: #565e64;
|
||
}
|
||
|
||
/* --- TinyMCE chrome ------------------------------------------------------
|
||
TinyMCE's oxide skins ship their own palette - a blue-slate #222f3e with
|
||
#161f29 borders in dark - which sits next to the app's neutral #212529 and
|
||
#495057 and reads as a mismatch rather than a choice. The toolbar and
|
||
frame live in the main document (only the editing area is iframed), so
|
||
they can be driven from the same --bs-* variables as every other control,
|
||
which also means light and dark are handled by one set of rules.
|
||
|
||
The frame is matched to .form-control: same border, radius and focus ring. */
|
||
.tox.tox-tinymce {
|
||
border: var(--bs-border-width) solid var(--bs-border-color);
|
||
border-radius: var(--bs-border-radius);
|
||
}
|
||
.tox .tox-editor-header,
|
||
.tox .tox-toolbar,
|
||
.tox .tox-toolbar__primary,
|
||
.tox .tox-toolbar-overlord,
|
||
.tox .tox-menubar,
|
||
.tox .tox-statusbar {
|
||
background-color: var(--bs-tertiary-bg) !important;
|
||
border-color: var(--bs-border-color) !important;
|
||
}
|
||
.tox .tox-editor-header {
|
||
box-shadow: none;
|
||
}
|
||
.tox .tox-statusbar {
|
||
border-top: var(--bs-border-width) solid var(--bs-border-color);
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.tox .tox-edit-area__iframe {
|
||
background-color: var(--bs-body-bg) !important;
|
||
}
|
||
/* Toolbar buttons.
|
||
The skin gives every .tox-tbtn its own background:#222f3e, which sits as a
|
||
visible blue-slate box on the retinted toolbar - they need to be transparent
|
||
so the toolbar surface shows through, the way oxide looks in light mode.
|
||
|
||
Colour is set via `color`, never `fill`: the skin already declares
|
||
`.tox .tox-tbtn svg{fill:currentColor!important}`, so setting fill directly
|
||
fights its own model and, worse, overrides the forecolor swatch that is
|
||
supposed to show the currently selected text colour. */
|
||
.tox .tox-tbtn,
|
||
.tox .tox-mbtn,
|
||
.tox .tox-split-button {
|
||
background: transparent !important;
|
||
color: var(--bs-body-color) !important;
|
||
}
|
||
.tox .tox-tbtn:hover,
|
||
.tox .tox-mbtn:hover,
|
||
.tox .tox-tbtn:focus,
|
||
.tox .tox-split-button:hover {
|
||
background: var(--bs-secondary-bg) !important;
|
||
color: var(--bs-emphasis-color) !important;
|
||
}
|
||
.tox .tox-tbtn--enabled,
|
||
.tox .tox-tbtn--enabled:hover,
|
||
.tox .tox-tbtn.tox-tbtn--active {
|
||
background: var(--itflow-accent, #007bff) !important;
|
||
color: #fff !important;
|
||
}
|
||
/* light accents need a dark glyph - same L>0.335 cut as the buttons */
|
||
.theme-yellow .tox .tox-tbtn--enabled,
|
||
.theme-orange .tox .tox-tbtn--enabled,
|
||
.theme-teal .tox .tox-tbtn--enabled,
|
||
.theme-yellow .tox .tox-tbtn.tox-tbtn--active,
|
||
.theme-orange .tox .tox-tbtn.tox-tbtn--active,
|
||
.theme-teal .tox .tox-tbtn.tox-tbtn--active {
|
||
color: #000 !important;
|
||
}
|
||
.theme-yellow .tox .tox-mbtn--active,
|
||
.theme-orange .tox .tox-mbtn--active,
|
||
.theme-teal .tox .tox-mbtn--active {
|
||
color: #000 !important;
|
||
}
|
||
.theme-yellow .tox .tox-collection__item--active,
|
||
.theme-orange .tox .tox-collection__item--active,
|
||
.theme-teal .tox .tox-collection__item--active,
|
||
.theme-yellow .tox .tox-collection__item--enabled,
|
||
.theme-orange .tox .tox-collection__item--enabled,
|
||
.theme-teal .tox .tox-collection__item--enabled {
|
||
color: #000 !important;
|
||
}
|
||
/* Form controls that live IN the toolbar - the Styles listbox is the obvious
|
||
one - keep their own #2b3b4e fill and #161f29 border. Matched to .form-select
|
||
/ .form-control instead so they read as the same kind of control as the rest
|
||
of the app. */
|
||
.tox .tox-listboxfield .tox-listbox--select,
|
||
.tox .tox-textfield,
|
||
.tox .tox-toolbar-textfield,
|
||
.tox .tox-textarea {
|
||
background-color: var(--bs-body-bg) !important;
|
||
border-color: var(--bs-border-color) !important;
|
||
border-radius: var(--bs-border-radius) !important;
|
||
color: var(--bs-body-color) !important;
|
||
}
|
||
.tox .tox-listboxfield .tox-listbox--select:focus,
|
||
.tox .tox-textfield:focus,
|
||
.tox .tox-toolbar-textfield:focus,
|
||
.tox .tox-textarea:focus {
|
||
border-color: var(--itflow-accent, #007bff) !important;
|
||
box-shadow: 0 0 0 .25rem color-mix(in srgb, var(--itflow-accent, #007bff) 25%, transparent) !important;
|
||
}
|
||
/* the caret and label inside those controls */
|
||
.tox .tox-listboxfield .tox-listbox--select,
|
||
.tox .tox-tbtn__select-chevron {
|
||
color: var(--bs-body-color) !important;
|
||
}
|
||
.tox .tox-label,
|
||
.tox .tox-toolbar-label {
|
||
color: var(--bs-secondary-color) !important;
|
||
}
|
||
/* the Styles button face - 'styles' is in every ITFlow toolbar */
|
||
.tox .tox-tbtn--bespoke {
|
||
background: var(--bs-body-bg) !important;
|
||
border: var(--bs-border-width) solid var(--bs-border-color) !important;
|
||
border-radius: var(--bs-border-radius) !important;
|
||
}
|
||
.tox .tox-tbtn--bespoke:hover {
|
||
background: var(--bs-secondary-bg) !important;
|
||
}
|
||
.tox .tox-tbtn__select-label {
|
||
color: var(--bs-body-color) !important;
|
||
}
|
||
/* menubar active state was a fixed #599fef blue */
|
||
.tox .tox-mbtn--active,
|
||
.tox .tox-mbtn:not(:disabled).tox-mbtn--active:focus {
|
||
background: var(--itflow-accent, #007bff) !important;
|
||
color: #fff !important;
|
||
}
|
||
/* split buttons (forecolor, backcolor) - the focus fill was a fixed #222f3e */
|
||
.tox .tox-split-button:hover,
|
||
.tox .tox-split-button__main:focus,
|
||
.tox .tox-split-button__chevron:focus {
|
||
background: var(--bs-secondary-bg) !important;
|
||
color: var(--bs-body-color) !important;
|
||
}
|
||
/* disabled toolbar items */
|
||
.tox .tox-tbtn--disabled,
|
||
.tox .tox-tbtn:disabled {
|
||
color: var(--bs-secondary-color) !important;
|
||
}
|
||
|
||
/* focus ring, so the editor matches every other focused input */
|
||
.tox.tox-tinymce.tox-edit-focus,
|
||
.tox.tox-tinymce:focus-within {
|
||
border-color: var(--itflow-accent, #007bff);
|
||
box-shadow: 0 0 0 .25rem color-mix(in srgb, var(--itflow-accent, #007bff) 25%, transparent);
|
||
}
|
||
/* TinyMCE also draws its own focus box inside the editor - a fixed blue
|
||
(#006ce7) border on .tox-edit-area::before - so on any other theme a blue
|
||
box sat inside the theme-coloured ring. The ring above is the one every
|
||
input gets, so the inner box goes. */
|
||
.tox:not(.tox-tinymce-inline) .tox-edit-area::before {
|
||
display: none;
|
||
}
|
||
/* menus and dropdowns the toolbar opens */
|
||
.tox .tox-menu,
|
||
.tox .tox-collection--list .tox-collection__group {
|
||
background-color: var(--bs-body-bg) !important;
|
||
border-color: var(--bs-border-color) !important;
|
||
}
|
||
.tox .tox-collection__item {
|
||
color: var(--bs-body-color) !important;
|
||
}
|
||
.tox .tox-collection__item--active,
|
||
.tox .tox-collection__item--enabled {
|
||
background-color: var(--itflow-accent, #007bff) !important;
|
||
color: #fff !important;
|
||
}
|
||
|
||
/* --- Select picker - js/itflow_select.js ---------------------------------
|
||
Every <select class="select2">. The field is a .form-select, so height,
|
||
border, chevron, focus ring and dark mode all come from Bootstrap; this
|
||
adds what goes inside it and the panel that opens under it.
|
||
|
||
Before the script runs the browser paints the raw <select>. A single select
|
||
looks the same either way, but a multiple one renders as a four-row listbox
|
||
(~100px) that would collapse to a one-line field and yank the form up. So
|
||
it is held at field height with its text hidden until the picker takes
|
||
over (.itflow-select-native). If the script never runs, the delayed reveal
|
||
brings the plain control back after 3s so the form stays usable. */
|
||
select.select2[multiple]:not(.itflow-select-native) {
|
||
height: calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));
|
||
overflow-y: auto;
|
||
visibility: hidden;
|
||
animation: itflowRevealUninitSelect 1ms linear 3s forwards;
|
||
}
|
||
@keyframes itflowRevealUninitSelect {
|
||
to { visibility: visible; }
|
||
}
|
||
|
||
.itflow-select {
|
||
position: relative;
|
||
display: block;
|
||
width: 100%;
|
||
min-width: 0;
|
||
}
|
||
/* The real <select> stays in the form, under the field, for posting and for
|
||
the browser's "please select an item" bubble - just not seen or clicked */
|
||
.itflow-select > select.itflow-select-native {
|
||
position: absolute;
|
||
left: 0;
|
||
bottom: 0;
|
||
width: 100%;
|
||
height: 1px;
|
||
min-height: 0;
|
||
padding: 0;
|
||
border: 0;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
}
|
||
.itflow-select-control {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .375rem;
|
||
min-height: calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));
|
||
cursor: pointer;
|
||
user-select: none;
|
||
text-align: start;
|
||
}
|
||
.itflow-select-control:focus,
|
||
.itflow-select.is-open > .itflow-select-control {
|
||
border-color: var(--itflow-accent, #86b7fe);
|
||
outline: 0;
|
||
box-shadow: 0 0 0 .25rem color-mix(in srgb, var(--itflow-accent, #0d6efd) 25%, transparent);
|
||
}
|
||
.itflow-select.is-disabled > .itflow-select-control {
|
||
cursor: default;
|
||
color: var(--bs-secondary-color);
|
||
background-color: var(--bs-secondary-bg);
|
||
}
|
||
.itflow-select.is-invalid > .itflow-select-control,
|
||
.itflow-select.is-invalid-class > .itflow-select-control,
|
||
.was-validated .itflow-select:has(> select:invalid) > .itflow-select-control {
|
||
border-color: var(--bs-form-invalid-border-color);
|
||
}
|
||
.itflow-select-value {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .5rem;
|
||
min-width: 0;
|
||
flex: 1 1 auto;
|
||
}
|
||
.itflow-select-value .itflow-select-label {
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.itflow-select-placeholder,
|
||
.itflow-select-label.is-none {
|
||
color: var(--bs-secondary-color);
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
}
|
||
/* × to put the field back to its "nothing" option - only on optional fields */
|
||
.itflow-select.is-clearable > .itflow-select-control {
|
||
padding-right: 3.5rem;
|
||
}
|
||
.itflow-select-clear {
|
||
position: absolute;
|
||
top: 50%;
|
||
right: 2.25rem;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1.25rem;
|
||
height: 1.25rem;
|
||
padding: 0;
|
||
border: 0;
|
||
border-radius: 50%;
|
||
font-size: .7rem;
|
||
color: var(--bs-secondary-color);
|
||
background: transparent;
|
||
transform: translateY(-50%);
|
||
opacity: 0;
|
||
}
|
||
.itflow-select:hover .itflow-select-clear,
|
||
.itflow-select-control:focus .itflow-select-clear,
|
||
.itflow-select.is-open .itflow-select-clear {
|
||
opacity: 1;
|
||
}
|
||
.itflow-select-clear:hover {
|
||
color: var(--bs-body-color);
|
||
background: var(--bs-secondary-bg);
|
||
}
|
||
|
||
/* Multiple: chips in the field */
|
||
.itflow-select.is-multiple > .itflow-select-control {
|
||
flex-wrap: wrap;
|
||
gap: .25rem;
|
||
padding-top: .25rem;
|
||
padding-bottom: .25rem;
|
||
padding-left: .375rem;
|
||
}
|
||
.itflow-select.is-multiple .itflow-select-placeholder {
|
||
padding-left: .375rem;
|
||
}
|
||
.itflow-select-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: .3rem;
|
||
max-width: 100%;
|
||
height: 1.625rem;
|
||
padding: 0 .25rem 0 .55rem;
|
||
border: 1px solid var(--bs-border-color-translucent);
|
||
border-radius: 2rem;
|
||
font-size: .8125rem;
|
||
color: var(--bs-body-color);
|
||
background: var(--bs-tertiary-bg);
|
||
}
|
||
.itflow-select-chip.has-lead {
|
||
padding-left: .2rem;
|
||
}
|
||
.itflow-select-chip.is-more {
|
||
padding-right: .55rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-select-chip-label {
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.itflow-select-chip .itflow-select-avatar,
|
||
.itflow-select-chip .itflow-select-badge {
|
||
width: 1.25rem;
|
||
height: 1.25rem;
|
||
font-size: .55rem;
|
||
}
|
||
.itflow-select-chip .itflow-select-dot {
|
||
margin: 0 .15rem 0 .3rem;
|
||
}
|
||
.itflow-select-chip-remove {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1.125rem;
|
||
height: 1.125rem;
|
||
padding: 0;
|
||
border: 0;
|
||
border-radius: 50%;
|
||
font-size: .6rem;
|
||
color: var(--bs-secondary-color);
|
||
background: transparent;
|
||
}
|
||
.itflow-select-chip-remove:hover {
|
||
color: var(--bs-body-color);
|
||
background: var(--bs-secondary-bg);
|
||
}
|
||
|
||
/* In an .input-group the picker is the flexible middle piece */
|
||
.input-group > .itflow-select {
|
||
flex: 1 1 auto;
|
||
width: 1%;
|
||
}
|
||
.input-group > .itflow-select:focus-within,
|
||
.input-group > .itflow-select.is-open {
|
||
z-index: 5;
|
||
}
|
||
.input-group > .itflow-select:not(:first-child) > .itflow-select-control {
|
||
border-top-left-radius: 0;
|
||
border-bottom-left-radius: 0;
|
||
}
|
||
.input-group > .itflow-select:not(:last-child) > .itflow-select-control {
|
||
border-top-right-radius: 0;
|
||
border-bottom-right-radius: 0;
|
||
}
|
||
|
||
/* Pictures, tiles, dots and icons in front of a label */
|
||
.itflow-select-avatar {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1.5rem;
|
||
height: 1.5rem;
|
||
border-radius: 50%;
|
||
font-size: .6rem;
|
||
font-weight: 600;
|
||
letter-spacing: .02em;
|
||
object-fit: cover;
|
||
}
|
||
.itflow-select-avatar.is-tile {
|
||
border-radius: var(--bs-border-radius-sm);
|
||
}
|
||
.itflow-select-control .itflow-select-avatar,
|
||
.itflow-select-control .itflow-select-badge {
|
||
width: 1.375rem;
|
||
height: 1.375rem;
|
||
}
|
||
.itflow-select-dot {
|
||
flex: 0 0 auto;
|
||
width: .625rem;
|
||
height: .625rem;
|
||
margin: 0 .3125rem;
|
||
border-radius: 50%;
|
||
background-color: var(--itflow-select-color, var(--bs-secondary));
|
||
box-shadow: 0 0 0 1px rgba(0, 0, 0, .08);
|
||
}
|
||
.itflow-select-badge {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1.5rem;
|
||
height: 1.5rem;
|
||
border-radius: var(--bs-border-radius);
|
||
font-size: .65rem;
|
||
color: #fff;
|
||
background-color: var(--itflow-select-color, var(--bs-secondary));
|
||
}
|
||
.itflow-select-icon {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1.25rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
|
||
/* The panel */
|
||
.itflow-select-panel {
|
||
position: fixed;
|
||
z-index: 1075;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
font-size: .9375rem;
|
||
color: var(--bs-body-color);
|
||
background: var(--bs-body-bg);
|
||
border: 1px solid var(--bs-border-color-translucent);
|
||
border-radius: var(--bs-border-radius-lg);
|
||
box-shadow: 0 .75rem 2rem rgba(0, 0, 0, .14), 0 .125rem .375rem rgba(0, 0, 0, .06);
|
||
animation: itflowSelectIn .12s ease-out;
|
||
}
|
||
[data-bs-theme="dark"] .itflow-select-panel {
|
||
background: var(--bs-tertiary-bg);
|
||
box-shadow: 0 .75rem 2rem rgba(0, 0, 0, .5);
|
||
}
|
||
.itflow-select-panel.is-above {
|
||
animation-name: itflowSelectInAbove;
|
||
}
|
||
@keyframes itflowSelectIn {
|
||
from { opacity: 0; transform: translateY(-4px); }
|
||
}
|
||
@keyframes itflowSelectInAbove {
|
||
from { opacity: 0; transform: translateY(4px); }
|
||
}
|
||
.itflow-select-sheet-head {
|
||
display: none;
|
||
}
|
||
.itflow-select-search {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .5rem;
|
||
padding: .5rem .75rem;
|
||
border-bottom: 1px solid var(--bs-border-color-translucent);
|
||
color: var(--bs-secondary-color);
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-select-search[hidden] {
|
||
display: none;
|
||
}
|
||
.itflow-select-search input {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
padding: .125rem 0;
|
||
border: 0;
|
||
outline: 0;
|
||
font-size: .9375rem;
|
||
color: var(--bs-body-color);
|
||
background: transparent;
|
||
}
|
||
.itflow-select-search input::placeholder {
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-select-list {
|
||
max-height: 18rem;
|
||
padding: .25rem;
|
||
overflow-y: auto;
|
||
overscroll-behavior: contain;
|
||
}
|
||
.itflow-select-option {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .625rem;
|
||
min-height: 2.25rem;
|
||
padding: .375rem .625rem;
|
||
border-radius: var(--bs-border-radius);
|
||
cursor: pointer;
|
||
user-select: none;
|
||
}
|
||
.itflow-select-option.is-active {
|
||
background: var(--bs-secondary-bg);
|
||
}
|
||
.itflow-select-option.is-disabled {
|
||
cursor: default;
|
||
opacity: .5;
|
||
}
|
||
.itflow-select-option.is-none .itflow-select-label {
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-select-text {
|
||
display: flex;
|
||
flex: 1 1 auto;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
line-height: 1.3;
|
||
}
|
||
.itflow-select-text .itflow-select-label {
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.itflow-select-description {
|
||
font-size: .8125rem;
|
||
color: var(--bs-secondary-color);
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.itflow-select-option mark {
|
||
padding: 0;
|
||
color: inherit;
|
||
font-weight: 600;
|
||
background: color-mix(in srgb, var(--itflow-accent, #0d6efd) 16%, transparent);
|
||
border-radius: 2px;
|
||
}
|
||
.itflow-select-option.is-selected .itflow-select-label {
|
||
font-weight: 600;
|
||
}
|
||
.itflow-select-tick {
|
||
flex: 0 0 auto;
|
||
font-size: .8rem;
|
||
color: var(--itflow-accent, var(--bs-primary));
|
||
visibility: hidden;
|
||
}
|
||
.itflow-select-option.is-selected .itflow-select-tick {
|
||
visibility: visible;
|
||
}
|
||
/* Multiple: a checklist */
|
||
.itflow-select-checkbox {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1rem;
|
||
height: 1rem;
|
||
border: 1px solid var(--bs-border-color);
|
||
border-radius: .3rem;
|
||
font-size: .6rem;
|
||
color: transparent;
|
||
background: var(--bs-body-bg);
|
||
}
|
||
.itflow-select-option.is-selected .itflow-select-checkbox {
|
||
border-color: var(--itflow-accent, var(--bs-primary));
|
||
color: #fff;
|
||
background: var(--itflow-accent, var(--bs-primary));
|
||
}
|
||
.theme-yellow .itflow-select-option.is-selected .itflow-select-checkbox,
|
||
.theme-orange .itflow-select-option.is-selected .itflow-select-checkbox,
|
||
.theme-teal .itflow-select-option.is-selected .itflow-select-checkbox {
|
||
color: #000;
|
||
}
|
||
.itflow-select-create .itflow-select-icon {
|
||
color: var(--itflow-accent, var(--bs-primary));
|
||
}
|
||
.itflow-select-group {
|
||
padding: .5rem .625rem .25rem;
|
||
font-size: .75rem;
|
||
font-weight: 600;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-select-empty,
|
||
.itflow-select-more {
|
||
padding: .75rem .625rem;
|
||
font-size: .875rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-select-more {
|
||
padding-top: .375rem;
|
||
font-size: .8125rem;
|
||
}
|
||
.itflow-select-footer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: .25rem .375rem .25rem .875rem;
|
||
border-top: 1px solid var(--bs-border-color-translucent);
|
||
font-size: .8125rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-select-footer .btn,
|
||
.itflow-select-done {
|
||
--bs-btn-padding-y: .125rem;
|
||
text-decoration: none;
|
||
}
|
||
|
||
/* Phones: the panel comes up from the bottom of the screen */
|
||
.itflow-select-backdrop {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 1074;
|
||
background: rgba(0, 0, 0, .35);
|
||
animation: itflowSelectFade .15s ease-out;
|
||
}
|
||
.itflow-select-panel.is-sheet {
|
||
top: auto !important;
|
||
right: 0;
|
||
bottom: 0;
|
||
left: 0 !important;
|
||
width: auto !important;
|
||
max-height: 80vh;
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
border-width: 1px 0 0;
|
||
border-radius: 1rem 1rem 0 0;
|
||
font-size: 1rem;
|
||
animation: itflowSelectUp .2s ease-out;
|
||
}
|
||
.itflow-select-panel.is-sheet .itflow-select-sheet-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: .75rem .75rem .5rem 1rem;
|
||
font-weight: 600;
|
||
}
|
||
.itflow-select-panel.is-sheet .itflow-select-list {
|
||
flex: 1 1 auto;
|
||
max-height: none;
|
||
}
|
||
.itflow-select-panel.is-sheet .itflow-select-option {
|
||
min-height: 2.75rem;
|
||
}
|
||
.itflow-select-panel.is-sheet .itflow-select-search input {
|
||
font-size: 1rem;
|
||
}
|
||
@keyframes itflowSelectUp {
|
||
from { transform: translateY(100%); }
|
||
}
|
||
@keyframes itflowSelectFade {
|
||
from { opacity: 0; }
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.itflow-select-panel,
|
||
.itflow-select-backdrop {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
/* --- Muted contextual palette for dark mode ------------------------------
|
||
AdminLTE 3 swapped in softer variants of the contextual colours whenever
|
||
dark mode was on, so a success badge or a danger button did not glare off a
|
||
dark surface. Bootstrap 5 does not do this - it keeps one palette for both
|
||
modes. These are v3's own dark-mode values, taken from its
|
||
.dark-mode .card-<colour> > .card-header rules.
|
||
|
||
Overriding --bs-<colour> and its -rgb pair carries the change through every
|
||
consumer at once: .bg-*, .text-*, .text-bg-*, .border-*, badges and tables.
|
||
Buttons are handled separately below because .btn-* hardcodes its own hex
|
||
rather than reading the theme colour.
|
||
|
||
--bs-primary is deliberately left alone: btn-primary already follows
|
||
--itflow-accent, and moving --bs-primary would fight that. */
|
||
[data-bs-theme=dark] {
|
||
--bs-success: #00bc8c;
|
||
--bs-success-rgb: 0, 188, 140;
|
||
--bs-info: #3498db;
|
||
--bs-info-rgb: 52, 152, 219;
|
||
--bs-warning: #f39c12;
|
||
--bs-warning-rgb: 243, 156, 18;
|
||
--bs-danger: #e74c3c;
|
||
--bs-danger-rgb: 231, 76, 60;
|
||
}
|
||
/* .text-bg-* hardcodes its foreground, so the pairs that changed lightness
|
||
need re-stating. Same L>0.335 cut used everywhere else: the muted success
|
||
(L=0.379) and warning (L=0.429) take dark text, info and danger keep white. */
|
||
[data-bs-theme=dark] .text-bg-success {
|
||
color: #000;
|
||
}
|
||
[data-bs-theme=dark] .text-bg-info,
|
||
[data-bs-theme=dark] .text-bg-danger {
|
||
color: #fff;
|
||
}
|
||
|
||
/* Contextual table rows hardcode a pale tint plus black text, which is
|
||
unreadable on a dark table. ITFlow uses danger/warning/primary/secondary. */
|
||
[data-bs-theme=dark] .table-danger,
|
||
[data-bs-theme=dark] .table-warning,
|
||
[data-bs-theme=dark] .table-primary,
|
||
[data-bs-theme=dark] .table-secondary {
|
||
--bs-table-color: var(--bs-body-color);
|
||
--bs-table-striped-color: var(--bs-body-color);
|
||
--bs-table-active-color: var(--bs-body-color);
|
||
--bs-table-hover-color: var(--bs-body-color);
|
||
--bs-table-border-color: var(--bs-border-color);
|
||
}
|
||
[data-bs-theme=dark] .table-danger {
|
||
--bs-table-bg: rgba(231, 76, 60, .22);
|
||
}
|
||
[data-bs-theme=dark] .table-warning {
|
||
--bs-table-bg: rgba(243, 156, 18, .22);
|
||
}
|
||
[data-bs-theme=dark] .table-primary {
|
||
--bs-table-bg: rgba(63, 103, 145, .28);
|
||
}
|
||
[data-bs-theme=dark] .table-secondary {
|
||
--bs-table-bg: rgba(108, 117, 125, .25);
|
||
}
|
||
|
||
/* Buttons hardcode their own hex instead of reading --bs-<colour>. */
|
||
[data-bs-theme=dark] .btn-success {
|
||
--bs-btn-color: #000;
|
||
--bs-btn-bg: #00bc8c;
|
||
--bs-btn-border-color: #00bc8c;
|
||
--bs-btn-hover-color: #000;
|
||
--bs-btn-hover-bg: #26c69d;
|
||
--bs-btn-hover-border-color: #1ac295;
|
||
--bs-btn-active-color: #000;
|
||
--bs-btn-active-bg: #33cda3;
|
||
--bs-btn-active-border-color: #1ac295;
|
||
--bs-btn-disabled-color: #000;
|
||
--bs-btn-disabled-bg: #00bc8c;
|
||
--bs-btn-disabled-border-color: #00bc8c;
|
||
}
|
||
[data-bs-theme=dark] .btn-info {
|
||
--bs-btn-color: #fff;
|
||
--bs-btn-bg: #3498db;
|
||
--bs-btn-border-color: #3498db;
|
||
--bs-btn-hover-color: #fff;
|
||
--bs-btn-hover-bg: #2c81ba;
|
||
--bs-btn-hover-border-color: #2a7aaf;
|
||
--bs-btn-active-color: #fff;
|
||
--bs-btn-active-bg: #2a7aaf;
|
||
--bs-btn-active-border-color: #2772a4;
|
||
--bs-btn-disabled-color: #fff;
|
||
--bs-btn-disabled-bg: #3498db;
|
||
--bs-btn-disabled-border-color: #3498db;
|
||
}
|
||
[data-bs-theme=dark] .btn-warning {
|
||
--bs-btn-color: #000;
|
||
--bs-btn-bg: #f39c12;
|
||
--bs-btn-border-color: #f39c12;
|
||
--bs-btn-hover-color: #000;
|
||
--bs-btn-hover-bg: #f5ab36;
|
||
--bs-btn-hover-border-color: #f4a62a;
|
||
--bs-btn-active-color: #000;
|
||
--bs-btn-active-bg: #f5b041;
|
||
--bs-btn-active-border-color: #f4a62a;
|
||
--bs-btn-disabled-color: #000;
|
||
--bs-btn-disabled-bg: #f39c12;
|
||
--bs-btn-disabled-border-color: #f39c12;
|
||
}
|
||
[data-bs-theme=dark] .btn-danger {
|
||
--bs-btn-color: #fff;
|
||
--bs-btn-bg: #e74c3c;
|
||
--bs-btn-border-color: #e74c3c;
|
||
--bs-btn-hover-color: #fff;
|
||
--bs-btn-hover-bg: #c44133;
|
||
--bs-btn-hover-border-color: #b93d30;
|
||
--bs-btn-active-color: #fff;
|
||
--bs-btn-active-bg: #b93d30;
|
||
--bs-btn-active-border-color: #ae392d;
|
||
--bs-btn-disabled-color: #fff;
|
||
--bs-btn-disabled-bg: #e74c3c;
|
||
--bs-btn-disabled-border-color: #e74c3c;
|
||
}
|
||
/* outline variants read --bs-<colour>, so they follow automatically apart
|
||
from the foreground on hover */
|
||
[data-bs-theme=dark] .btn-outline-success:hover,
|
||
[data-bs-theme=dark] .btn-outline-warning:hover {
|
||
--bs-btn-hover-color: #000;
|
||
}
|
||
|
||
/* --- FullCalendar --------------------------------------------------------
|
||
v7's classic theme ships a dark palette keyed on [data-color-scheme=dark],
|
||
which nothing was setting - that attribute is now emitted next to
|
||
data-bs-theme in includes/header.php, so the built-in dark mode activates.
|
||
|
||
These then pull the palette onto the app's own colours, so the calendar
|
||
matches in BOTH modes rather than being a near-black (#030712) panel next to
|
||
a #212529 page, and so events and the toolbar follow the theme accent. */
|
||
#calendar {
|
||
/* Reserve the calendar's final height before it exists.
|
||
|
||
agent/calendar.php renders an empty <div id="calendar"></div> and
|
||
FullCalendar only fills it on DOMContentLoaded - which cannot happen
|
||
until every parser-blocking script has run, including the 777KB
|
||
fullcalendar.global.js that loads after the footer. Until then the div
|
||
is zero pixels tall, so the page paints with no calendar and then snaps
|
||
to full height, shoving everything below it down. That jump is the
|
||
flash.
|
||
|
||
90vh is not a guess: it is the exact `height: '90vh'` the calendar is
|
||
configured with in calendar.php, set outside the mobile branch so it
|
||
applies on both. Matching it here means the box is the right size from
|
||
first paint and nothing moves when the widget arrives. */
|
||
min-height: 90vh;
|
||
|
||
--fc-classic-background: var(--bs-body-bg);
|
||
--fc-classic-foreground: var(--bs-body-color);
|
||
--fc-classic-muted-foreground: var(--bs-secondary-color);
|
||
--fc-classic-border: var(--bs-border-color);
|
||
--fc-classic-strong-border: var(--bs-border-color);
|
||
|
||
/* events and the "primary" accent */
|
||
--fc-classic-primary: var(--itflow-accent, #007bff);
|
||
--fc-classic-event: var(--itflow-accent, #007bff);
|
||
|
||
/* Only the pressed/active toolbar button is themed. FullCalendar's resting
|
||
slate is the same in both its palettes, i.e. deliberate, and recolouring
|
||
it to a dark grey would look wrong in light mode. */
|
||
--fc-classic-button-strong: var(--itflow-accent, #007bff);
|
||
--fc-classic-button-strong-border: var(--itflow-accent, #007bff);
|
||
}
|
||
/* light accents need a dark foreground on events and the active view button */
|
||
.theme-yellow #calendar,
|
||
.theme-orange #calendar,
|
||
.theme-teal #calendar {
|
||
--fc-classic-primary-foreground: #000;
|
||
--fc-classic-event-contrast: #000;
|
||
--fc-classic-button-foreground: #000;
|
||
}
|
||
|
||
/* --- SweetAlert2 ----------------------------------------------------------
|
||
v11 keys its dark palette on [data-swal2-theme=dark], an attribute it sets
|
||
on its own container from the `theme` option - which defaults to "light",
|
||
so the confirm dialogs stayed white in dark mode. js/confirm_modal.js now
|
||
hands it the app's mode.
|
||
|
||
Same treatment as FullCalendar above: its dark theme is its own near-black
|
||
(#19191a) next to a #212529 page, so the two seed variables are repointed
|
||
onto the app's colours. Everything else in its dark block - text, input
|
||
background, validation message - derives from these two, so nothing further
|
||
needs restating here. */
|
||
[data-swal2-theme=dark] {
|
||
--swal2-dark-theme-black: var(--bs-body-bg);
|
||
--swal2-dark-theme-white: var(--bs-body-color);
|
||
|
||
/* The popup is borderless by default, which reads fine as a white panel on
|
||
the backdrop but leaves a #212529 one with no edge against it. */
|
||
--swal2-border: 1px solid var(--bs-border-color);
|
||
}
|
||
|
||
/* --- Close button focus ring ---------------------------------------------
|
||
Bootstrap styles .btn-close on plain :focus rather than :focus-visible, so
|
||
any programmatic or mouse-driven focus leaves a visible ring on it. Keyboard
|
||
users still get the ring; a modal that merely opened does not. */
|
||
.btn-close:focus:not(:focus-visible) {
|
||
box-shadow: none;
|
||
}
|
||
/* the modal container is focused on open and is not an interactive control,
|
||
so it should never draw a ring of its own */
|
||
.modal:focus {
|
||
outline: 0;
|
||
}
|
||
|
||
/* --- Primary colour: the two cases upstream cannot cover -------------------
|
||
`data-lte-primary` on <body> now drives buttons, links, pagination, pills,
|
||
dropdowns, list groups, progress, accordions and every form focus state
|
||
from AdminLTE's own palette. --itflow-accent is still ours and still drives
|
||
the navbar, sidebar, nav-tabs, TinyMCE, the select picker, FullCalendar and the
|
||
theme picker.
|
||
|
||
1. There is no `black` in AdminLTE's palette - its darkest neutral is
|
||
gray-dark (#343a40), which is a different colour - so the same variables
|
||
are supplied here. The generic [data-lte-primary] component rules then
|
||
pick these up exactly as they do for a built-in colour. */
|
||
.theme-black {
|
||
--bs-primary: #000000;
|
||
--bs-primary-rgb: 0, 0, 0;
|
||
--bs-link-color: #000000;
|
||
--bs-link-color-rgb: 0, 0, 0;
|
||
--bs-link-hover-color: #333333;
|
||
--bs-link-hover-color-rgb: 51, 51, 51;
|
||
--lte-primary-hover: #262626;
|
||
--lte-primary-hover-border: #1a1a1a;
|
||
--lte-primary-active: #1a1a1a;
|
||
--lte-primary-active-border: #000000;
|
||
}
|
||
|
||
/* 2. AdminLTE gives teal WHITE button text (its v3 YIQ rule). White on
|
||
#20c997 is a 2.13 contrast ratio, below the 4.5 AA threshold; black is
|
||
9.86. Overriding their own variable keeps the dark text we had. */
|
||
.theme-teal {
|
||
--lte-primary-color: #000000;
|
||
--lte-primary-color-rgb: 0, 0, 0;
|
||
}
|
||
|
||
/* --- Colour inputs ---------------------------------------------------------
|
||
Bootstrap sizes `.form-control-color` at width:3rem, but every colour input
|
||
in ITFlow sits inside an .input-group, and `.input-group > .form-control`
|
||
(0,2,0) sets `width: 1%; flex: 1 1 auto` - which outranks .form-control-color
|
||
(0,1,0) and collapses the swatch to a sliver. Matching that specificity is
|
||
the only way to win it back; the selector is strictly narrower than the one
|
||
it ties with, so nothing but a colour input is affected.
|
||
|
||
4rem rather than Bootstrap's 3rem: the swatch is the entire point of the
|
||
control, and .text-sm keeps the height at the 35px of its neighbours, so
|
||
there is only width available to read the colour by. */
|
||
.input-group > .form-control-color {
|
||
flex: 0 0 auto;
|
||
width: 4rem;
|
||
}
|
||
|
||
/* --- intl-tel-input --------------------------------------------------------
|
||
The library wraps the input in a .iti container, which inside an
|
||
.input-group becomes the flex item instead of the input. Bootstrap sizes its
|
||
children with `.input-group > .form-control { flex: 1 1 auto; width: 1% }`,
|
||
and that selector no longer matches the now-nested input - so without these
|
||
the wrapper collapses to its content width and the field sits detached from
|
||
the addon beside it.
|
||
|
||
The wrapper takes the sizing the input used to get, and the input inside it
|
||
is squared off on the left because .iti draws the country button there and
|
||
the addon draws the rounded edge. */
|
||
.input-group > .iti {
|
||
position: relative;
|
||
flex: 1 1 auto;
|
||
width: 1%;
|
||
min-width: 0;
|
||
}
|
||
.input-group > .iti > .form-control {
|
||
width: 100%;
|
||
}
|
||
.input-group > .iti:not(:last-child) > .form-control {
|
||
border-top-right-radius: 0;
|
||
border-bottom-right-radius: 0;
|
||
}
|
||
.input-group > .iti:not(:first-child) > .form-control,
|
||
.iti--separate-dial-code .form-control {
|
||
border-top-left-radius: 0;
|
||
border-bottom-left-radius: 0;
|
||
}
|
||
/* the country button is chrome, not a control - keep it on the form-control
|
||
palette so it does not read as a raised button in either mode */
|
||
.iti__selected-country {
|
||
background-color: var(--bs-tertiary-bg);
|
||
border: var(--bs-border-width) solid var(--bs-border-color);
|
||
border-right: 0;
|
||
border-radius: var(--bs-border-radius) 0 0 var(--bs-border-radius);
|
||
}
|
||
.iti__country-list,
|
||
.iti__dropdown-content {
|
||
background-color: var(--bs-body-bg);
|
||
color: var(--bs-body-color);
|
||
border: var(--bs-border-width) solid var(--bs-border-color);
|
||
}
|
||
.iti__country.iti__highlight,
|
||
.iti__country:hover {
|
||
background-color: var(--bs-secondary-bg);
|
||
}
|
||
.iti__dial-code {
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
|
||
/* --- Print ----------------------------------------------------------------
|
||
AdminLTE 3 bundled Bootstrap 4, whose print reboot shrank every printout by
|
||
declaring `@page { size: a3 }` and letting the browser scale that oversized
|
||
page down onto the real sheet - roughly 71% on A4, 73% on Letter. Bootstrap
|
||
5 dropped that block and AdminLTE 4 ships no page scaling of its own, so
|
||
pages come out at 1:1 and read about 1.4x larger than they used to.
|
||
AdminLTE 4.9.1 did add a real print block - it fixes the layout, the
|
||
chrome and the trailing blank page, but not the scale, so this stays.
|
||
|
||
Deliberately NOT restoring the @page rule: modern browsers treat a CSS page
|
||
size as a paper REQUEST rather than a scale hint, so it can hand you literal
|
||
A3 output instead of shrunken A4. Scaling the root font instead keeps the
|
||
page at whatever paper is actually selected and lets the layout reflow to
|
||
its real width, which suits the wide report tables better than shrinking the
|
||
whole page ever did.
|
||
|
||
Nothing else in the app sets a root font-size, and Bootstrap, AdminLTE and
|
||
the .text-sm wrapper all size in rem, so this one line is the only knob:
|
||
raise it for larger print, lower it for smaller. At 71% of a 16px default
|
||
the app's .text-sm body text lands at ~9.9px (~7.4pt), which is what v3's
|
||
page scaling was effectively producing. */
|
||
@media print {
|
||
html {
|
||
font-size: 71%;
|
||
}
|
||
|
||
/* AdminLTE 3 zeroed Bootstrap's card border (`border: 0 solid`) and drew
|
||
the card's edge with a box-shadow instead - and Bootstrap 4's print
|
||
reboot then killed box-shadows, so a printed card had no outline at all,
|
||
just the header's own bottom rule. AdminLTE 4 gives .card a real 1px
|
||
border, keeps the shadow, and its print block leaves cards alone, so
|
||
both still print.
|
||
|
||
border-WIDTH rather than the border shorthand, so .card-outline's 3px
|
||
accent keeps its colour and can be handed its width straight back - it
|
||
is a border-top longhand and survived v3's border:0 the same way. */
|
||
.card {
|
||
border-width: 0 !important;
|
||
}
|
||
.card.card-outline {
|
||
border-top-width: 3px !important;
|
||
}
|
||
.card-footer {
|
||
border-top-width: 0 !important;
|
||
}
|
||
|
||
/* Bootstrap 4 dropped every shadow when printing; they render as grey
|
||
smudges on paper. Same rule it used, which also covers the dashboard
|
||
small-boxes rather than cards alone. */
|
||
*,
|
||
*::before,
|
||
*::after {
|
||
box-shadow: none !important;
|
||
}
|
||
}
|
||
|
||
/* Keep the layout viewport a constant width.
|
||
|
||
This is what was making some navigations judder and others not. AdminLTE
|
||
sizes .app-wrapper, .app-header, .app-main and .app-footer against 100vw,
|
||
and 100vw INCLUDES the scrollbar, so the usable width is 100vw minus
|
||
whatever the scrollbar takes. Nothing reserves that space, so the vertical
|
||
scrollbar - and with it the layout width - appears and disappears purely on
|
||
whether a given page happens to be taller than the window.
|
||
|
||
For a view transition that is not cosmetic. ::view-transition-group(root) is
|
||
sized to the viewport, and the UA animates the group's width and height from
|
||
the outgoing value to the incoming one, stretching BOTH snapshots to fit for
|
||
the whole duration and snapping at the end. Navigate between a page that
|
||
scrolls and one that does not and every glyph on the page smears sideways by
|
||
the scrollbar width and then jumps back.
|
||
|
||
The client side nav is where this shows worst, because
|
||
agent/includes/inc_client_top_head.php renders #clientHeader expanded on
|
||
client_overview.php and collapsed everywhere else - so moving between
|
||
Overview and any sibling page flips the scrollbar on and off almost every
|
||
time.
|
||
|
||
scrollbar-gutter reserves the space permanently, so the width never moves.
|
||
The max-width correction has to come with it: with the gutter always
|
||
present, an unqualified 100vw now overruns the layout viewport on EVERY
|
||
page rather than only on tall ones, which would trade an intermittent
|
||
horizontal jump for a permanent horizontal scrollbar. 100% is the width of
|
||
the grid area these four already occupy, which is what the rule wanted.
|
||
AdminLTE made the same correction upstream in 4.8.3. */
|
||
html {
|
||
scrollbar-gutter: stable;
|
||
}
|
||
|
||
.app-wrapper,
|
||
.app-header,
|
||
.app-main,
|
||
.app-footer {
|
||
max-width: 100%;
|
||
}
|
||
|
||
/* AdminLTE puts a clearfix ::after on every card header, body and footer. On a
|
||
flex container that pseudo-element becomes an empty last flex item, and any
|
||
gap-* utility then adds a gap after it - so the last real item (the client
|
||
header's actions button, the overview's "View all" link) sits a gap short of
|
||
the padding. Floats don't apply inside flex anyway, so the clearfix only
|
||
ever does harm there. */
|
||
.card-header.d-flex::after,
|
||
.card-body.d-flex::after,
|
||
.card-footer.d-flex::after {
|
||
content: none;
|
||
}
|
||
|
||
/* Client header - agent/includes/inc_client_top_head.php
|
||
|
||
Initials, name and tags in one row; the name opens the panel underneath
|
||
(#clientHeader), which holds the same columns as the overview's header. Which way the chevron points is carried by the icon class
|
||
itself (fa-chevron-right / fa-chevron-down) - emitted by PHP for the first
|
||
paint, swapped by the include's own script after that. Not a CSS rotation,
|
||
so the direction cannot go stale behind a cached stylesheet. */
|
||
.itflow-client-head-main,
|
||
.itflow-client-head-toggle {
|
||
min-width: 0;
|
||
}
|
||
.itflow-client-head-toggle {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: .5rem;
|
||
color: var(--bs-body-color);
|
||
text-decoration: none;
|
||
}
|
||
.itflow-client-head-toggle:hover,
|
||
.itflow-client-head-toggle:focus-visible {
|
||
color: var(--bs-body-color);
|
||
}
|
||
/* Name with the client type under it, together no taller than the 2rem
|
||
initials tile beside them, so the row stays the same height */
|
||
.itflow-client-head-title {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
}
|
||
.itflow-client-head-name {
|
||
font-size: 1.125rem;
|
||
font-weight: 600;
|
||
line-height: 1.25rem;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.itflow-client-head-type {
|
||
font-size: .75rem;
|
||
line-height: .75rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.client-header-chevron {
|
||
font-size: .7rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.client-header-toggle:hover .client-header-chevron {
|
||
color: var(--bs-body-color);
|
||
}
|
||
/* Open tickets and balance due beside the menu */
|
||
.itflow-client-head-glance {
|
||
font-size: .875rem;
|
||
white-space: nowrap;
|
||
}
|
||
.itflow-client-head-glance a {
|
||
text-decoration: none;
|
||
}
|
||
/* Phone: a smaller name so most fit on one line beside the menu button, the
|
||
tags under it, then open tickets and balance due on their own line */
|
||
@media (max-width: 767.98px) {
|
||
.itflow-client-head-name {
|
||
font-size: 1rem;
|
||
}
|
||
.itflow-client-head-glance {
|
||
flex-basis: 100%;
|
||
}
|
||
}
|
||
|
||
/* Search palette - js/global_search.js
|
||
|
||
Opens over the page on Ctrl+K. Sits high rather than centred, so the
|
||
results grow downwards from the box instead of the box jumping as they
|
||
arrive. Everything takes its colour from the Bootstrap variables, so dark
|
||
mode needs nothing extra; the highlighted row uses the theme accent. */
|
||
.itflow-search .modal-dialog {
|
||
max-width: 44rem;
|
||
margin-top: 8vh;
|
||
}
|
||
.itflow-search .modal-content {
|
||
overflow: hidden;
|
||
border: 0;
|
||
box-shadow: 0 1rem 3rem rgba(0, 0, 0, .35);
|
||
}
|
||
.itflow-search-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .75rem;
|
||
padding: .75rem 1rem;
|
||
border-bottom: 1px solid var(--bs-border-color);
|
||
}
|
||
.itflow-search-icon,
|
||
.itflow-search-spinner {
|
||
flex: 0 0 auto;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-search-input {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
padding: .25rem 0;
|
||
border: 0;
|
||
outline: 0;
|
||
background: transparent;
|
||
color: var(--bs-body-color);
|
||
font-size: 1.15rem;
|
||
}
|
||
.itflow-search-input::placeholder {
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-search-results {
|
||
max-height: min(60vh, 34rem);
|
||
overflow-y: auto;
|
||
padding: .25rem 0 .5rem;
|
||
}
|
||
.itflow-search-group {
|
||
padding: .65rem 1rem .25rem;
|
||
font-size: .7rem;
|
||
font-weight: 600;
|
||
letter-spacing: .06em;
|
||
text-transform: uppercase;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-search-group .fa-fw {
|
||
margin-right: .35rem;
|
||
}
|
||
.itflow-search-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .75rem;
|
||
padding: .45rem 1rem .45rem calc(1rem - 3px);
|
||
border-left: 3px solid transparent;
|
||
color: var(--bs-body-color);
|
||
text-decoration: none;
|
||
cursor: pointer;
|
||
}
|
||
.itflow-search-item:hover,
|
||
.itflow-search-item:focus {
|
||
color: var(--bs-body-color);
|
||
text-decoration: none;
|
||
}
|
||
.itflow-search-item.active {
|
||
border-left-color: var(--itflow-accent, #007bff);
|
||
background-color: rgba(var(--bs-emphasis-color-rgb), .06);
|
||
}
|
||
.itflow-search-item-icon {
|
||
flex: 0 0 auto;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-search-item.active .itflow-search-item-icon {
|
||
color: var(--itflow-accent, #007bff);
|
||
}
|
||
.itflow-search-item-body {
|
||
display: flex;
|
||
flex: 1 1 auto;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
}
|
||
.itflow-search-item-title,
|
||
.itflow-search-item-sub,
|
||
.itflow-search-item-context {
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.itflow-search-item-sub,
|
||
.itflow-search-item-context {
|
||
font-size: .8rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-search-item-aside {
|
||
display: flex;
|
||
flex: 0 1 auto;
|
||
align-items: center;
|
||
gap: .5rem;
|
||
max-width: 40%;
|
||
}
|
||
.itflow-search-ref {
|
||
margin-right: .5rem;
|
||
font-family: var(--bs-font-monospace);
|
||
font-size: .8rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-search mark {
|
||
padding: 0;
|
||
background: transparent;
|
||
color: inherit;
|
||
font-weight: 700;
|
||
}
|
||
.itflow-search-more .itflow-search-item-title,
|
||
.itflow-search-all .itflow-search-item-title {
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-search-more.active .itflow-search-item-title,
|
||
.itflow-search-all.active .itflow-search-item-title {
|
||
color: var(--bs-body-color);
|
||
}
|
||
.itflow-search-all {
|
||
margin-top: .25rem;
|
||
border-top: 1px solid var(--bs-border-color-translucent);
|
||
padding-top: .6rem;
|
||
}
|
||
.itflow-search-empty,
|
||
.itflow-search-hint {
|
||
padding: 1.25rem 1rem;
|
||
text-align: center;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-search-empty .fa-fw {
|
||
font-size: 1.5rem;
|
||
margin-bottom: .5rem;
|
||
}
|
||
.itflow-search-hint {
|
||
padding: .75rem 1rem;
|
||
font-size: .85rem;
|
||
}
|
||
.itflow-search-footer {
|
||
gap: 1rem;
|
||
align-items: center;
|
||
padding: .45rem 1rem;
|
||
border-top: 1px solid var(--bs-border-color);
|
||
background-color: var(--bs-tertiary-bg);
|
||
font-size: .75rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-search kbd,
|
||
.navbar-search-kbd {
|
||
padding: .05rem .35rem;
|
||
border: 1px solid var(--bs-border-color);
|
||
border-radius: .25rem;
|
||
background-color: var(--bs-body-bg);
|
||
color: var(--bs-secondary-color);
|
||
font-family: inherit;
|
||
font-size: .7rem;
|
||
box-shadow: none;
|
||
}
|
||
@media (max-width: 575.98px) {
|
||
.itflow-search .modal-dialog {
|
||
margin-top: 0;
|
||
}
|
||
.itflow-search .modal-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.itflow-search-results {
|
||
flex: 1 1 auto;
|
||
max-height: none;
|
||
}
|
||
/* No room beside the title on a phone: client and status drop under it */
|
||
.itflow-search-item {
|
||
flex-wrap: wrap;
|
||
row-gap: .15rem;
|
||
}
|
||
.itflow-search-item-body {
|
||
flex: 1 1 0;
|
||
}
|
||
.itflow-search-item-aside {
|
||
order: 3;
|
||
max-width: none;
|
||
width: 100%;
|
||
padding-left: calc(1.25em + .75rem);
|
||
}
|
||
}
|
||
|
||
/* The Ctrl K hint inside the top bar search box. Pointer events off so a click on
|
||
it still lands on the box underneath and opens the palette. */
|
||
.navbar-search .navbar-search-field .navbar-search-kbd {
|
||
position: absolute;
|
||
top: 50%;
|
||
right: .6rem;
|
||
transform: translateY(-50%);
|
||
pointer-events: none;
|
||
background-color: transparent;
|
||
border-color: rgba(var(--bs-emphasis-color-rgb), .25);
|
||
}
|
||
.navbar-search .navbar-search-field .form-control {
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* Full search results - agent/global_search.php. Same bold-not-yellow match
|
||
marking as the palette, and the client column capped so a long client name
|
||
cannot push the title off the row. */
|
||
.global-search-group mark {
|
||
padding: 0;
|
||
background: transparent;
|
||
color: inherit;
|
||
font-weight: 700;
|
||
}
|
||
.global-search-context {
|
||
max-width: 14rem;
|
||
}
|
||
.global-search-group {
|
||
scroll-margin-top: 4rem;
|
||
}
|
||
|
||
/* Notifications - js/notifications.js, the bell panel and agent/notifications.php
|
||
|
||
One row design for both. The icon tile takes Bootstrap's subtle colour
|
||
utilities (bg-*-subtle / text-*-emphasis), so each tone already has a dark
|
||
mode version. Unread rows carry a dot and a heavier title; read rows step
|
||
back. The read / unread button shows on hover and keyboard focus, and
|
||
always on touch screens, where there is no hover. */
|
||
/* data-bs-theme on the panel (to escape the dark navbar) also puts Bootstrap's
|
||
default blue links back. Carry the page's themed link colours in instead -
|
||
resolved on body, where the theme colour and the page's mode both apply. */
|
||
body {
|
||
--itflow-link-color: var(--bs-link-color);
|
||
--itflow-link-color-rgb: var(--bs-link-color-rgb);
|
||
--itflow-link-hover-color: var(--bs-link-hover-color);
|
||
--itflow-link-hover-color-rgb: var(--bs-link-hover-color-rgb);
|
||
}
|
||
.itflow-notify-panel {
|
||
--bs-link-color: var(--itflow-link-color);
|
||
--bs-link-color-rgb: var(--itflow-link-color-rgb);
|
||
--bs-link-hover-color: var(--itflow-link-hover-color);
|
||
--bs-link-hover-color-rgb: var(--itflow-link-hover-color-rgb);
|
||
width: min(25rem, calc(100vw - 1rem));
|
||
overflow: hidden;
|
||
box-shadow: 0 .75rem 2rem rgba(0, 0, 0, .25);
|
||
}
|
||
.itflow-notify-head {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: .75rem 1rem .25rem;
|
||
}
|
||
.itflow-notify-tabs {
|
||
gap: 1rem;
|
||
padding: 0 1rem;
|
||
border-bottom: 1px solid var(--bs-border-color);
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-notify-tabs .nav-link {
|
||
padding: .4rem 0;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-notify-tabs .nav-link.active {
|
||
color: var(--bs-emphasis-color);
|
||
border-bottom-color: var(--itflow-accent, #007bff);
|
||
}
|
||
.itflow-notify-list {
|
||
max-height: min(65vh, 32rem);
|
||
overflow-y: auto;
|
||
}
|
||
.itflow-notify-group {
|
||
padding: .6rem 1rem .2rem;
|
||
font-size: .7rem;
|
||
font-weight: 600;
|
||
letter-spacing: .06em;
|
||
text-transform: uppercase;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-notify-item {
|
||
position: relative;
|
||
}
|
||
.itflow-notify-item .itflow-notify-link {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: .75rem;
|
||
padding: .6rem 2.75rem .6rem 1.75rem;
|
||
white-space: normal;
|
||
color: var(--bs-body-color);
|
||
text-decoration: none;
|
||
}
|
||
.itflow-notify-item .itflow-notify-link:hover,
|
||
.itflow-notify-item .itflow-notify-link:focus {
|
||
color: var(--bs-body-color);
|
||
background-color: rgba(var(--bs-emphasis-color-rgb), .05);
|
||
}
|
||
.itflow-notify-icon {
|
||
display: flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 2rem;
|
||
height: 2rem;
|
||
border-radius: 50%;
|
||
font-size: .85rem;
|
||
}
|
||
.itflow-notify-body {
|
||
display: flex;
|
||
flex: 1 1 0;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
}
|
||
.itflow-notify-top {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: .5rem;
|
||
}
|
||
.itflow-notify-type {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-notify-time {
|
||
flex: 0 0 auto;
|
||
font-size: .75rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-notify-text {
|
||
display: -webkit-box;
|
||
-webkit-box-orient: vertical;
|
||
-webkit-line-clamp: 2;
|
||
overflow: hidden;
|
||
font-size: .8125rem;
|
||
color: var(--bs-secondary-color);
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.itflow-notify-client {
|
||
margin-top: .15rem;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
font-size: .75rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-notify-client .fa-fw {
|
||
margin-right: .25rem;
|
||
opacity: .7;
|
||
}
|
||
.itflow-notify-item.is-unread .itflow-notify-type {
|
||
font-weight: 600;
|
||
color: var(--bs-emphasis-color);
|
||
}
|
||
.itflow-notify-item.is-unread::before {
|
||
content: "";
|
||
position: absolute;
|
||
top: 1.35rem;
|
||
left: .7rem;
|
||
width: .5rem;
|
||
height: .5rem;
|
||
border-radius: 50%;
|
||
background-color: var(--itflow-accent, #007bff);
|
||
}
|
||
.itflow-notify-item:not(.is-unread) .itflow-notify-icon {
|
||
opacity: .6;
|
||
}
|
||
.itflow-notify-toggle {
|
||
position: absolute;
|
||
top: .55rem;
|
||
right: .5rem;
|
||
width: 1.9rem;
|
||
height: 1.9rem;
|
||
padding: 0;
|
||
border: 0;
|
||
border-radius: 50%;
|
||
color: var(--bs-secondary-color);
|
||
opacity: 0;
|
||
}
|
||
.itflow-notify-toggle:hover,
|
||
.itflow-notify-toggle:focus-visible {
|
||
color: var(--bs-emphasis-color);
|
||
background-color: rgba(var(--bs-emphasis-color-rgb), .1);
|
||
}
|
||
.itflow-notify-item:hover .itflow-notify-toggle,
|
||
.itflow-notify-item:focus-within .itflow-notify-toggle {
|
||
opacity: 1;
|
||
}
|
||
@media (hover: none) {
|
||
.itflow-notify-toggle {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
/* A finger needs a bigger target than a pointer: 44px on touch screens, with
|
||
the row's text kept clear of it */
|
||
@media (pointer: coarse) {
|
||
.itflow-notify-toggle {
|
||
top: .3rem;
|
||
right: .25rem;
|
||
width: 2.75rem;
|
||
height: 2.75rem;
|
||
}
|
||
.itflow-notify-item .itflow-notify-link {
|
||
padding-right: 3.25rem;
|
||
}
|
||
}
|
||
.itflow-notify-empty {
|
||
padding: 2rem 1rem;
|
||
text-align: center;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-notify-foot {
|
||
display: block;
|
||
padding: .6rem 1rem;
|
||
border-top: 1px solid var(--bs-border-color);
|
||
font-size: .875rem;
|
||
text-align: center;
|
||
text-decoration: none;
|
||
}
|
||
.itflow-notify-foot:hover {
|
||
background-color: rgba(var(--bs-emphasis-color-rgb), .05);
|
||
}
|
||
|
||
/* The full page lists the same rows inside a card */
|
||
.itflow-notify-page .itflow-notify-item + .itflow-notify-item {
|
||
border-top: 1px solid var(--bs-border-color-translucent);
|
||
}
|
||
.itflow-notify-page .itflow-notify-link {
|
||
padding-top: .75rem;
|
||
padding-bottom: .75rem;
|
||
}
|
||
.itflow-notify-page .itflow-notify-text {
|
||
-webkit-line-clamp: 3;
|
||
}
|
||
.itflow-notify-page .itflow-notify-group {
|
||
background-color: var(--bs-tertiary-bg);
|
||
border-top: 1px solid var(--bs-border-color-translucent);
|
||
}
|
||
/* Bootstrap does not use Popper for dropdowns inside a navbar, so the panel is
|
||
pinned to the bell's right edge and a phone screen cuts off its left side.
|
||
On narrow screens it spans the width under the top bar instead. */
|
||
@media (max-width: 575.98px) {
|
||
.app-header .navbar-nav .itflow-notify-panel[data-bs-popper] {
|
||
position: fixed;
|
||
top: 3rem;
|
||
right: .5rem;
|
||
left: .5rem;
|
||
width: auto;
|
||
margin-top: .25rem;
|
||
}
|
||
}
|
||
|
||
/* Dashboard - agent/dashboard.php
|
||
|
||
Built on cards, list-groups and Bootstrap's subtle colour utilities; this is
|
||
only what those do not cover: the greeting and section headings, quieter
|
||
card headers, the "Needs attention" rail, the money strip, the priority dot
|
||
and chart heights. Every number on the page uses tabular figures so columns
|
||
of amounts line up without a monospace font. */
|
||
.itflow-dash {
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.itflow-dash-greeting {
|
||
font-size: 1.6rem;
|
||
font-weight: 600;
|
||
letter-spacing: -.01em;
|
||
}
|
||
.itflow-dash-section {
|
||
font-size: 1.125rem;
|
||
font-weight: 600;
|
||
}
|
||
.itflow-dash .card-header {
|
||
background: transparent;
|
||
border-bottom: 0;
|
||
padding-bottom: .25rem;
|
||
}
|
||
.itflow-dash .card-title {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
}
|
||
.itflow-dash .card > .list-group-flush {
|
||
border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
|
||
}
|
||
.itflow-dash .card-header + .list-group {
|
||
margin-top: .5rem;
|
||
}
|
||
/* Clickable rows read as normal text, not the greyed default */
|
||
.itflow-dash .list-group {
|
||
--bs-list-group-action-color: var(--bs-body-color);
|
||
}
|
||
|
||
/* Needs attention: one card split into cells, each with a severity bar on top.
|
||
The -1px margins tuck every cell's left/top hairline under the card edge, so
|
||
only the lines between cells show however the row wraps. */
|
||
.itflow-dash-rail {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
margin: -1px 0 0 -1px;
|
||
}
|
||
.itflow-dash-rail-item {
|
||
display: flex;
|
||
flex: 1 1 10rem;
|
||
flex-direction: column;
|
||
gap: .15rem;
|
||
min-width: 0;
|
||
padding: 1rem 1.1rem .9rem;
|
||
border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
|
||
border-left: var(--bs-border-width) solid var(--bs-border-color-translucent);
|
||
box-shadow: inset 0 3px 0 var(--itflow-dash-tone, var(--bs-secondary));
|
||
color: inherit;
|
||
text-decoration: none;
|
||
}
|
||
a.itflow-dash-rail-item:hover,
|
||
a.itflow-dash-rail-item:focus-visible {
|
||
background-color: var(--bs-tertiary-bg);
|
||
color: inherit;
|
||
}
|
||
.itflow-dash-rail-count {
|
||
font-size: 1.75rem;
|
||
font-weight: 600;
|
||
line-height: 1.1;
|
||
}
|
||
.itflow-dash-rail-label {
|
||
font-size: .875rem;
|
||
font-weight: 500;
|
||
}
|
||
.itflow-dash-rail-label .fas {
|
||
margin-right: .35rem;
|
||
}
|
||
.itflow-dash-rail-detail {
|
||
font-size: .8rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-dash-rail-clear {
|
||
flex-basis: 100%;
|
||
}
|
||
|
||
/* Finance: the period figures side by side, then "Right now" beside them */
|
||
.itflow-dash-money + .itflow-dash-money {
|
||
border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
|
||
}
|
||
.itflow-dash-money a:hover,
|
||
.itflow-dash-money a:focus-visible {
|
||
background-color: var(--bs-tertiary-bg);
|
||
}
|
||
.itflow-dash-money-value {
|
||
margin: .1rem 0 .15rem;
|
||
font-size: 1.5rem;
|
||
font-weight: 600;
|
||
line-height: 1.2;
|
||
}
|
||
.itflow-dash-now {
|
||
border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
|
||
background-color: var(--bs-tertiary-bg);
|
||
}
|
||
.itflow-dash-now .list-group {
|
||
--bs-list-group-bg: transparent;
|
||
}
|
||
@media (min-width: 768px) {
|
||
.itflow-dash-money + .itflow-dash-money {
|
||
border-top: 0;
|
||
border-left: var(--bs-border-width) solid var(--bs-border-color-translucent);
|
||
}
|
||
}
|
||
@media (min-width: 992px) {
|
||
.itflow-dash-now {
|
||
border-top: 0;
|
||
border-left: var(--bs-border-width) solid var(--bs-border-color-translucent);
|
||
}
|
||
}
|
||
|
||
/* Your schedule: day headings, then time | colour mark | what */
|
||
.itflow-dash-schedule {
|
||
border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
|
||
margin-top: .5rem;
|
||
padding-bottom: .4rem;
|
||
}
|
||
/* Beside the ticket list it takes that card's height and scrolls the rest;
|
||
stacked on smaller screens it stops at a sensible height */
|
||
@media (max-width: 1199.98px) {
|
||
.itflow-dash-schedule {
|
||
max-height: 26rem;
|
||
overflow-y: auto;
|
||
}
|
||
}
|
||
@media (min-width: 1200px) {
|
||
.itflow-dash-schedule {
|
||
flex: 1 1 0;
|
||
min-height: 14rem;
|
||
overflow-y: auto;
|
||
}
|
||
}
|
||
.itflow-dash-schedule-day {
|
||
padding: .6rem 1rem .2rem;
|
||
font-size: .8rem;
|
||
font-weight: 600;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-dash-schedule-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .75rem;
|
||
padding: .4rem 1rem;
|
||
color: var(--bs-body-color);
|
||
text-decoration: none;
|
||
}
|
||
.itflow-dash-schedule-item:hover,
|
||
.itflow-dash-schedule-item:focus-visible {
|
||
background-color: var(--bs-tertiary-bg);
|
||
color: var(--bs-body-color);
|
||
}
|
||
/* Earlier today - still listed, but stepped back */
|
||
.itflow-dash-schedule-item.is-past {
|
||
opacity: .55;
|
||
}
|
||
.itflow-dash-schedule-time {
|
||
flex: 0 0 4.25rem;
|
||
font-size: .8rem;
|
||
color: var(--bs-secondary-color);
|
||
white-space: nowrap;
|
||
}
|
||
.itflow-dash-schedule-mark {
|
||
flex: 0 0 auto;
|
||
align-self: stretch;
|
||
width: 3px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
/* Ranked rows with a bar underneath: open tickets by tech, top clients */
|
||
.itflow-dash-bars {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: .35rem;
|
||
margin-inline: -.5rem;
|
||
}
|
||
.itflow-dash-bar {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
column-gap: .75rem;
|
||
row-gap: .3rem;
|
||
padding: .35rem .5rem;
|
||
border-radius: var(--bs-border-radius-sm);
|
||
color: var(--bs-body-color);
|
||
text-decoration: none;
|
||
}
|
||
.itflow-dash-bar:hover,
|
||
.itflow-dash-bar:focus-visible {
|
||
background-color: var(--bs-tertiary-bg);
|
||
color: var(--bs-body-color);
|
||
}
|
||
.itflow-dash-bar-label {
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.itflow-dash-bar-value {
|
||
font-weight: 600;
|
||
white-space: nowrap;
|
||
}
|
||
.itflow-dash-bar-track {
|
||
display: flex;
|
||
grid-column: 1 / -1;
|
||
height: .375rem;
|
||
overflow: hidden;
|
||
border-radius: 1rem;
|
||
background-color: var(--bs-secondary-bg);
|
||
}
|
||
.itflow-dash-bar-fill {
|
||
height: 100%;
|
||
background-color: var(--itflow-accent);
|
||
}
|
||
|
||
/* Receivables aging: one bar split by lateness, green through to deep red */
|
||
.itflow-dash-aging {
|
||
display: flex;
|
||
gap: 2px;
|
||
height: .75rem;
|
||
overflow: hidden;
|
||
border-radius: 1rem;
|
||
background-color: var(--bs-secondary-bg);
|
||
}
|
||
.itflow-dash-age-current {
|
||
background-color: var(--bs-success);
|
||
}
|
||
.itflow-dash-age-1-30 {
|
||
background-color: var(--bs-warning);
|
||
}
|
||
.itflow-dash-age-31-60 {
|
||
background-color: var(--bs-orange);
|
||
}
|
||
.itflow-dash-age-61-90 {
|
||
background-color: var(--bs-danger);
|
||
}
|
||
.itflow-dash-age-90plus {
|
||
background-color: #842029;
|
||
}
|
||
[data-bs-theme=dark] .itflow-dash-age-90plus {
|
||
background-color: #a52834;
|
||
}
|
||
|
||
.itflow-dash-dot {
|
||
flex: 0 0 auto;
|
||
width: .55rem;
|
||
height: .55rem;
|
||
border-radius: 50%;
|
||
}
|
||
/* Urgent is "dark" everywhere else in the ticket lists - invisible on a dark page */
|
||
[data-bs-theme=dark] .itflow-dash-dot.bg-dark {
|
||
background-color: var(--bs-emphasis-color) !important;
|
||
}
|
||
.itflow-dash-chart {
|
||
position: relative;
|
||
height: 240px;
|
||
}
|
||
.itflow-dash-chart-donut {
|
||
height: 180px;
|
||
}
|
||
.itflow-dash-donut-total {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
line-height: 1.25;
|
||
pointer-events: none;
|
||
}
|
||
.itflow-dash-donut-total [data-donut-total] {
|
||
font-size: 1.2rem;
|
||
}
|
||
.itflow-dash-legend {
|
||
margin-inline: -.4rem;
|
||
font-size: .85rem;
|
||
}
|
||
.itflow-dash-legend li {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .5rem;
|
||
padding: .2rem .4rem;
|
||
border-radius: var(--bs-border-radius-sm);
|
||
}
|
||
.itflow-dash-legend li:hover {
|
||
background-color: var(--bs-tertiary-bg);
|
||
}
|
||
.itflow-dash-legend-swatch {
|
||
flex: 0 0 auto;
|
||
width: .6rem;
|
||
height: .6rem;
|
||
border-radius: 50%;
|
||
}
|
||
.itflow-dash-legend-label {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.itflow-dash-legend-value {
|
||
white-space: nowrap;
|
||
}
|
||
.itflow-dash-legend-share {
|
||
flex: 0 0 2.5rem;
|
||
text-align: right;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-dash-chart-lg {
|
||
height: 300px;
|
||
}
|
||
.itflow-dash-chart-sm {
|
||
min-height: 150px;
|
||
}
|
||
|
||
/* Clients list - agent/clients.php
|
||
|
||
Rows are a table with Bootstrap's own utilities; this is only the initials
|
||
tile beside each name (also used at the top of the client side nav), the
|
||
small tag pills under it, and a tint on rows ticked for a bulk action. */
|
||
.itflow-client-avatar {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 2.5rem;
|
||
height: 2.5rem;
|
||
border-radius: var(--bs-border-radius-lg);
|
||
font-size: .8rem;
|
||
font-weight: 600;
|
||
letter-spacing: .02em;
|
||
}
|
||
/* Tags - functions/tags.php tagPills(), on every list and detail page that has
|
||
them. A pill opens the list filtered to that tag; the dashed "+" at the end
|
||
opens the tag picker (js/tag_picker.js). The group sits above stretched-link
|
||
row and cell links so the pills stay clickable on top of them. */
|
||
.itflow-tags {
|
||
position: relative;
|
||
z-index: 2;
|
||
display: inline-flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: .25rem;
|
||
max-width: 100%;
|
||
vertical-align: middle;
|
||
}
|
||
.itflow-tag {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: .2rem;
|
||
padding: .05rem .45rem;
|
||
border: 1px solid transparent;
|
||
border-radius: 1rem;
|
||
font-size: .75rem;
|
||
font-weight: normal;
|
||
line-height: 1.4;
|
||
color: #fff;
|
||
background-color: var(--itflow-tag-color, var(--bs-secondary));
|
||
text-decoration: none;
|
||
white-space: nowrap;
|
||
}
|
||
.itflow-tag:hover,
|
||
.itflow-tag:focus-visible {
|
||
color: #fff;
|
||
filter: brightness(1.1);
|
||
}
|
||
.itflow-tag-add {
|
||
color: var(--bs-secondary-color);
|
||
background-color: transparent;
|
||
border-color: var(--bs-border-color);
|
||
border-style: dashed;
|
||
cursor: pointer;
|
||
}
|
||
.itflow-tag-add:hover,
|
||
.itflow-tag-add:focus-visible,
|
||
.itflow-tag-add[aria-expanded="true"] {
|
||
color: var(--itflow-accent, var(--bs-primary));
|
||
border-color: currentColor;
|
||
filter: none;
|
||
}
|
||
/* The picker - the select picker's panel, plus the bulk note and the dash for
|
||
a tag only some of the selected records have */
|
||
.itflow-tag-panel-note {
|
||
padding: .5rem .75rem;
|
||
border-bottom: 1px solid var(--bs-border-color-translucent);
|
||
font-size: .8125rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-tag-panel .itflow-select-footer {
|
||
gap: .25rem;
|
||
padding-top: .375rem;
|
||
padding-bottom: .375rem;
|
||
}
|
||
.itflow-tag-done {
|
||
--bs-btn-padding-x: .75rem;
|
||
}
|
||
/* On a phone the sheet's own header has the Done */
|
||
.itflow-select-panel.is-sheet .itflow-tag-done {
|
||
display: none;
|
||
}
|
||
.itflow-select-option.is-mixed .itflow-select-checkbox {
|
||
border-color: var(--itflow-accent, var(--bs-primary));
|
||
color: var(--itflow-accent, var(--bs-primary));
|
||
}
|
||
.itflow-clients-street {
|
||
max-width: 16rem;
|
||
}
|
||
/* A row ticked for a bulk action takes a subtle theme tint - every list with
|
||
bulk checkboxes (clients, assets, contacts, tickets...), not just clients */
|
||
.table > tbody > tr:has(.bulk-select:checked) > * {
|
||
--bs-table-bg-state: var(--bs-primary-bg-subtle);
|
||
}
|
||
|
||
/* Quick pickers - js/quick_pick.js. A ticket's priority, assignee or billing
|
||
badge is a button that opens a small menu under it; the menu itself is the
|
||
select picker's panel. On the ticket page the button also carries
|
||
.ticket-prop-edit, so it looks like the other properties there. */
|
||
.itflow-quick-pick {
|
||
padding: 0;
|
||
border: 0;
|
||
border-radius: 2rem;
|
||
font: inherit;
|
||
color: inherit;
|
||
background: none;
|
||
cursor: pointer;
|
||
transition: opacity .15s;
|
||
}
|
||
.itflow-quick-pick-list:hover .badge,
|
||
.itflow-quick-pick-list:focus-visible .badge,
|
||
.itflow-quick-pick-list[aria-expanded="true"] .badge {
|
||
box-shadow: 0 0 0 .2rem color-mix(in srgb, var(--itflow-accent, #0d6efd) 30%, transparent);
|
||
}
|
||
.itflow-quick-pick:focus-visible {
|
||
outline: 0;
|
||
}
|
||
.itflow-quick-pick-detail {
|
||
border-radius: var(--bs-border-radius);
|
||
}
|
||
.itflow-quick-pick.is-saving {
|
||
opacity: .5;
|
||
pointer-events: none;
|
||
}
|
||
/* A brief ring on the value that just changed */
|
||
.itflow-quick-pick.is-saved .badge {
|
||
animation: itflowQuickSaved .9s ease-out;
|
||
}
|
||
@keyframes itflowQuickSaved {
|
||
from { box-shadow: 0 0 0 .3rem color-mix(in srgb, var(--bs-success) 45%, transparent); }
|
||
to { box-shadow: 0 0 0 .3rem transparent; }
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.itflow-quick-pick.is-saved .badge {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
/* Client side nav - agent/includes/client_side_nav.php: the client's initials
|
||
and name at the top, under the "All clients" link */
|
||
/* Same width, corners and left padding as the menu links below it, so the
|
||
tile lines up with their icons; sits right under the "All clients" bar */
|
||
.itflow-client-nav-identity {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .6rem;
|
||
margin: 0;
|
||
padding: .35rem .8rem;
|
||
border: 1px solid rgba(255, 255, 255, .08);
|
||
border-radius: var(--bs-border-radius);
|
||
background-color: rgba(255, 255, 255, .05);
|
||
}
|
||
.itflow-client-avatar-sm {
|
||
width: 2rem;
|
||
height: 2rem;
|
||
border-radius: var(--bs-border-radius);
|
||
font-size: .7rem;
|
||
}
|
||
.itflow-client-nav-identity-label {
|
||
font-size: .65rem;
|
||
font-weight: 600;
|
||
letter-spacing: .05em;
|
||
line-height: 1.2;
|
||
text-transform: uppercase;
|
||
color: rgba(255, 255, 255, .55);
|
||
}
|
||
/* The card is the client switcher's button (js/client_switcher.js) */
|
||
button.itflow-client-nav-identity {
|
||
width: 100%;
|
||
text-align: left;
|
||
color: inherit;
|
||
font: inherit;
|
||
cursor: pointer;
|
||
}
|
||
button.itflow-client-nav-identity:hover,
|
||
button.itflow-client-nav-identity[aria-expanded="true"] {
|
||
background-color: rgba(255, 255, 255, .1);
|
||
}
|
||
button.itflow-client-nav-identity:focus-visible {
|
||
outline: 2px solid var(--itflow-accent, var(--bs-primary));
|
||
outline-offset: 1px;
|
||
}
|
||
.itflow-client-switcher-caret {
|
||
flex: 0 0 auto;
|
||
margin-left: auto;
|
||
font-size: .75rem;
|
||
color: rgba(255, 255, 255, .45);
|
||
}
|
||
.itflow-client-switcher {
|
||
margin-bottom: .5rem;
|
||
}
|
||
.itflow-client-switcher-menu {
|
||
width: 100%;
|
||
margin-top: .25rem !important;
|
||
padding: .5rem 0 .25rem;
|
||
}
|
||
.itflow-client-switcher-list {
|
||
max-height: min(22rem, 60vh);
|
||
overflow-y: auto;
|
||
}
|
||
.itflow-client-switcher-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .5rem;
|
||
padding-top: .3rem;
|
||
padding-bottom: .3rem;
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-client-avatar-xs {
|
||
width: 1.5rem;
|
||
height: 1.5rem;
|
||
border-radius: var(--bs-border-radius-sm);
|
||
font-size: .6rem;
|
||
}
|
||
.itflow-client-switcher-empty {
|
||
padding: .4rem 1rem;
|
||
font-size: .8rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
/* Long names get two lines, then an ellipsis - the full name is in the tooltip */
|
||
.itflow-client-nav-identity-name {
|
||
display: -webkit-box;
|
||
-webkit-box-orient: vertical;
|
||
-webkit-line-clamp: 2;
|
||
overflow: hidden;
|
||
font-size: .875rem;
|
||
font-weight: 600;
|
||
line-height: 1.25;
|
||
color: #fff;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
|
||
/* Client overview - agent/client_overview.php
|
||
|
||
Reuses the dashboard's pieces (quiet card headers, the attention rail, the
|
||
schedule list, money figures); this adds the large initials tile, the small
|
||
labels in the header, contact avatars, the notes box and the money strip. */
|
||
.itflow-client-avatar-lg {
|
||
width: 3.5rem;
|
||
height: 3.5rem;
|
||
border-radius: var(--bs-border-radius-lg);
|
||
font-size: 1.1rem;
|
||
}
|
||
/* Phone: the name stays beside the initials and the buttons drop under them */
|
||
@media (max-width: 767.98px) {
|
||
.itflow-ov-hero-name {
|
||
flex: 1 1 0;
|
||
}
|
||
.itflow-ov-hero-actions {
|
||
flex-basis: 100%;
|
||
}
|
||
}
|
||
.itflow-ov-label {
|
||
margin-bottom: .25rem;
|
||
font-size: .8rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-ov-notes {
|
||
min-height: 12rem;
|
||
resize: vertical;
|
||
}
|
||
/* The money strip is the attention rail's layout without the coloured bar */
|
||
.itflow-ov-stats .itflow-dash-rail-item {
|
||
--itflow-dash-tone: transparent;
|
||
flex-basis: 12rem;
|
||
}
|
||
|
||
/* Racks - agent/racks.php, agent/rack.php, agent/includes/rack_elevation.php
|
||
|
||
A rack is drawn as a cabinet: dark frame, numbered rails either side and a
|
||
bay with one CSS grid row per U. Items are placed by grid-row (PHP works out
|
||
the row from the U and which way the rack is numbered), empty U are buttons
|
||
that open "add here". The frame stays dark in both themes - it is a picture
|
||
of a rack, not page chrome - so its colours are fixed rather than taken from
|
||
the Bootstrap variables.
|
||
|
||
The row height shrinks for tall racks so a 42U rack still fits a screen. */
|
||
.min-w-0 {
|
||
min-width: 0;
|
||
}
|
||
.itflow-rack-pair {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
gap: 1.5rem;
|
||
}
|
||
.itflow-rack {
|
||
--rack-u: clamp(1.3rem, calc(60rem / var(--rack-rows)), 1.75rem);
|
||
flex: 1 1 19rem;
|
||
max-width: 30rem;
|
||
}
|
||
.itflow-rack-cap {
|
||
margin-bottom: .35rem;
|
||
font-size: .7rem;
|
||
font-weight: 600;
|
||
letter-spacing: .08em;
|
||
text-align: center;
|
||
text-transform: uppercase;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-rack-frame {
|
||
display: flex;
|
||
padding: .5rem .3rem;
|
||
border-radius: .75rem;
|
||
background: linear-gradient(180deg, #2b3035, #212529);
|
||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .06), 0 .25rem .75rem rgba(0, 0, 0, .18);
|
||
}
|
||
.itflow-rack-rail {
|
||
display: grid;
|
||
flex: 0 0 1.75rem;
|
||
grid-template-rows: repeat(var(--rack-rows), var(--rack-u));
|
||
}
|
||
.itflow-rack-rail span {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-family: var(--bs-font-monospace);
|
||
font-size: .62rem;
|
||
color: rgba(255, 255, 255, .42);
|
||
}
|
||
.itflow-rack-bay {
|
||
position: relative;
|
||
display: grid;
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
grid-template-columns: minmax(0, 1fr);
|
||
grid-template-rows: repeat(var(--rack-rows), var(--rack-u));
|
||
border-right: 2px solid #3a4148;
|
||
border-left: 2px solid #3a4148;
|
||
background-color: #15181b;
|
||
background-image: repeating-linear-gradient(to bottom, transparent 0, transparent calc(var(--rack-u) - 1px), rgba(255, 255, 255, .05) calc(var(--rack-u) - 1px), rgba(255, 255, 255, .05) var(--rack-u));
|
||
}
|
||
.itflow-rack-slot {
|
||
grid-column: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin: 1px 2px;
|
||
padding: 0;
|
||
border: 1px dashed transparent;
|
||
border-radius: .3rem;
|
||
background: transparent;
|
||
font-size: .7rem;
|
||
color: rgba(255, 255, 255, .85);
|
||
}
|
||
.itflow-rack-slot span {
|
||
opacity: 0;
|
||
transition: opacity .12s ease-in-out;
|
||
}
|
||
button.itflow-rack-slot:hover,
|
||
button.itflow-rack-slot:focus-visible {
|
||
border-color: rgba(255, 255, 255, .35);
|
||
background-color: rgba(255, 255, 255, .07);
|
||
outline: 0;
|
||
}
|
||
button.itflow-rack-slot:hover span,
|
||
button.itflow-rack-slot:focus-visible span {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Items */
|
||
.itflow-rack-item {
|
||
position: relative;
|
||
grid-column: 1;
|
||
margin: 1px 2px;
|
||
overflow: hidden;
|
||
border-radius: .3rem;
|
||
color: #fff;
|
||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14), inset 0 -1px 0 rgba(0, 0, 0, .25), 0 1px 2px rgba(0, 0, 0, .45);
|
||
}
|
||
.itflow-rack-item-body {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .5rem;
|
||
width: 100%;
|
||
height: 100%;
|
||
padding: 0 .5rem;
|
||
border: 0;
|
||
background: transparent;
|
||
font: inherit;
|
||
text-align: left;
|
||
color: inherit;
|
||
text-decoration: none;
|
||
}
|
||
a.itflow-rack-item-body:hover,
|
||
button.itflow-rack-item-body:hover {
|
||
color: inherit;
|
||
background-color: rgba(255, 255, 255, .08);
|
||
}
|
||
.itflow-rack-item-body:focus-visible {
|
||
outline: 2px solid #fff;
|
||
outline-offset: -2px;
|
||
}
|
||
.itflow-rack-item-icon {
|
||
flex: 0 0 auto;
|
||
font-size: .8rem;
|
||
opacity: .9;
|
||
}
|
||
.itflow-rack-item-text {
|
||
display: flex;
|
||
flex: 1 1 auto;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
line-height: 1.15;
|
||
}
|
||
.itflow-rack-item-name,
|
||
.itflow-rack-item-meta {
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.itflow-rack-item-name {
|
||
font-size: .78rem;
|
||
font-weight: 600;
|
||
}
|
||
.itflow-rack-item-meta {
|
||
font-size: .68rem;
|
||
opacity: .78;
|
||
}
|
||
/* One U is one line: the name, then the quieter text beside it - which gives
|
||
way first when the line is short */
|
||
.itflow-rack-h1 .itflow-rack-item-text {
|
||
flex-direction: row;
|
||
align-items: baseline;
|
||
gap: .5rem;
|
||
}
|
||
.itflow-rack-h1 .itflow-rack-item-name {
|
||
flex: 0 1 auto;
|
||
max-width: 75%;
|
||
}
|
||
.itflow-rack-h1 .itflow-rack-item-meta {
|
||
flex: 1 1 0;
|
||
min-width: 0;
|
||
}
|
||
.itflow-rack-item-pos {
|
||
flex: 0 0 auto;
|
||
margin-left: auto;
|
||
font-family: var(--bs-font-monospace);
|
||
font-size: .62rem;
|
||
opacity: .6;
|
||
}
|
||
.itflow-rack-item-edit {
|
||
position: absolute;
|
||
top: 50%;
|
||
right: .3rem;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1.35rem;
|
||
height: 1.2rem;
|
||
margin-top: -.6rem;
|
||
padding: 0;
|
||
border: 0;
|
||
border-radius: .25rem;
|
||
background-color: rgba(0, 0, 0, .35);
|
||
font-size: .6rem;
|
||
color: #fff;
|
||
opacity: 0;
|
||
transition: opacity .12s ease-in-out;
|
||
}
|
||
.itflow-rack-item:hover .itflow-rack-item-edit,
|
||
.itflow-rack-item:focus-within .itflow-rack-item-edit {
|
||
opacity: 1;
|
||
}
|
||
.itflow-rack-item:has(.itflow-rack-item-edit):hover .itflow-rack-item-pos {
|
||
visibility: hidden;
|
||
}
|
||
.itflow-rack-item-edit:hover {
|
||
background-color: rgba(0, 0, 0, .55);
|
||
}
|
||
.itflow-rack-item.is-archived {
|
||
opacity: .55;
|
||
}
|
||
.itflow-rack-item.is-archived .itflow-rack-item-name {
|
||
text-decoration: line-through;
|
||
}
|
||
/* A full-depth item seen from its other side: it fills this face too */
|
||
.itflow-rack-item.is-ghost {
|
||
border: 1px dashed rgba(255, 255, 255, .16);
|
||
background: repeating-linear-gradient(45deg, rgba(255, 255, 255, .06) 0, rgba(255, 255, 255, .06) 5px, transparent 5px, transparent 10px), #1c2024;
|
||
color: rgba(255, 255, 255, .55);
|
||
box-shadow: none;
|
||
}
|
||
.itflow-rack-item.is-ghost .itflow-rack-item-body {
|
||
cursor: default;
|
||
}
|
||
|
||
/* Tones - the faceplate colour of each kind of kit, shared by the elevation,
|
||
the mini racks on the listing, the list chips and the legend */
|
||
.itflow-rack-tone-server { background: linear-gradient(180deg, #3d72bb, #2c5891); }
|
||
.itflow-rack-tone-network { background: linear-gradient(180deg, #27907f, #1b6c60); }
|
||
.itflow-rack-tone-firewall { background: linear-gradient(180deg, #c04e4e, #963838); }
|
||
.itflow-rack-tone-power { background: linear-gradient(180deg, #b8862f, #8e6420); }
|
||
.itflow-rack-tone-device { background: linear-gradient(180deg, #6d5fc4, #5447a1); }
|
||
.itflow-rack-tone-shelf { background: linear-gradient(180deg, #6b7682, #57616c); }
|
||
.itflow-rack-tone-patch {
|
||
background: radial-gradient(rgba(0, 0, 0, .38) 1.2px, transparent 1.7px) 0 0 / 7px 7px, linear-gradient(180deg, #4d5864, #3b444e);
|
||
}
|
||
.itflow-rack-tone-cable {
|
||
background: repeating-linear-gradient(90deg, rgba(255, 255, 255, .08) 0, rgba(255, 255, 255, .08) 2px, transparent 2px, transparent 9px), linear-gradient(180deg, #31373d, #262b30);
|
||
}
|
||
.itflow-rack-tone-blank {
|
||
background: repeating-linear-gradient(90deg, rgba(255, 255, 255, .05) 0, rgba(255, 255, 255, .05) 10px, transparent 10px, transparent 13px), #1f2327;
|
||
color: rgba(255, 255, 255, .7);
|
||
}
|
||
|
||
/* Small coloured tile with the item's icon - item list, "not racked yet" */
|
||
.itflow-rack-chip {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1.9rem;
|
||
height: 1.9rem;
|
||
border-radius: var(--bs-border-radius);
|
||
font-size: .8rem;
|
||
color: #fff;
|
||
}
|
||
.itflow-rack-legend {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
gap: .35rem 1.1rem;
|
||
}
|
||
.itflow-rack-swatch {
|
||
display: inline-block;
|
||
width: .75rem;
|
||
height: .75rem;
|
||
margin-right: .35rem;
|
||
border-radius: .2rem;
|
||
vertical-align: -.1rem;
|
||
}
|
||
.itflow-rack-swatch-ghost {
|
||
border: 1px dashed var(--bs-secondary-color);
|
||
background: repeating-linear-gradient(45deg, var(--bs-secondary-bg) 0, var(--bs-secondary-bg) 2px, transparent 2px, transparent 4px);
|
||
}
|
||
|
||
/* Rack page header and side cards */
|
||
.itflow-rack-head-icon {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 3rem;
|
||
height: 3rem;
|
||
border-radius: var(--bs-border-radius-lg);
|
||
background: linear-gradient(180deg, #2b3035, #1c1f23);
|
||
font-size: 1.2rem;
|
||
color: #fff;
|
||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08);
|
||
}
|
||
.itflow-rack-dl {
|
||
display: grid;
|
||
grid-template-columns: auto minmax(0, 1fr);
|
||
gap: .4rem 1rem;
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-rack-dl dt {
|
||
font-weight: 500;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-rack-dl dd {
|
||
margin: 0;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.itflow-rack-stats .fw-semibold {
|
||
font-size: 1.1rem;
|
||
}
|
||
.itflow-rack-photo {
|
||
position: relative;
|
||
display: block;
|
||
overflow: hidden;
|
||
border-radius: var(--bs-border-radius);
|
||
}
|
||
.itflow-rack-photo img {
|
||
display: block;
|
||
width: 100%;
|
||
aspect-ratio: 4 / 3;
|
||
object-fit: cover;
|
||
transition: transform .2s ease-in-out;
|
||
}
|
||
.itflow-rack-photo:hover img {
|
||
transform: scale(1.04);
|
||
}
|
||
.itflow-rack-photo span {
|
||
position: absolute;
|
||
bottom: .35rem;
|
||
left: .35rem;
|
||
padding: .05rem .5rem;
|
||
border-radius: 50rem;
|
||
background-color: rgba(0, 0, 0, .6);
|
||
font-size: .7rem;
|
||
color: #fff;
|
||
}
|
||
.itflow-rack-form-photo img {
|
||
max-width: 100%;
|
||
max-height: 8rem;
|
||
border-radius: var(--bs-border-radius);
|
||
}
|
||
.itflow-rack-fit:empty {
|
||
display: none;
|
||
}
|
||
|
||
/* Listing cards: the whole card opens the rack, the menu sits above that link */
|
||
.itflow-rack-card {
|
||
transition: box-shadow .15s ease-in-out, border-color .15s ease-in-out;
|
||
}
|
||
.itflow-rack-card:hover {
|
||
border-color: var(--itflow-accent, var(--bs-primary));
|
||
box-shadow: 0 .25rem .9rem rgba(0, 0, 0, .1);
|
||
}
|
||
.itflow-rack-card-menu {
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
.itflow-rack-mini {
|
||
display: grid;
|
||
flex: 0 0 2.4rem;
|
||
grid-template-columns: minmax(0, 1fr);
|
||
grid-template-rows: repeat(var(--rack-rows), minmax(0, 1fr));
|
||
align-self: flex-start;
|
||
height: clamp(5.5rem, calc(var(--rack-rows) * .32rem), 9rem);
|
||
padding: 2px;
|
||
border: 3px solid #2b3035;
|
||
border-radius: .4rem;
|
||
background-color: #15181b;
|
||
}
|
||
.itflow-rack-mini span {
|
||
grid-column: 1;
|
||
margin: .5px 0;
|
||
border-radius: 1px;
|
||
}
|
||
.itflow-rack-mini span.is-ghost {
|
||
opacity: .45;
|
||
}
|
||
|
||
@media print {
|
||
.itflow-rack-frame,
|
||
.itflow-rack-bay,
|
||
.itflow-rack-item,
|
||
.itflow-rack-chip,
|
||
.itflow-rack-swatch,
|
||
.itflow-rack-head-icon {
|
||
-webkit-print-color-adjust: exact;
|
||
print-color-adjust: exact;
|
||
}
|
||
.itflow-rack-slot span,
|
||
.itflow-rack-item-edit {
|
||
display: none;
|
||
}
|
||
.itflow-rack {
|
||
--rack-u: clamp(1rem, calc(48rem / var(--rack-rows)), 1.5rem);
|
||
}
|
||
.itflow-rack-items,
|
||
.itflow-rack-pair {
|
||
break-inside: avoid;
|
||
}
|
||
}
|
||
|
||
/* Assets - agent/assets.php, agent/asset.php, agent/modals/asset/
|
||
|
||
Each kind of asset has a colour (functions/asset.php assetTypeTone()), used
|
||
for the icon tile on the list, the asset page header and the related-asset
|
||
chips. The tile is the colour at low strength with the icon at full, so it
|
||
reads in light and dark alike. */
|
||
.itflow-asset-tone-workstation { --itflow-asset-tone: #3d72bb; }
|
||
.itflow-asset-tone-server { --itflow-asset-tone: #6d5fc4; }
|
||
.itflow-asset-tone-virtual { --itflow-asset-tone: #9a5cc6; }
|
||
.itflow-asset-tone-network { --itflow-asset-tone: #23897a; }
|
||
.itflow-asset-tone-firewall { --itflow-asset-tone: #c04e4e; }
|
||
.itflow-asset-tone-wireless { --itflow-asset-tone: #1b8fc2; }
|
||
.itflow-asset-tone-phone { --itflow-asset-tone: #b8862f; }
|
||
.itflow-asset-tone-peripheral { --itflow-asset-tone: #5b6f8a; }
|
||
.itflow-asset-tone-other { --itflow-asset-tone: #6c757d; }
|
||
[data-bs-theme="dark"] .itflow-asset-tone-workstation { --itflow-asset-tone: #6f9fe0; }
|
||
[data-bs-theme="dark"] .itflow-asset-tone-server { --itflow-asset-tone: #a197ee; }
|
||
[data-bs-theme="dark"] .itflow-asset-tone-virtual { --itflow-asset-tone: #c595ea; }
|
||
[data-bs-theme="dark"] .itflow-asset-tone-network { --itflow-asset-tone: #4fc4b0; }
|
||
[data-bs-theme="dark"] .itflow-asset-tone-firewall { --itflow-asset-tone: #ec8585; }
|
||
[data-bs-theme="dark"] .itflow-asset-tone-wireless { --itflow-asset-tone: #5cc0ec; }
|
||
[data-bs-theme="dark"] .itflow-asset-tone-phone { --itflow-asset-tone: #e3b35c; }
|
||
[data-bs-theme="dark"] .itflow-asset-tone-peripheral { --itflow-asset-tone: #9fb2cc; }
|
||
[data-bs-theme="dark"] .itflow-asset-tone-other { --itflow-asset-tone: #adb5bd; }
|
||
.itflow-asset-icon {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 2.5rem;
|
||
height: 2.5rem;
|
||
border-radius: var(--bs-border-radius-lg);
|
||
background-color: color-mix(in srgb, var(--itflow-asset-tone) 14%, transparent);
|
||
color: var(--itflow-asset-tone);
|
||
font-size: 1.05rem;
|
||
}
|
||
.itflow-asset-icon-lg {
|
||
width: 3.5rem;
|
||
height: 3.5rem;
|
||
font-size: 1.5rem;
|
||
}
|
||
.itflow-asset-icon-sm {
|
||
width: 1.75rem;
|
||
height: 1.75rem;
|
||
border-radius: var(--bs-border-radius);
|
||
font-size: .8rem;
|
||
}
|
||
img.itflow-asset-icon {
|
||
object-fit: cover;
|
||
background: none;
|
||
}
|
||
|
||
/* Kind-of-asset tabs above the filter bar */
|
||
.itflow-asset-tabs .nav-link {
|
||
padding: .3rem .75rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-asset-tabs .nav-link .badge {
|
||
background-color: var(--bs-secondary-bg);
|
||
color: var(--bs-secondary-color);
|
||
font-weight: 500;
|
||
}
|
||
.itflow-asset-tabs .nav-link.active .badge {
|
||
background-color: rgba(255, 255, 255, .25);
|
||
color: inherit;
|
||
}
|
||
|
||
/* List rows */
|
||
.itflow-asset-table td {
|
||
padding-top: .6rem;
|
||
padding-bottom: .6rem;
|
||
}
|
||
.itflow-asset-name-cell {
|
||
min-width: 15rem;
|
||
}
|
||
.itflow-asset-desc {
|
||
max-width: 18rem;
|
||
}
|
||
.itflow-asset-model-cell {
|
||
min-width: 9rem;
|
||
}
|
||
/* Tag pills and other controls inside a cell with a stretched-link stay clickable */
|
||
.itflow-above-stretch {
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
.btn.btn-xs {
|
||
--bs-btn-padding-y: 0;
|
||
--bs-btn-padding-x: .4rem;
|
||
--bs-btn-font-size: .7rem;
|
||
--bs-btn-border-radius: var(--bs-border-radius-sm);
|
||
font-size: .7rem;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
/* Copy button beside a value - an IP, MAC, serial, email, username, URI or a
|
||
password. Quiet until the row or line it sits in is pointed at: a table row,
|
||
a list-group row, a quick-look line, or just its own parent element.
|
||
.itflow-copy-always keeps it showing where copying is the point (an MFA
|
||
secret). Markup:
|
||
<button type="button" class="itflow-copy clipboardjs" title="Copy IP" data-clipboard-text="..."><i class="far fa-copy"></i></button>
|
||
Copy buttons attached to an input stay input-group buttons. */
|
||
.itflow-copy {
|
||
margin-left: .3rem;
|
||
padding: 0 .15rem;
|
||
border: 0;
|
||
background: none;
|
||
font-size: .8em;
|
||
color: var(--bs-secondary-color);
|
||
opacity: 0;
|
||
transition: opacity .12s ease-in-out;
|
||
}
|
||
tr:hover .itflow-copy,
|
||
.list-group-item:hover .itflow-copy,
|
||
.itflow-glance-item:hover .itflow-copy,
|
||
:hover > .itflow-copy,
|
||
.itflow-copy-always .itflow-copy,
|
||
.itflow-copy:focus-visible {
|
||
opacity: 1;
|
||
}
|
||
.itflow-copy:hover {
|
||
color: var(--bs-emphasis-color);
|
||
}
|
||
@media (hover: none) {
|
||
.itflow-copy {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
/* Asset page */
|
||
.itflow-asset-dl {
|
||
display: grid;
|
||
grid-template-columns: auto minmax(0, 1fr);
|
||
gap: .45rem 1rem;
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-asset-dl dt {
|
||
font-weight: 500;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-asset-dl dd {
|
||
margin: 0;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.itflow-asset-pills {
|
||
gap: .25rem;
|
||
}
|
||
.itflow-asset-related .itflow-asset-pills .nav-link {
|
||
padding: .3rem .7rem;
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-asset-related .itflow-asset-pills .nav-link:not(.active) {
|
||
color: var(--bs-body-color);
|
||
}
|
||
.itflow-asset-related .itflow-asset-pills .nav-link.is-empty:not(.active) {
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-asset-pills .nav-link .badge {
|
||
background-color: var(--bs-secondary-bg);
|
||
color: var(--bs-secondary-color);
|
||
font-weight: 500;
|
||
}
|
||
.itflow-asset-pills .nav-link .badge.text-bg-danger {
|
||
background-color: var(--bs-danger) !important;
|
||
color: #fff !important;
|
||
}
|
||
.itflow-asset-pills .nav-link.active .badge {
|
||
background-color: rgba(255, 255, 255, .25);
|
||
color: inherit;
|
||
}
|
||
.itflow-asset-pane-bar {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: .5rem;
|
||
padding: .75rem 1rem;
|
||
border-bottom: 1px solid var(--bs-border-color-translucent);
|
||
}
|
||
.itflow-asset-related .table > :not(caption) > * > :first-child {
|
||
padding-left: 1rem;
|
||
}
|
||
.itflow-asset-related .table > :not(caption) > * > :last-child {
|
||
padding-right: 1rem;
|
||
}
|
||
.itflow-asset-related .table td:first-child {
|
||
min-width: 11rem;
|
||
}
|
||
.itflow-asset-related .table td.checkbox-column {
|
||
min-width: 0;
|
||
}
|
||
.itflow-asset-empty {
|
||
padding: 2.5rem 1rem;
|
||
text-align: center;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-asset-dl .font-monospace {
|
||
font-size: .82rem;
|
||
}
|
||
|
||
/* Section heading inside a long form (asset add/edit) */
|
||
.itflow-form-section {
|
||
margin-bottom: .75rem;
|
||
padding-bottom: .35rem;
|
||
border-bottom: 1px solid var(--bs-border-color-translucent);
|
||
font-size: .75rem;
|
||
font-weight: 600;
|
||
letter-spacing: .06em;
|
||
text-transform: uppercase;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
|
||
/* Asset quick pickers - status, location and who it is assigned to, as pills
|
||
on the assets list and in the asset page header (functions/asset.php
|
||
assetQuickHtml), as lines in the asset page's Where & who card */
|
||
.itflow-asset-pill {
|
||
max-width: 15rem;
|
||
padding: .35em .7em;
|
||
vertical-align: middle;
|
||
}
|
||
.itflow-asset-pill > .text-truncate {
|
||
min-width: 0;
|
||
}
|
||
.itflow-asset-pill-person {
|
||
padding: .2rem .6rem .2rem .2rem;
|
||
}
|
||
/* Nothing set yet - a dashed slot, so it reads as something to fill in */
|
||
.itflow-asset-pill-empty {
|
||
border: 1px dashed var(--bs-border-color);
|
||
color: var(--bs-secondary-color);
|
||
background: transparent;
|
||
}
|
||
/* In the Where & who card, and the quick look's glance lines, the picker is
|
||
the whole line: it lights up on hover with a chevron, like the ticket
|
||
page's properties. The negative margin keeps its text in line with the
|
||
plain values above and below. */
|
||
.itflow-asset-dl .itflow-quick-pick-detail,
|
||
.itflow-asset-glance .itflow-quick-pick-detail {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: .5rem;
|
||
width: calc(100% + 1rem);
|
||
min-width: 0;
|
||
margin: -.3rem -.5rem;
|
||
padding: .3rem .5rem;
|
||
text-align: left;
|
||
}
|
||
.itflow-asset-dl .itflow-quick-pick-detail:hover,
|
||
.itflow-asset-glance .itflow-quick-pick-detail:hover,
|
||
.itflow-asset-dl .itflow-quick-pick-detail:focus-visible,
|
||
.itflow-asset-glance .itflow-quick-pick-detail:focus-visible,
|
||
.itflow-asset-dl .itflow-quick-pick-detail[aria-expanded="true"],
|
||
.itflow-asset-glance .itflow-quick-pick-detail[aria-expanded="true"] {
|
||
background-color: var(--bs-tertiary-bg);
|
||
}
|
||
.itflow-asset-dl .itflow-quick-pick-detail + :not(:empty),
|
||
.itflow-asset-glance .itflow-quick-pick-detail + :not(:empty) {
|
||
margin-top: .35rem;
|
||
}
|
||
.itflow-asset-dl .ticket-prop-edit-icon,
|
||
.itflow-asset-glance .ticket-prop-edit-icon {
|
||
flex: 0 0 auto;
|
||
font-size: .7rem;
|
||
color: var(--bs-secondary-color);
|
||
opacity: 0;
|
||
transition: opacity .15s;
|
||
}
|
||
.itflow-asset-dl .itflow-quick-pick-detail:hover .ticket-prop-edit-icon,
|
||
.itflow-asset-glance .itflow-quick-pick-detail:hover .ticket-prop-edit-icon,
|
||
.itflow-asset-dl .itflow-quick-pick-detail:focus-visible .ticket-prop-edit-icon,
|
||
.itflow-asset-glance .itflow-quick-pick-detail:focus-visible .ticket-prop-edit-icon,
|
||
.itflow-asset-dl .itflow-quick-pick-detail[aria-expanded="true"] .ticket-prop-edit-icon,
|
||
.itflow-asset-glance .itflow-quick-pick-detail[aria-expanded="true"] .ticket-prop-edit-icon {
|
||
opacity: 1;
|
||
}
|
||
@media (hover: none) {
|
||
.itflow-asset-dl .ticket-prop-edit-icon,
|
||
.itflow-asset-glance .ticket-prop-edit-icon {
|
||
opacity: .6;
|
||
}
|
||
}
|
||
.itflow-asset-dl .itflow-quick-pick-detail.is-saved,
|
||
.itflow-asset-glance .itflow-quick-pick-detail.is-saved {
|
||
animation: itflowAssetQuickSaved .9s ease-out;
|
||
}
|
||
@keyframes itflowAssetQuickSaved {
|
||
from { background-color: var(--bs-success-bg-subtle); }
|
||
to { background-color: transparent; }
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.itflow-asset-dl .itflow-quick-pick-detail.is-saved,
|
||
.itflow-asset-glance .itflow-quick-pick-detail.is-saved {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
/* Asset add / edit tabs - one line, scrolling sideways on a phone */
|
||
.itflow-asset-form-tabs {
|
||
gap: .25rem;
|
||
scrollbar-width: thin;
|
||
}
|
||
.itflow-asset-form-tabs .nav-link {
|
||
padding-inline: .75rem;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Asset quick look - agent/modals/asset/asset.php. The glance lines under the
|
||
header: an icon in place of a label, then the value */
|
||
.itflow-asset-glance {
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-glance-item {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: .6rem;
|
||
min-width: 0;
|
||
padding: .3rem 0;
|
||
}
|
||
.itflow-glance-item > .fa-fw:first-child {
|
||
flex: 0 0 auto;
|
||
margin-top: .2rem;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-glance-item .font-monospace {
|
||
font-size: .82rem;
|
||
}
|
||
.itflow-asset-quicknote,
|
||
.itflow-contact-quicknote {
|
||
min-height: 2.6rem;
|
||
max-height: 12rem;
|
||
field-sizing: content;
|
||
border-color: transparent;
|
||
background-color: var(--bs-tertiary-bg);
|
||
resize: vertical;
|
||
}
|
||
.itflow-asset-quicknote:focus,
|
||
.itflow-contact-quicknote:focus {
|
||
background-color: var(--bs-body-bg);
|
||
}
|
||
.itflow-asset-quick-related {
|
||
padding-top: .75rem;
|
||
border-top: 1px solid var(--bs-border-color-translucent);
|
||
}
|
||
.itflow-asset-quick-related .table,
|
||
.itflow-asset-quick-related .list-group {
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-asset-quick-related .table > :not(caption) > * > :first-child {
|
||
padding-left: 0;
|
||
}
|
||
.itflow-asset-quick-related .table > :not(caption) > * > :last-child {
|
||
padding-right: 0;
|
||
}
|
||
.itflow-asset-quick-related .table td:first-child {
|
||
min-width: 0;
|
||
}
|
||
.itflow-asset-quick-related .itflow-asset-pills {
|
||
flex-wrap: nowrap;
|
||
gap: .15rem;
|
||
overflow-x: auto;
|
||
scrollbar-width: thin;
|
||
}
|
||
.itflow-asset-quick-related .itflow-asset-pills .nav-link {
|
||
padding: .25rem .55rem;
|
||
}
|
||
|
||
/* Contacts - functions/contact.php contactAvatar(): a photo, or initials on a
|
||
soft colour that stays the same for the person (like the client avatars) */
|
||
.itflow-contact-avatar {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 2.5rem;
|
||
height: 2.5rem;
|
||
border-radius: 50%;
|
||
font-size: .8rem;
|
||
font-weight: 600;
|
||
letter-spacing: .02em;
|
||
}
|
||
img.itflow-contact-avatar {
|
||
object-fit: cover;
|
||
}
|
||
/* contactLink(): the avatar, then the name - in a table cell, a card or a line of text */
|
||
.itflow-contact-ref {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: .4rem;
|
||
min-width: 0;
|
||
max-width: 100%;
|
||
vertical-align: middle;
|
||
}
|
||
.itflow-contact-avatar-xs {
|
||
width: 1.25rem;
|
||
height: 1.25rem;
|
||
font-size: .5rem;
|
||
letter-spacing: 0;
|
||
}
|
||
.itflow-contact-avatar-sm {
|
||
width: 1.75rem;
|
||
height: 1.75rem;
|
||
font-size: .65rem;
|
||
}
|
||
.itflow-contact-avatar-md {
|
||
width: 2rem;
|
||
height: 2rem;
|
||
font-size: .7rem;
|
||
}
|
||
.itflow-contact-avatar-lg {
|
||
width: 3.5rem;
|
||
height: 3.5rem;
|
||
font-size: 1.1rem;
|
||
}
|
||
/* Contacts list */
|
||
.itflow-contact-table td {
|
||
padding-top: .6rem;
|
||
padding-bottom: .6rem;
|
||
}
|
||
.itflow-contact-name-cell {
|
||
min-width: 16rem;
|
||
}
|
||
.itflow-contact-role {
|
||
max-width: 20rem;
|
||
}
|
||
.itflow-contact-reach > div + div {
|
||
margin-top: .15rem;
|
||
}
|
||
/* What is linked to a contact - a small count that opens that tab on their page */
|
||
.itflow-contact-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: .3rem;
|
||
padding: .1rem .5rem;
|
||
border-radius: 2rem;
|
||
font-size: .75rem;
|
||
color: var(--bs-body-color);
|
||
background-color: var(--bs-secondary-bg);
|
||
text-decoration: none;
|
||
white-space: nowrap;
|
||
}
|
||
.itflow-contact-chip i {
|
||
color: var(--bs-secondary-color);
|
||
font-size: .7rem;
|
||
}
|
||
.itflow-contact-chip:hover {
|
||
color: var(--bs-emphasis-color);
|
||
background-color: var(--bs-tertiary-bg);
|
||
box-shadow: inset 0 0 0 1px var(--bs-border-color);
|
||
}
|
||
.itflow-contact-chip.is-alert {
|
||
color: var(--bs-danger-text-emphasis);
|
||
background-color: var(--bs-danger-bg-subtle);
|
||
}
|
||
.itflow-contact-chip.is-alert i {
|
||
color: inherit;
|
||
}
|
||
|
||
/* Files & documents - agent/files.php, agent/document.php, the file and
|
||
document quick looks, the upload and link modals.
|
||
|
||
A file's type is a tinted tile like an asset's type (fileTypeTile() in
|
||
functions/files.php): the colour family says what it is at a glance -
|
||
PDFs red, Word blue, spreadsheets green - and created documents take the
|
||
app's own blue. */
|
||
.itflow-file-icon {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 2.5rem;
|
||
height: 2.5rem;
|
||
border-radius: var(--bs-border-radius-lg);
|
||
background-color: color-mix(in srgb, var(--itflow-file-tone) 14%, transparent);
|
||
color: var(--itflow-file-tone);
|
||
font-size: 1.1rem;
|
||
}
|
||
.itflow-file-icon-sm {
|
||
width: 1.75rem;
|
||
height: 1.75rem;
|
||
border-radius: var(--bs-border-radius);
|
||
font-size: .8rem;
|
||
}
|
||
.itflow-file-icon-lg {
|
||
width: 3.5rem;
|
||
height: 3.5rem;
|
||
font-size: 1.6rem;
|
||
}
|
||
.itflow-file-tone-pdf { --itflow-file-tone: #c8372d; }
|
||
.itflow-file-tone-word { --itflow-file-tone: #2b5797; }
|
||
.itflow-file-tone-excel { --itflow-file-tone: #1e7145; }
|
||
.itflow-file-tone-slides { --itflow-file-tone: #c4501c; }
|
||
.itflow-file-tone-image { --itflow-file-tone: #13857d; }
|
||
.itflow-file-tone-media { --itflow-file-tone: #8e44ad; }
|
||
.itflow-file-tone-archive { --itflow-file-tone: #8a6a2f; }
|
||
.itflow-file-tone-text { --itflow-file-tone: #5c6773; }
|
||
.itflow-file-tone-code { --itflow-file-tone: #4b5d8a; }
|
||
.itflow-file-tone-drawing { --itflow-file-tone: #d97706; }
|
||
.itflow-file-tone-cert { --itflow-file-tone: #a1781b; }
|
||
.itflow-file-tone-other { --itflow-file-tone: #6c757d; }
|
||
.itflow-file-tone-document { --itflow-file-tone: var(--itflow-accent, #0d6efd); }
|
||
[data-bs-theme="dark"] .itflow-file-tone-pdf { --itflow-file-tone: #f08a80; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-word { --itflow-file-tone: #8ab4f0; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-excel { --itflow-file-tone: #6fcf9a; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-slides { --itflow-file-tone: #f2a072; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-image { --itflow-file-tone: #5fd1c6; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-media { --itflow-file-tone: #c99ae0; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-archive { --itflow-file-tone: #d9b878; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-text { --itflow-file-tone: #b4bec8; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-code { --itflow-file-tone: #a3b3dc; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-drawing { --itflow-file-tone: #f5b257; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-cert { --itflow-file-tone: #e3c46b; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-other { --itflow-file-tone: #adb5bd; }
|
||
[data-bs-theme="dark"] .itflow-file-tone-document { --itflow-file-tone: #8ab4f8; }
|
||
|
||
/* The folder tree beside the list */
|
||
.itflow-files-tree {
|
||
background-color: var(--bs-tertiary-bg);
|
||
}
|
||
@media (min-width: 992px) {
|
||
.itflow-files-tree #filesFolderTree {
|
||
position: sticky;
|
||
top: 4rem;
|
||
max-height: calc(100vh - 5rem);
|
||
overflow-y: auto;
|
||
}
|
||
}
|
||
.itflow-folder > ul {
|
||
display: none;
|
||
}
|
||
.itflow-folder.is-open > ul {
|
||
display: block;
|
||
}
|
||
.itflow-folder-row {
|
||
display: flex;
|
||
align-items: center;
|
||
padding-left: calc(var(--itflow-folder-depth, 0) * .9rem);
|
||
border-radius: var(--bs-border-radius);
|
||
}
|
||
.itflow-folder-row:hover {
|
||
background-color: var(--bs-secondary-bg);
|
||
}
|
||
.itflow-folder-row.active {
|
||
background-color: var(--bs-primary-bg-subtle);
|
||
}
|
||
.itflow-folder-row.active .itflow-folder-link {
|
||
color: var(--bs-primary-text-emphasis);
|
||
font-weight: 600;
|
||
}
|
||
.itflow-folder-toggle {
|
||
flex: 0 0 1.25rem;
|
||
width: 1.25rem;
|
||
padding: 0;
|
||
border: 0;
|
||
background: none;
|
||
font-size: .65rem;
|
||
color: var(--bs-secondary-color);
|
||
transition: transform .12s ease-in-out;
|
||
}
|
||
.itflow-folder.is-open > .itflow-folder-row .itflow-folder-toggle {
|
||
transform: rotate(90deg);
|
||
}
|
||
.itflow-folder-link {
|
||
display: flex;
|
||
flex: 1 1 auto;
|
||
align-items: center;
|
||
min-width: 0;
|
||
padding: .3rem .4rem .3rem .1rem;
|
||
font-size: .875rem;
|
||
color: var(--bs-body-color);
|
||
text-decoration: none;
|
||
}
|
||
.itflow-folder-link .fa-folder,
|
||
.itflow-folder-link .fa-folder-open,
|
||
.itflow-folder-link .fa-home {
|
||
color: #d4a72c;
|
||
}
|
||
.itflow-folder-link .fa-home {
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-folder-link .badge {
|
||
background-color: var(--bs-secondary-bg);
|
||
color: var(--bs-secondary-color);
|
||
font-weight: 500;
|
||
}
|
||
.itflow-folder-menu .btn {
|
||
padding: 0 .4rem;
|
||
color: var(--bs-secondary-color);
|
||
opacity: 0;
|
||
}
|
||
.itflow-folder-row:hover .itflow-folder-menu .btn,
|
||
.itflow-folder-menu .btn[aria-expanded="true"],
|
||
.itflow-folder-menu .btn:focus-visible {
|
||
opacity: 1;
|
||
}
|
||
@media (hover: none) {
|
||
.itflow-folder-menu .btn {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
/* The list */
|
||
.itflow-files-main {
|
||
position: relative;
|
||
}
|
||
.itflow-files-path .breadcrumb {
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-files-table td {
|
||
padding-top: .55rem;
|
||
padding-bottom: .55rem;
|
||
}
|
||
.itflow-files-name-cell {
|
||
min-width: 14rem;
|
||
}
|
||
.itflow-files-desc {
|
||
max-width: 32rem;
|
||
}
|
||
.itflow-files-linked {
|
||
min-width: 8.5rem;
|
||
}
|
||
.itflow-files-name-cell .itflow-above-stretch {
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
|
||
/* The grid */
|
||
.itflow-file-card {
|
||
position: relative;
|
||
overflow: hidden;
|
||
transition: box-shadow .12s ease-in-out;
|
||
}
|
||
.itflow-file-card:hover {
|
||
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .08);
|
||
}
|
||
.itflow-file-card:has(.bulk-select:checked) {
|
||
outline: 2px solid var(--bs-primary);
|
||
background-color: var(--bs-primary-bg-subtle);
|
||
}
|
||
.itflow-file-card-check {
|
||
position: absolute;
|
||
top: .4rem;
|
||
left: .4rem;
|
||
z-index: 3;
|
||
display: flex;
|
||
min-height: 0;
|
||
margin: 0;
|
||
padding: .2rem;
|
||
border-radius: var(--bs-border-radius-sm);
|
||
background-color: var(--bs-body-bg);
|
||
box-shadow: 0 0 0 1px var(--bs-border-color-translucent);
|
||
/* Out of the way of the preview until the card is pointed at or ticked */
|
||
opacity: 0;
|
||
transition: opacity .12s ease-in-out;
|
||
}
|
||
.itflow-file-card:hover .itflow-file-card-check,
|
||
.itflow-file-card-check:has(:checked),
|
||
.itflow-file-card-check:has(:focus-visible),
|
||
.itflow-files-grid:has(.bulk-select:checked) .itflow-file-card-check {
|
||
opacity: 1;
|
||
}
|
||
@media (hover: none) {
|
||
.itflow-file-card-check {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
.itflow-file-card-check .form-check-input {
|
||
margin: 0;
|
||
background-color: var(--bs-body-bg);
|
||
}
|
||
.itflow-file-card-preview {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 9.5rem;
|
||
overflow: hidden;
|
||
border-bottom: 1px solid var(--bs-border-color-translucent);
|
||
background-color: var(--bs-tertiary-bg);
|
||
text-decoration: none;
|
||
}
|
||
.itflow-file-card-preview img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
.itflow-file-card-preview iframe {
|
||
width: 100%;
|
||
/* Taller than the box and pulled up, so a viewer's toolbar is clipped away */
|
||
height: calc(100% + 56px);
|
||
margin-top: -56px;
|
||
border: 0;
|
||
pointer-events: none;
|
||
background: #fff;
|
||
}
|
||
.itflow-file-card-preview pre {
|
||
width: 100%;
|
||
height: 100%;
|
||
margin: 0;
|
||
padding: .6rem .7rem;
|
||
overflow: hidden;
|
||
font-size: .65rem;
|
||
line-height: 1.35;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
color: var(--bs-secondary-color);
|
||
background-color: var(--bs-body-bg);
|
||
}
|
||
.itflow-file-card.is-document .itflow-file-card-preview pre {
|
||
font-family: var(--bs-body-font-family);
|
||
font-size: .7rem;
|
||
}
|
||
|
||
/* Files dragged over the list */
|
||
.itflow-files-dropzone {
|
||
position: absolute;
|
||
z-index: 20;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
border: 2px dashed var(--bs-primary);
|
||
border-radius: var(--bs-border-radius-lg);
|
||
background-color: color-mix(in srgb, var(--bs-body-bg) 82%, transparent);
|
||
color: var(--bs-primary-text-emphasis);
|
||
pointer-events: none;
|
||
}
|
||
.itflow-files-dropzone[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
/* Upload modal */
|
||
.itflow-upload-drop {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: .15rem;
|
||
padding: 1.6rem 1rem;
|
||
border: 2px dashed var(--bs-border-color);
|
||
border-radius: var(--bs-border-radius-lg);
|
||
color: var(--bs-secondary-color);
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: border-color .12s, background-color .12s;
|
||
}
|
||
.itflow-upload-drop:hover,
|
||
.itflow-upload-drop.is-over {
|
||
border-color: var(--bs-primary);
|
||
background-color: var(--bs-primary-bg-subtle);
|
||
color: var(--bs-primary-text-emphasis);
|
||
}
|
||
.itflow-upload-drop.has-files {
|
||
padding: .9rem 1rem;
|
||
}
|
||
.itflow-upload-drop:focus-within {
|
||
outline: 2px solid var(--bs-primary);
|
||
outline-offset: 2px;
|
||
}
|
||
.itflow-upload-list {
|
||
max-height: 12rem;
|
||
overflow-y: auto;
|
||
}
|
||
.itflow-upload-list li {
|
||
padding: .2rem .25rem;
|
||
border-bottom: 1px solid var(--bs-border-color-translucent);
|
||
}
|
||
|
||
/* File quick look preview */
|
||
.itflow-file-preview {
|
||
overflow: hidden;
|
||
border: 1px solid var(--bs-border-color);
|
||
border-radius: var(--bs-border-radius-lg);
|
||
background-color: var(--bs-tertiary-bg);
|
||
text-align: center;
|
||
}
|
||
.itflow-file-preview img {
|
||
max-width: 100%;
|
||
max-height: 22rem;
|
||
object-fit: contain;
|
||
}
|
||
.itflow-file-preview iframe {
|
||
display: block;
|
||
width: 100%;
|
||
height: 26rem;
|
||
border: 0;
|
||
background: #fff;
|
||
}
|
||
.itflow-file-preview pre {
|
||
max-height: 20rem;
|
||
margin: 0;
|
||
padding: .75rem 1rem;
|
||
overflow: auto;
|
||
font-size: .75rem;
|
||
text-align: left;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
}
|
||
.itflow-file-preview .itflow-asset-empty {
|
||
border: 0;
|
||
}
|
||
|
||
/* Linked records - linkGroupsHtml() in functions/links.php */
|
||
.itflow-link-group + .itflow-link-group,
|
||
.itflow-link-group + .itflow-link-empty {
|
||
margin-top: .9rem;
|
||
}
|
||
.itflow-link-group-head {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: .35rem;
|
||
font-size: .75rem;
|
||
font-weight: 600;
|
||
letter-spacing: .02em;
|
||
text-transform: uppercase;
|
||
color: var(--bs-secondary-color);
|
||
}
|
||
.itflow-link-group-head .badge {
|
||
background-color: var(--bs-secondary-bg);
|
||
color: var(--bs-secondary-color);
|
||
font-weight: 500;
|
||
}
|
||
.itflow-link-group-head a {
|
||
text-transform: none;
|
||
letter-spacing: 0;
|
||
font-weight: 500;
|
||
}
|
||
.itflow-link-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .6rem;
|
||
padding: .3rem .4rem;
|
||
margin: 0 -.4rem;
|
||
border-radius: var(--bs-border-radius);
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-link-row:hover {
|
||
background-color: var(--bs-tertiary-bg);
|
||
}
|
||
.itflow-link-icon {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1.75rem;
|
||
height: 1.75rem;
|
||
border-radius: var(--bs-border-radius);
|
||
background-color: var(--bs-secondary-bg);
|
||
color: var(--bs-secondary-color);
|
||
font-size: .8rem;
|
||
}
|
||
.itflow-link-remove {
|
||
flex: 0 0 auto;
|
||
padding: 0 .25rem;
|
||
font-size: .8rem;
|
||
color: var(--bs-secondary-color);
|
||
opacity: 0;
|
||
transition: opacity .12s ease-in-out;
|
||
}
|
||
.itflow-link-row:hover .itflow-link-remove,
|
||
.itflow-link-remove:focus-visible {
|
||
opacity: 1;
|
||
}
|
||
.itflow-link-remove:hover {
|
||
color: var(--bs-danger);
|
||
}
|
||
@media (hover: none) {
|
||
.itflow-link-remove {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
.itflow-link-empty {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: .35rem;
|
||
}
|
||
.itflow-link-types .nav-link {
|
||
padding: .3rem .7rem;
|
||
font-size: .875rem;
|
||
}
|
||
.itflow-link-types .nav-link:not(.active) {
|
||
color: var(--bs-body-color);
|
||
}
|
||
|
||
/* The document page and quick looks */
|
||
.itflow-document-body {
|
||
min-height: 12rem;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.itflow-document-quick-content {
|
||
max-height: 55vh;
|
||
overflow: auto;
|
||
padding: 1rem 1.25rem;
|
||
border: 1px solid var(--bs-border-color);
|
||
border-radius: var(--bs-border-radius-lg);
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.itflow-document-history .list-group-item {
|
||
padding-top: .5rem;
|
||
padding-bottom: .5rem;
|
||
}
|
||
.itflow-document-history .list-group-item:hover .itflow-copy {
|
||
opacity: 1;
|
||
}
|
||
.itflow-preview-nav {
|
||
z-index: 2;
|
||
}
|
||
@media print {
|
||
.itflow-document-head .card-body {
|
||
padding: 0;
|
||
}
|
||
.itflow-document-head,
|
||
.itflow-document-body {
|
||
border: 0;
|
||
box-shadow: none;
|
||
}
|
||
}
|