/* ===== Layout tokens =====
   One source of truth for the sidebar/navbar geometry so the sidebar, navbar,
   content and footer can never drift out of alignment. */
:root {
    --sidebar-width: 250px;
    --sidebar-collapsed-width: 70px;
    --navbar-height: 56px;
}

/* ===== Sidebar ===== */
.sidebar {
    position: fixed;
    /* Sits BELOW the fixed navbar. Previously both were top:0 and the navbar
       (z-index 1031) painted over the sidebar header, hiding it entirely. */
    top: var(--navbar-height);
    left: 0;
    width: var(--sidebar-width);
    height: calc(100vh - var(--navbar-height));
    background-color: #343a40;
    color: #fff;
    z-index: 1020;
    overflow-y: auto;
    overflow-x: hidden;
    transition: transform 0.3s ease, width 0.3s ease;
}

/* Thin scrollbar so a long menu doesn't get a chunky light-on-dark bar. */
.sidebar {
    scrollbar-width: thin;
    scrollbar-color: #6c757d #343a40;
}

.sidebar::-webkit-scrollbar {
    width: 6px;
}

.sidebar::-webkit-scrollbar-thumb {
    background-color: #6c757d;
    border-radius: 3px;
}

.sidebar-nav {
    padding-bottom: 1rem;
}

.sidebar-nav .nav-link {
    color: #adb5bd;
    padding: 10px 20px;
    font-size: 0.9rem;
    border-left: 3px solid transparent;
    transition: all 0.2s ease;
}

.sidebar-nav .nav-link:hover {
    background-color: #495057;
    color: #fff;
}

.sidebar-nav .nav-link.active {
    background-color: #0d6efd;
    color: #fff;
    border-left-color: #fff;
}

.sidebar-nav .nav-link i {
    width: 22px;
    text-align: center;
    margin-right: 8px;
}

.sidebar-nav .nav-link .float-end {
    transition: transform 0.2s ease;
    font-size: 0.7rem;
    margin-top: 4px;
}

.sidebar-nav .collapse.show + .nav-link .float-end,
.sidebar-nav [aria-expanded="true"] .float-end {
    transform: rotate(90deg);
}

.sidebar-nav .collapse .nav-link {
    padding: 6px 20px;
    font-size: 0.85rem;
}

/* ===== Collapsed (icon rail) =====
   Driven by a class on <body>. The old rules keyed off sibling combinators
   (`.sidebar.collapsed ~ .main-content .navbar`) that could never match, because
   the navbar is a sibling BEFORE the sidebar, not a descendant of .main-content
   - so collapsing left the navbar and content padding stuck at full width. */
body.sidebar-collapsed .sidebar {
    width: var(--sidebar-collapsed-width);
}

body.sidebar-collapsed .sidebar .nav-link span,
body.sidebar-collapsed .sidebar .float-end {
    display: none;
}

body.sidebar-collapsed .sidebar .nav-link {
    text-align: center;
    padding: 12px 0;
}

body.sidebar-collapsed .sidebar .nav-link i {
    margin-right: 0;
    font-size: 1.1rem;
}

/* Submenu links are bare text with no <span> to hide, so they used to spill
   out of the 70px rail. Hide the whole group instead. */
body.sidebar-collapsed .sidebar .collapse,
body.sidebar-collapsed .sidebar .collapsing {
    display: none !important;
}

/* ===== Top Navbar ===== */
.navbar.fixed-top {
    height: var(--navbar-height);
    padding-left: calc(var(--sidebar-width) + 10px);
    transition: padding-left 0.3s ease;
}

body.sidebar-collapsed .navbar.fixed-top {
    padding-left: calc(var(--sidebar-collapsed-width) + 10px);
}

/* ===== Main Content ===== */
.main-content {
    margin-left: var(--sidebar-width);
    margin-top: var(--navbar-height);
    padding: 20px;
    background-color: #f8f9fa;
    min-height: calc(100vh - var(--navbar-height));
    transition: margin-left 0.3s ease;
}

body.sidebar-collapsed .main-content {
    margin-left: var(--sidebar-collapsed-width);
}

/* ===== Footer ===== */
.main-footer {
    margin-left: var(--sidebar-width);
    background-color: #fff;
    border-top: 1px solid #dee2e6;
    transition: margin-left 0.3s ease;
}

body.sidebar-collapsed .main-footer {
    margin-left: var(--sidebar-collapsed-width);
}

/* ===== Mobile backdrop ===== */
.sidebar-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 1019;
}

body.sidebar-open .sidebar-backdrop {
    display: block;
}

/* ===== Cards ===== */
.card {
    border-radius: 8px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
    border: 1px solid #e9ecef;
}

.card-header {
    background-color: #fff;
    border-bottom: 1px solid #e9ecef;
    font-weight: 600;
}

/* ===== Stat Cards ===== */
.stat-card {
    border-left: 4px solid;
    border-radius: 8px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
    transition: transform 0.15s ease;
}

.stat-card:hover {
    transform: translateY(-2px);
}

.stat-card.border-primary { border-left-color: #0d6efd; }
.stat-card.border-success { border-left-color: #198754; }
.stat-card.border-warning { border-left-color: #ffc107; }
.stat-card.border-danger  { border-left-color: #dc3545; }
.stat-card.border-info    { border-left-color: #0dcaf0; }

/* Several views use a bare `.stat-card <colour>` without Bootstrap's `.card`
   wrapper, which left the content with no background and no padding - the
   number ended up flush against the coloured border. Make the component stand
   on its own so both spellings render identically. */
.stat-card:not(.card) {
    background-color: #fff;
    padding: 1rem 1.25rem;
    height: 100%;
}

.stat-card.primary { border-left-color: #0d6efd; }
.stat-card.success { border-left-color: #198754; }
.stat-card.warning { border-left-color: #ffc107; }
.stat-card.danger  { border-left-color: #dc3545; }
.stat-card.info    { border-left-color: #0dcaf0; }

.stat-card.primary .stat-value { color: #0d6efd; }
.stat-card.success .stat-value { color: #198754; }
.stat-card.warning .stat-value { color: #997404; }
.stat-card.danger  .stat-value { color: #dc3545; }
.stat-card.info    .stat-value { color: #087990; }

/* Bare cards carry currency totals, which overflow at the default size. */
.stat-card:not(.card) .stat-value {
    font-size: 1.35rem;
}

.stat-card .stat-icon {
    font-size: 2rem;
    opacity: 0.3;
}

.stat-card .stat-value {
    font-size: 1.8rem;
    font-weight: 700;
    line-height: 1.1;
    /* Long values (currency, big counts) must wrap inside the card rather
       than push the icon out or spill past the border. */
    overflow-wrap: anywhere;
}

/* For currency and other long values that would otherwise dominate the card. */
.stat-card .stat-value-sm {
    font-size: 1.35rem;
}

.stat-card .stat-label {
    font-size: 0.85rem;
    color: #6c757d;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* Lets a flex child shrink below its content width so text can wrap. */
.min-w-0 {
    min-width: 0;
}

.stat-card .stat-icon {
    flex-shrink: 0;
    margin-left: 0.75rem;
}

/* ===== Status Badges ===== */
.badge-active {
    background-color: #198754;
    color: #fff;
}

.badge-inactive {
    background-color: #6c757d;
    color: #fff;
}

.badge-terminated {
    background-color: #dc3545;
    color: #fff;
}

.badge-pending {
    background-color: #ffc107;
    color: #212529;
}

.badge-approved {
    background-color: #198754;
    color: #fff;
}

.badge-rejected {
    background-color: #dc3545;
    color: #fff;
}

.badge-draft {
    background-color: #6c757d;
    color: #fff;
}

.badge-open {
    background-color: #0d6efd;
    color: #fff;
}

.badge-closed {
    background-color: #6c757d;
    color: #fff;
}

.badge-shortlisted {
    background-color: #0dcaf0;
    color: #212529;
}

.badge-hired {
    background-color: #198754;
    color: #fff;
}

/* ===== Tables ===== */
.table thead {
    background-color: #f8f9fa;
}

.table thead th {
    font-weight: 600;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    color: #495057;
    border-bottom-width: 1px;
}

.table td {
    vertical-align: middle;
}

/* ===== Attendance Calendar ===== */
.att-cell {
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    font-size: 0.7rem;
    font-weight: 600;
    margin: 1px;
    cursor: default;
}

.att-present {
    background-color: #d1e7dd;
    color: #0f5132;
}

.att-absent {
    background-color: #f8d7da;
    color: #842029;
}

.att-half {
    background-color: #fff3cd;
    color: #664d03;
}

.att-leave {
    background-color: #cff4fc;
    color: #055160;
}

.att-holiday {
    background-color: #e2e3e5;
    color: #41464b;
}

.att-weekend {
    background-color: #f5f5f5;
    color: #adb5bd;
}

/* ===== Forms ===== */
.form-label {
    font-weight: 500;
    font-size: 0.9rem;
    margin-bottom: 0.3rem;
}

.form-control:focus,
.form-select:focus {
    border-color: #86b7fe;
    box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.15);
}

.form-section {
    background: #fff;
    border-radius: 8px;
    padding: 1.5rem;
    margin-bottom: 1.5rem;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}

.form-section h5 {
    border-bottom: 2px solid #0d6efd;
    padding-bottom: 0.5rem;
    margin-bottom: 1rem;
}

/* ===== Quick Actions ===== */
.quick-action-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 1rem;
    border-radius: 8px;
    text-decoration: none;
    color: #495057;
    transition: all 0.2s ease;
    border: 1px solid #e9ecef;
}

.quick-action-btn:hover {
    background-color: #e9ecef;
    color: #212529;
    transform: translateY(-2px);
}

.quick-action-btn i {
    font-size: 1.5rem;
    margin-bottom: 0.5rem;
}

/* ===== Punch / Face Capture ===== */
.punch-stage {
    position: relative;
    width: 100%;
    aspect-ratio: 4 / 3;
    background: #212529;
    border-radius: 8px;
    overflow: hidden;
}

.punch-stage video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Mirror the preview so moving left on screen matches moving left in life. */
    transform: scaleX(-1);
}

.punch-camera-off {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: #adb5bd;
}

.punch-camera-off i {
    font-size: 2.5rem;
    margin-bottom: 0.5rem;
    opacity: 0.5;
}

/* ===== Chart Empty State ===== */
/* Matches the 250px canvas height so a data-less card keeps the row aligned
   instead of collapsing next to a chart that does have data. */
.chart-empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 250px;
    color: #6c757d;
    text-align: center;
}

.chart-empty-state i {
    font-size: 2.5rem;
    opacity: 0.25;
    margin-bottom: 0.75rem;
}

.chart-empty-state p {
    font-size: 0.9rem;
}

/* ===== Page Header ===== */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 1.5rem;
}

.page-header h4 {
    margin-bottom: 0;
    font-weight: 600;
}

/* ===== Responsive ===== */
@media (max-width: 768px) {
    /* Off-canvas: the sidebar slides over the content instead of reserving
       space, and always at full width - never the icon rail. */
    .sidebar,
    body.sidebar-collapsed .sidebar {
        width: var(--sidebar-width);
        transform: translateX(-100%);
    }

    body.sidebar-open .sidebar {
        transform: translateX(0);
    }

    /* Full-width menu on mobile even if the desktop state was collapsed. */
    body.sidebar-collapsed .sidebar .nav-link span,
    body.sidebar-collapsed .sidebar .float-end {
        display: inline;
    }

    body.sidebar-collapsed .sidebar .nav-link {
        text-align: left;
        padding: 10px 20px;
    }

    body.sidebar-collapsed .sidebar .collapse.show {
        display: block !important;
    }

    .main-content,
    body.sidebar-collapsed .main-content,
    .main-footer,
    body.sidebar-collapsed .main-footer {
        margin-left: 0;
    }

    .navbar.fixed-top,
    body.sidebar-collapsed .navbar.fixed-top {
        padding-left: 10px;
    }

    .page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.5rem;
    }

    .stat-card .stat-value {
        font-size: 1.4rem;
    }
}

/* ===== Print ===== */
@media print {
    .sidebar,
    .navbar,
    .main-footer,
    .no-print {
        display: none !important;
    }

    .main-content {
        margin-left: 0 !important;
        margin-top: 0 !important;
        padding: 0 !important;
    }

    .card {
        box-shadow: none !important;
        border: 1px solid #dee2e6 !important;
    }
}

/* ===== Misc ===== */
.text-truncate-2 {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.avatar-sm {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 0.8rem;
}

.cursor-pointer {
    cursor: pointer;
}

.border-dashed {
    border-style: dashed !important;
}

/* ===== Navbar brand ===== */
/* The brand is a logo + company name that must not push the bell and avatar
   off a narrow screen, so it is allowed to shrink and truncate. */
.navbar-brand {
    max-width: 55vw;
    min-width: 0;
}

.navbar-logo {
    height: 32px;
    width: auto;
    max-width: 120px;
    object-fit: contain;
}

/* ===== Notification dropdown ===== */
.notif-dropdown {
    width: 340px;
    max-width: calc(100vw - 1.5rem);
    max-height: 70vh;
    overflow-y: auto;
    padding-bottom: 0;
}

.notif-item {
    white-space: normal;
    border-bottom: 1px solid #f1f3f5;
    padding: 0.6rem 1rem;
}

.notif-item:last-of-type {
    border-bottom: 0;
}

.notif-unread {
    background-color: #eef5ff;
}

.notif-message {
    /* Two lines then ellipsis - a long message must not turn the panel into a
       full-height wall of text. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.notif-time {
    font-size: 0.7rem;
}

/* =====================================================================
   Mobile hardening
   ---------------------------------------------------------------------
   Pages are built with wide data tables and header rows that put a title
   and two or three buttons on one line. Both overflow a phone, and the
   result was a page that scrolled sideways with controls off-screen.
   These rules are deliberately global so a view does not have to opt in.
   ===================================================================== */

/* Nothing may widen the page itself. Overflow is handled by the specific
   element that is too wide (a table, a pre, a long word), never by the
   document, so position: sticky keeps working. */
html, body {
    max-width: 100%;
}

.main-content {
    /* The sidebar is fixed, so the content column owns the horizontal axis. */
    max-width: 100%;
}

@media (max-width: 768px) {
    /* Bootstrap's .table-responsive wrapper is missing on a number of older
       views. Applying the same treatment to the table itself scrolls it in
       place instead of stretching the page. A table already inside a
       .table-responsive is unaffected - the wrapper scrolls first. */
    .card-body > .table,
    .card > .table,
    .main-content > .table {
        display: block;
        width: 100%;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    /* Title-plus-buttons header rows: let the buttons drop to a second line
       rather than squeezing the heading to one character per line. */
    .main-content .d-flex.justify-content-between {
        flex-wrap: wrap;
        gap: 0.5rem;
    }

    /* Filter bars built from inline selects: full width beats a 60px select. */
    .main-content form .form-select-sm[style*="width:auto"],
    .main-content form .form-control-sm[style*="width:auto"] {
        width: 100% !important;
    }

    .container-fluid {
        padding-left: 0.75rem;
        padding-right: 0.75rem;
    }

    /* Comfortable tap targets - 44px is the usual minimum. */
    .btn:not(.btn-sm):not(.btn-close),
    .form-control:not(.form-control-sm),
    .form-select:not(.form-select-sm) {
        min-height: 44px;
    }

    /* A button group of three or more wraps instead of overflowing. */
    .btn-group,
    .btn-toolbar {
        flex-wrap: wrap;
    }

    /* A dropdown opened near the right edge must stay on screen. */
    .dropdown-menu {
        max-width: calc(100vw - 1.5rem);
    }

    /* Modals get the full width of a phone. */
    .modal-dialog:not(.modal-fullscreen) {
        margin: 0.5rem;
    }

    /* Cards holding a sticky order summary should not stick on a phone -
       there is no second column for it to sit beside. */
    .card.sticky-top {
        position: static !important;
    }

    /* Long unbroken strings (emails, UPI IDs, reference numbers) wrap. */
    .text-break,
    td, th {
        overflow-wrap: anywhere;
    }

    /* Definition-style two-column tables inside profile/detail cards read
       better stacked than squeezed. */
    .table-sm.table-borderless td {
        padding-left: 0;
    }

    h1, .h1 { font-size: 1.6rem; }
    h2, .h2 { font-size: 1.4rem; }
    h3, .h3 { font-size: 1.25rem; }
    h4, .h4 { font-size: 1.1rem; }
}

@media (max-width: 575.98px) {
    /* Below ~576px a stat card grid of four is unreadable; two across works. */
    .stat-card .stat-value {
        font-size: 1.25rem;
    }

    .navbar-brand {
        max-width: 45vw;
        font-size: 1rem;
    }

    .navbar-logo {
        height: 26px;
        max-width: 80px;
    }

    /* Pagination with many pages overflows; let it scroll. */
    .pagination {
        flex-wrap: wrap;
    }
}

/* ===== Company logo preview (Settings > General) ===== */
.company-logo-preview {
    height: 90px;
    padding: 0.5rem;
}

.company-logo-preview img {
    max-height: 100%;
    max-width: 100%;
    object-fit: contain;
}

/* =====================================================================
   Organisation chart
   ---------------------------------------------------------------------
   Two layouts over one set of markup (nested ul/li), chosen by breakpoint:

     - Desktop: a classic top-down chart. Connectors are drawn with
       borders on ::before / ::after, so there is no charting library and
       it prints correctly.
     - Phone and small tablet: an indented vertical outline, like a file
       tree. A horizontal org chart on a 380px screen is unusable - a node
       and its parent never fit on screen together - so below 768px the
       same data is re-laid-out rather than merely scaled down.
   ===================================================================== */

:root {
    --org-line: #ced4da;
}

.org-chart-scroll {
    overflow-x: auto;
    overflow-y: hidden;
    padding: 1.25rem 0.5rem 1.5rem;
    -webkit-overflow-scrolling: touch;
}

.org-tree,
.org-tree ul {
    margin: 0;
    padding: 0;
    list-style: none;
}

/* ---------- Desktop: top-down chart ---------- */
@media (min-width: 769px) {
    .org-tree,
    .org-tree ul {
        display: flex;
        justify-content: center;
        padding-top: 22px;
        position: relative;
    }

    .org-tree li {
        position: relative;
        padding: 22px 10px 0;
        text-align: center;
    }

    /* Horizontal bar joining siblings: each child draws half of it. */
    .org-tree li::before,
    .org-tree li::after {
        content: '';
        position: absolute;
        top: 0;
        width: 50%;
        height: 22px;
        border-top: 2px solid var(--org-line);
    }

    .org-tree li::before { right: 50%; }
    .org-tree li::after  { left: 50%; }

    /* Trim the outer ends so the bar spans exactly the sibling group
       rather than overhanging it, and round the corners it turns at. */
    .org-tree li:first-child::before { border: 0 none; }
    .org-tree li:last-child::after   { border: 0 none; }

    .org-tree li:first-child::after {
        border-left: 2px solid var(--org-line);
        border-radius: 6px 0 0 0;
    }

    .org-tree li:last-child::before {
        border-right: 2px solid var(--org-line);
        border-radius: 0 6px 0 0;
    }

    /* An only child needs just the vertical stem from its parent. */
    .org-tree li:only-child::before,
    .org-tree li:only-child::after {
        display: none;
    }

    .org-tree li:only-child > .org-node::before {
        content: '';
        position: absolute;
        top: -22px;
        left: 50%;
        width: 0;
        height: 22px;
        border-left: 2px solid var(--org-line);
    }

    /* Stem dropping from a parent card to the bar above its children. */
    .org-tree ul::before {
        content: '';
        position: absolute;
        top: 0;
        left: 50%;
        width: 0;
        height: 22px;
        border-left: 2px solid var(--org-line);
    }

    /* Roots sit at the top; nothing connects above them. */
    .org-tree > li {
        padding-top: 0;
    }

    .org-tree > li::before,
    .org-tree > li::after {
        display: none;
    }

    .org-tree > li:only-child > .org-node::before {
        display: none;
    }
}


/* ---------- The card ---------- */
.org-node {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    position: relative;
    /* Wider than it was: at 172px a normal Indian full name plus a
       designation wrapped to four or five lines and neighbouring cards ended
       up wildly different heights. */
    min-width: 208px;
    max-width: 240px;
    min-height: 148px;
    justify-content: flex-start;
    padding: 0.85rem 0.9rem 1rem;
    background: #fff;
    border: 1px solid #dee2e6;
    border-radius: 10px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
    transition: box-shadow 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
    white-space: normal;
    vertical-align: top;
}

.org-node:hover {
    border-color: #0d6efd;
    box-shadow: 0 4px 14px rgba(13, 110, 253, 0.16);
}

.org-node-link {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    min-width: 0;
    text-decoration: none;
    color: inherit;
}

.org-avatar {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    object-fit: cover;
    margin-bottom: 0.5rem;
    flex-shrink: 0;
    background: #e9ecef;
}

.org-avatar-initials {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: #0d6efd;
    color: #fff;
    font-weight: 600;
    font-size: 1.05rem;
}

/* Lets the text column shrink so long names ellipsize instead of
   stretching the card. */
.org-node-text {
    min-width: 0;
    width: 100%;
}

/* Names and roles are capped at two lines and then ellipsized. Without the
   cap one long designation stretched its card and pushed the whole branch
   out of alignment. The full text is on the element's title attribute. */
.org-name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-weight: 600;
    font-size: 0.95rem;
    line-height: 1.3;
    color: #212529;
    word-break: break-word;
}

.org-role {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: 0.82rem;
    color: #0d6efd;
    line-height: 1.3;
    margin-top: 3px;
    word-break: break-word;
}

.org-meta {
    display: block;
    font-size: 0.75rem;
    color: #6c757d;
    margin-top: 4px;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

/* ---------- Zoom ----------
   A deep organisation is wider than any screen. Scaling the tree lets the
   whole shape be taken in at once, and printing picks up the same scale. */
.org-tree-zoom {
    transform-origin: top center;
    transition: transform 0.15s ease;
}

/* Collapse control, doubling as the direct-report count. */
.org-toggle {
    position: absolute;
    bottom: -11px;
    left: 50%;
    transform: translateX(-50%);
    border: 1px solid #ced4da;
    background: #fff;
    border-radius: 999px;
    min-width: 26px;
    height: 22px;
    padding: 0 8px;
    font-size: 0.7rem;
    font-weight: 600;
    line-height: 1;
    color: #495057;
    cursor: pointer;
    z-index: 3;
}

.org-toggle:hover {
    background: #0d6efd;
    border-color: #0d6efd;
    color: #fff;
}

.org-tree li.collapsed > ul {
    display: none;
}

.org-tree li.collapsed > .org-node .org-toggle {
    background: #6c757d;
    border-color: #6c757d;
    color: #fff;
}

/* A branch whose manager is outside the current filter still renders as a
   root; the dashed amber edge says the link upwards is hidden, not missing. */
.org-node.org-detached {
    border-color: #ffc107;
    border-style: dashed;
}

/* Search highlighting */
.org-node.org-match {
    border-color: #ffc107;
    box-shadow: 0 0 0 3px rgba(255, 193, 7, 0.35);
}

.org-node.org-dim {
    opacity: 0.3;
}

/* Placed after the desktop card rules on purpose: a media query adds no
   specificity, so when this block sat above them the base .org-node
   width and column layout overrode it and the mobile outline never
   actually applied. */
/* ---------- Mobile: indented outline ---------- */
@media (max-width: 768px) {
    .org-chart-scroll {
        overflow-x: hidden;
        padding: 0.5rem 0;
    }

    .org-tree li {
        position: relative;
        padding: 0 0 0 1.35rem;
        margin: 0;
    }

    .org-tree > li {
        padding-left: 0;
    }

    /* Vertical spine running down a group of siblings... */
    .org-tree li::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0.4rem;
        bottom: 0;
        border-left: 2px solid var(--org-line);
    }

    /* ...which stops at the last sibling so no line dangles below it. */
    .org-tree li:last-child::before {
        bottom: auto;
        height: 1.75rem;
    }

    /* ...and a short elbow across to each card. */
    .org-tree li::after {
        content: '';
        position: absolute;
        top: 1.75rem;
        left: 0.4rem;
        width: 0.95rem;
        border-top: 2px solid var(--org-line);
    }

    .org-tree > li::before,
    .org-tree > li::after {
        display: none;
    }

    /* The card becomes a full-width row: avatar left, text right. */
    .org-node {
        display: flex;
        align-items: center;
        width: 100%;
        max-width: none;
        min-width: 0;
        margin: 0.35rem 0;
        padding: 0.55rem 0.65rem;
        text-align: left;
    }

    .org-node-link {
        flex-direction: row;
        align-items: center;
        text-align: left;
        gap: 0.65rem;
    }

    .org-avatar {
        margin-bottom: 0;
        width: 38px;
        height: 38px;
    }

    /* display is reset from -webkit-box: the desktop cards clamp to two
       lines, but a full-width mobile row reads better as a single
       ellipsized line. */
    .org-name,
    .org-role,
    .org-meta {
        display: block;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        max-width: 100%;
    }

    .org-name { font-size: 0.92rem; }
    .org-role { font-size: 0.8rem; }
    .org-meta { font-size: 0.74rem; }

    /* The collapse control moves inline, to the right of the row. */
    .org-toggle {
        position: static;
        transform: none;
        margin-left: auto;
        flex-shrink: 0;
        align-self: center;
    }
}

@media print {
    .org-chart-scroll {
        overflow: visible;
        padding: 0;
    }

    /* Print every branch, whatever is collapsed on screen. */
    .org-tree li.collapsed > ul {
        display: flex !important;
    }

    .org-toggle {
        display: none;
    }

    .org-node {
        box-shadow: none;
        break-inside: avoid;
    }
}

/* =========================================================================
   Dashboard
   =========================================================================
   Everything here is scoped to classes only the dashboard uses. The rest of
   the app keeps its restrained white cards - Payroll, Compliance and Assets
   are dense working screens where colour would be noise. The dashboard is the
   landing page, so it is allowed to be bright.
   ========================================================================= */

.dash-page {
    background:
        radial-gradient(1100px 460px at 8% -8%, rgba(99, 102, 241, 0.13), transparent 60%),
        radial-gradient(900px 420px at 96% 4%, rgba(236, 72, 153, 0.11), transparent 60%);
    margin: -1.5rem -1.5rem 0;
    padding: 1.5rem;
}

/* ---------- Stat tiles ---------- */
.stat-tile {
    position: relative;
    overflow: hidden;
    border: 0;
    border-radius: 16px;
    color: #fff;
    box-shadow: 0 10px 24px rgba(16, 24, 40, 0.18);
    transition: transform 0.18s ease, box-shadow 0.18s ease;
}

.stat-tile:hover {
    transform: translateY(-4px);
    box-shadow: 0 16px 32px rgba(16, 24, 40, 0.26);
}

.stat-tile .card-body {
    position: relative;
    z-index: 1;
    padding: 1.25rem 1.35rem;
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 1rem;
}

/* The icon sits in a frosted disc rather than as a faint watermark, which is
   what stops the tile reading as a plain block of colour. */
.stat-chip {
    flex-shrink: 0;
    width: 56px;
    height: 56px;
    border-radius: 16px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    color: #fff;
    background: rgba(255, 255, 255, 0.22);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.28);
}

.stat-tile .stat-value {
    font-size: 2.1rem;
    font-weight: 700;
    line-height: 1.05;
    color: #fff;
    overflow-wrap: anywhere;
}

.stat-tile .stat-label {
    font-size: 0.86rem;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.95);
}

.stat-tile .stat-foot {
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.85);
}

/* A soft highlight across the top so each tile has some depth. */
.stat-tile::after {
    content: '';
    position: absolute;
    inset: 0 0 auto 0;
    height: 45%;
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.18), transparent);
    pointer-events: none;
}

.stat-tile-primary { background: linear-gradient(135deg, #6366f1 0%, #4338ca 55%, #3730a3 100%); }
.stat-tile-success { background: linear-gradient(135deg, #14b8a6 0%, #0d9488 55%, #0f766e 100%); }
.stat-tile-warning { background: linear-gradient(135deg, #fb923c 0%, #f97316 55%, #ea580c 100%); }
.stat-tile-danger  { background: linear-gradient(135deg, #fb7185 0%, #e11d48 55%, #be123c 100%); }
.stat-tile-info    { background: linear-gradient(135deg, #38bdf8 0%, #0ea5e9 55%, #0284c7 100%); }
.stat-tile-purple  { background: linear-gradient(135deg, #c084fc 0%, #a855f7 55%, #7e22ce 100%); }

/* ---------- Panels ---------- */
/* A full coloured header band, not a hairline border. This is what carries
   the colour down the page instead of leaving the lower two thirds white. */
.dash-panel {
    border: 0;
    border-radius: 16px;
    overflow: hidden;
    box-shadow: 0 4px 16px rgba(16, 24, 40, 0.10);
}

.dash-panel > .card-header {
    border: 0;
    padding: 0.85rem 1.15rem;
    color: #fff;
    font-weight: 600;
}

.dash-panel > .card-header h6 {
    color: #fff;
    font-weight: 600;
}

.dash-panel > .card-header i { color: rgba(255, 255, 255, 0.95); }

.dash-head-primary { background: linear-gradient(135deg, #6366f1, #4338ca); }
.dash-head-success { background: linear-gradient(135deg, #14b8a6, #0d9488); }
.dash-head-info    { background: linear-gradient(135deg, #38bdf8, #0284c7); }
.dash-head-purple  { background: linear-gradient(135deg, #c084fc, #7e22ce); }
.dash-head-warning { background: linear-gradient(135deg, #fb923c, #ea580c); }

/* ---------- Activity list ---------- */
/* A coloured initials disc per person turns a grey list into something with
   rhythm, and makes each row scannable at a glance. */
.dash-avatar {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.78rem;
    font-weight: 700;
    color: #fff;
    flex-shrink: 0;
}

.dash-av-0 { background: linear-gradient(135deg, #6366f1, #4338ca); }
.dash-av-1 { background: linear-gradient(135deg, #14b8a6, #0d9488); }
.dash-av-2 { background: linear-gradient(135deg, #fb923c, #ea580c); }
.dash-av-3 { background: linear-gradient(135deg, #fb7185, #e11d48); }
.dash-av-4 { background: linear-gradient(135deg, #38bdf8, #0284c7); }
.dash-av-5 { background: linear-gradient(135deg, #c084fc, #7e22ce); }

.dash-list .list-group-item {
    border-left: 0;
    border-right: 0;
    padding: 0.75rem 1.1rem;
    transition: background 0.15s ease;
}

.dash-list .list-group-item:hover { background: #f7f8fc; }

/* ---------- Quick actions ---------- */
/* Solid colour with white text. The earlier pastel version washed out against
   the card and the icons had nothing to sit on. */
.qa-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 1rem 0.5rem;
    border-radius: 14px;
    text-decoration: none;
    color: #fff;
    font-size: 0.8rem;
    font-weight: 600;
    text-align: center;
    line-height: 1.25;
    box-shadow: 0 4px 12px rgba(16, 24, 40, 0.14);
    transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease;
}

.qa-tile i { font-size: 1.35rem; }

.qa-tile:hover {
    color: #fff;
    transform: translateY(-3px);
    filter: brightness(1.06);
    box-shadow: 0 8px 18px rgba(16, 24, 40, 0.22);
}

.qa-primary { background: linear-gradient(135deg, #6366f1, #4338ca); }
.qa-success { background: linear-gradient(135deg, #14b8a6, #0d9488); }
.qa-warning { background: linear-gradient(135deg, #fb923c, #ea580c); }
.qa-info    { background: linear-gradient(135deg, #38bdf8, #0284c7); }
.qa-purple  { background: linear-gradient(135deg, #c084fc, #7e22ce); }
.qa-danger  { background: linear-gradient(135deg, #fb7185, #e11d48); }

/* ---------- Empty states ---------- */
.card-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    min-height: 170px;
    color: #8a94a6;
    padding: 1.5rem;
}

.card-empty i { font-size: 2rem; opacity: 0.45; margin-bottom: 0.6rem; }
.card-empty p { margin: 0; font-size: 0.88rem; }

/* Stretched panels must let their body grow, or the contents huddle at the
   top of a tall card. Scoped to .dash-panel: applied to every .card.h-100 it
   also caught the stat tiles, whose body is a deliberate row, and stacked
   their icon above the number. */
.dash-panel.h-100 > .card-body { display: flex; flex-direction: column; }
.dash-panel.h-100 > .card-body > .list-group { flex: 1 1 auto; }

/* Chart.js with maintainAspectRatio:false fills its parent, which inside a
   stretched card meant a plot area hundreds of pixels tall. Pin it. */
.chart-box { position: relative; height: 260px; width: 100%; }
.chart-box > canvas { max-height: 260px; }

/* =========================================================================
   Stacked tables on small screens
   =========================================================================
   A ten-column report inside .table-responsive does not actually scroll: the
   cells wrap instead of overflowing, so the table shrinks to fit and every
   column ends up a few characters wide with text running one letter per line.

   Below 768px a `.table-stack` becomes one card per row, each cell labelled
   from its `data-label`. Add the attribute to every `td` or the label is
   simply omitted for that cell, which degrades quietly rather than breaking.
   ========================================================================= */
@media (max-width: 767.98px) {
    /* The wrapper's own horizontal scroll has to go, or the stacked cards
       inherit a scroll context and their values are cut off to the right
       instead of wrapping inside the card. */
    .table-responsive-stack,
    .table-responsive:has(.table-stack) {
        overflow-x: visible;
    }

    .table-stack,
    .table-stack thead,
    .table-stack tbody,
    .table-stack tr,
    .table-stack td {
        display: block;
        width: 100%;
        max-width: 100%;
    }

    /* Headers are redundant once every cell carries its own label. Moved off
       screen rather than display:none so screen readers keep the association. */
    .table-stack thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
    }

    .table-stack tbody tr {
        border: 1px solid #e5e7eb;
        border-radius: 12px;
        padding: 0.35rem 0.15rem;
        margin-bottom: 0.85rem;
        background: #fff;
        box-shadow: 0 1px 3px rgba(16, 24, 40, 0.06);
    }

    .table-stack tbody td {
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
        gap: 0.75rem;
        border: 0;
        border-bottom: 1px solid #f1f3f5;
        padding: 0.5rem 0.85rem;
        text-align: right !important;
        min-height: 0;
        /* The value is an anonymous flex item and so defaults to min-width
           auto, which stops it shrinking below its content and pushes the
           card wider than the screen. These let a long email or department
           name wrap instead. */
        min-width: 0;
        overflow-wrap: anywhere;
        word-break: break-word;
        white-space: normal;
    }

    .table-stack tbody td:last-child { border-bottom: 0; }

    .table-stack tbody td::before {
        content: attr(data-label);
        flex: 0 0 38%;
        min-width: 0;
        overflow-wrap: anywhere;
        text-align: left;
        font-size: 0.78rem;
        font-weight: 600;
        color: #6b7280;
        text-transform: uppercase;
        letter-spacing: 0.02em;
    }

    /* A cell with no label spans the full width - used for the row number and
       for the "no records" message. */
    .table-stack tbody td:not([data-label])::before { content: none; }

    /* The row number becomes a heading strip rather than its own line. */
    .table-stack tbody td[data-label="#"] {
        background: #f8f9fb;
        border-radius: 10px 10px 0 0;
        font-weight: 600;
        color: #374151;
    }

    /* An empty-state row must not be dressed up as a record card. */
    .table-stack tbody tr.is-empty {
        border: 0;
        box-shadow: none;
        background: transparent;
    }

    .table-stack tbody tr.is-empty td {
        text-align: center !important;
        border: 0;
    }

    /* Totals rows read as a summary block, not another record. */
    .table-stack tfoot,
    .table-stack tfoot tr,
    .table-stack tfoot td {
        display: block;
        width: 100%;
    }

    .table-stack tfoot tr {
        border: 2px solid #dee2e6;
        border-radius: 12px;
        margin-top: 0.25rem;
        background: #f8f9fb;
    }

    .table-stack tfoot td {
        display: flex;
        justify-content: space-between;
        gap: 1rem;
        text-align: right !important;
        padding: 0.5rem 0.85rem;
        border: 0;
    }

    .table-stack tfoot td::before {
        content: attr(data-label);
        text-align: left;
        font-weight: 600;
        color: #6b7280;
    }

    /* A cell carrying no value at this size is just an empty row. */
    .table-stack tfoot td:empty { display: none; }

    /* The report toolbars put filters and export buttons on one line, which
       runs off a phone screen. Let them wrap and fill the width instead. */
    .page-header {
        flex-direction: column;
        align-items: stretch;
        gap: 0.75rem;
    }

    .page-header > div:last-child {
        flex-wrap: wrap;
    }

    .page-header form {
        width: 100%;
        flex-wrap: wrap;
    }

    /* The filter selects carry an inline style="width:auto". As flex items
       they also default to min-width:auto, so they refuse to shrink below
       their longest option and two of them together made the page wider than
       a phone screen - which is what was clipping the report tables on the
       right, not the tables themselves. */
    .page-header form .form-select,
    .page-header > div .form-select {
        flex: 1 1 45%;
        min-width: 0;
        width: auto !important;
    }

    /* Nothing in a page toolbar may establish a width the screen cannot
       hold. */
    .page-header,
    .page-header > div,
    .page-header form {
        max-width: 100%;
        min-width: 0;
    }
}

/* =========================================================================
   Stacked tables, second pass
   =========================================================================
   The labels are now applied at runtime by initStackedTables() in app.js,
   which reads them off the header row, so every list view in the app gets
   the card layout without the template having to carry data-label on each
   cell. These rules refine the layout that block above sets up; they come
   later in the file on purpose, so they win without !important.
   ========================================================================= */
@media (max-width: 767.98px) {
    /* A stacked cell is one line: the label on the left, the value flowing to
       the right.

       The cell is not a flex container. Flex turned every child into its own
       item, so a cell holding a name, a badge and a sub-line had its three
       pieces spread apart across the row instead of reading as one value.

       The label is floated rather than absolutely positioned so that it still
       contributes to the cell's height: "Parent Department" and "Employee
       Count" wrap to two lines on a phone, and an out-of-flow label left them
       overlapping the row below. ::after clears the float so the cell grows
       to whichever side is taller. */
    .table-stack tbody td,
    .table-stack tfoot td {
        display: block;
        padding: 0.45rem 0.85rem;
        text-align: right !important;
        min-height: 0;
    }

    .table-stack tbody td::before,
    .table-stack tfoot td::before {
        float: left;
        width: 36%;
        margin-right: 0.75rem;
        text-align: left;
        white-space: normal;
        line-height: 1.4;
        padding-top: 0.1rem;
    }

    .table-stack tbody td::after,
    .table-stack tfoot td::after {
        content: '';
        display: block;
        clear: both;
    }

    /* A value built as a flex row (avatar plus name) is a block-level box, so
       it sits beside the float rather than under it, and should finish on the
       same right edge as every plain-text value. Cells that already state
       their own alignment keep it - the permission matrix centres its
       checkboxes. */
    .table-stack tbody td > .d-flex:not([class*="justify-content"]) {
        justify-content: flex-end;
    }

    /* Striping and hover tint are drawn by Bootstrap as an inset box-shadow
       over the whole cell. On a stacked card that paints a grey block over
       the row instead of banding a table, so both are switched off here. */
    .table-stack > tbody > tr > td {
        background-color: transparent;
        box-shadow: none;
    }

    /* Restated after the reset above: the row number reads as the card's
       heading strip rather than as another field. */
    .table-stack tbody td[data-label="#"] {
        background: #f8f9fb;
        border-radius: 10px 10px 0 0;
        font-weight: 600;
        color: #374151;
        min-height: 0;
    }

    .table-stack tbody tr.is-empty td {
        padding: 1.5rem 0.85rem;
        text-align: center !important;
    }

    /* Action buttons get their own full-width strip at the foot of the card.
       Squeezed into the right-hand 60% they were a row of icons a thumb
       could not reliably hit. */
    .table-stack tbody td[data-label="Actions"],
    .table-stack tbody td[data-label="Action"] {
        text-align: left !important;
        background: #fbfcfd;
        border-radius: 0 0 10px 10px;
        border-bottom: 0;
    }

    .table-stack tbody td[data-label="Actions"]::before,
    .table-stack tbody td[data-label="Action"]::before {
        float: none;
        display: block;
        width: auto;
        margin: 0 0 0.35rem;
    }

    .table-stack tbody td[data-label="Actions"] .btn,
    .table-stack tbody td[data-label="Action"] .btn {
        min-height: 38px;
        min-width: 44px;
    }

    .table-stack tbody td[data-label="Actions"] .btn-group,
    .table-stack tbody td[data-label="Action"] .btn-group {
        vertical-align: top;
    }

    /* Most list views wrap the table in a .card-body.p-0, which leaves the
       cards touching the card edge with nothing to separate them from it. */
    .card-body.p-0 > .table-responsive-stack,
    .card-body.p-0 > .table-stack {
        padding: 0.75rem;
    }

    /* Bootstrap sizes columns from the header row's width attribute; with the
       header off screen those widths would still constrain the cells. */
    .table-stack tbody td[width],
    .table-stack thead th[width] {
        width: auto !important;
    }
}

/* =========================================================================
   Page toolbars
   =========================================================================
   Nearly every list view opens with a title on the left and one to three
   buttons on the right. On a phone that row either overflows or squeezes the
   heading to a character per line, so below 576px the buttons drop to their
   own full-width row.
   ========================================================================= */
.page-toolbar {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1.5rem;
}

.page-toolbar > .page-toolbar-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

@media (max-width: 575.98px) {
    .page-toolbar > .page-toolbar-actions {
        width: 100%;
    }

    .page-toolbar > .page-toolbar-actions > .btn,
    .page-toolbar > .page-toolbar-actions > form,
    .page-toolbar > .page-toolbar-actions > .btn-group {
        flex: 1 1 auto;
    }

    /* When an action is a <form> wrapping a button - Approve Batch, Email
       Payslips - the form is the flex item, so the button inside it must be
       told to fill the form's width or it sits content-sized in a full-width
       row. Left untouched on desktop, where the form is content-sized too. */
    .page-toolbar > .page-toolbar-actions > form > .btn {
        width: 100%;
    }

    /* Views that have not been converted to .page-toolbar still get the
       same treatment from their Bootstrap utility classes. */
    .main-content > .d-flex.justify-content-between > div:last-child {
        display: flex;
        flex-wrap: wrap;
        gap: 0.5rem;
        width: 100%;
    }

    .main-content > .d-flex.justify-content-between > div:last-child > .btn {
        flex: 1 1 auto;
        margin-right: 0 !important;
    }

    /* Card footers pair a "showing 1 to 20 of 340" line with the pager. */
    .card-footer > .d-flex.justify-content-between {
        flex-direction: column;
        align-items: center !important;
        gap: 0.5rem;
    }
}

/* A filter card is a grid of selects; on a phone each one owns its own row,
   and the Filter/Clear pair below them should fill the width. */
@media (max-width: 767.98px) {
    .card form.row > [class*="col-md-"] .btn {
        width: 100%;
        margin-right: 0 !important;
    }

    .card form.row > [class*="col-md-"] .btn + .btn,
    .card form.row > [class*="col-md-"] .btn + .a {
        margin-top: 0.5rem;
    }
}

/* =========================================================================
   Upload progress
   =========================================================================
   Driven by handleUploadSubmit() in app.js. A single panel is reused for
   every form on the page, docked to the bottom of the viewport so it is
   visible whatever the form was scrolled to.
   ========================================================================= */
.upload-progress {
    position: fixed;
    left: 50%;
    bottom: 1rem;
    z-index: 2050;
    width: 420px;
    max-width: calc(100vw - 1.5rem);
    padding: 0.75rem 0.9rem;
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(16, 24, 40, 0.18);
    /* Parked below the fold and inert until a request actually starts, so it
       never intercepts a tap on whatever is underneath it. */
    transform: translate(-50%, 160%);
    opacity: 0;
    visibility: hidden;
    transition: transform 0.25s ease, opacity 0.25s ease, visibility 0.25s;
}

.upload-progress.is-visible {
    transform: translate(-50%, 0);
    opacity: 1;
    visibility: visible;
}

.upload-progress-head {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.9rem;
    color: #111827;
}

.upload-progress-head i {
    color: #0d6efd;
}

.upload-progress-title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.upload-progress-bar {
    height: 8px;
    margin-top: 0.55rem;
    border-radius: 4px;
    background-color: #eef0f3;
}

.upload-progress-meta {
    margin-top: 0.4rem;
    font-size: 0.75rem;
    color: #6b7280;
}

.upload-progress.is-error {
    border-color: #f1aeb5;
}

.upload-progress.is-error .upload-progress-head i {
    color: #dc3545;
}

@media print {
    .upload-progress {
        display: none !important;
    }
}

/* =========================================================================
   Wide report matrices
   =========================================================================
   The payroll booking summary puts every component in its own column, so it
   is wider than any screen and scrolls sideways. Without pinning the first
   two columns you lose track of whose row you are reading by the time you
   reach the deductions, which for a money report is the whole problem.

   Only Code and Name are pinned: enough to identify the row, narrow enough
   to leave the figures room. `left` has to be a fixed value rather than
   `auto`, so the two widths below and the offset are one set of numbers.
   ========================================================================= */
/* Pinned by an explicit class, not by nth-child. nth-child counts CELLS, not
   columns: in the totals row the first cell spans seven columns, so the
   "second cell" is the first money figure - and pinning that put the Basic
   Salary total under the Name column with a white background. The view marks
   the two cells it wants pinned and nothing else can be caught by accident. */
.report-matrix .pin-1,
.report-matrix .pin-2 {
    position: sticky;
    z-index: 2;
    background-color: #fff;
}

.report-matrix .pin-1 { left: 0;     width: 110px; min-width: 110px; }
.report-matrix .pin-2 { left: 110px; width: 170px; min-width: 170px; }

.report-matrix thead .pin-1,
.report-matrix thead .pin-2 { background-color: #f8f9fa; z-index: 4; }

/* Hover paints over a sticky cell's own background, which would otherwise
   leave the pinned columns transparent with the figures showing through. */
.report-matrix tbody tr:hover .pin-1,
.report-matrix tbody tr:hover .pin-2 { background-color: #f2f4f7; }

/* A cell scrolling under the pinned pair needs a visible edge to pass behind. */
.report-matrix .pin-2::after {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 1px;
    background: #dee2e6;
}

/* The matrix is the point of the page when it is printed. */
@media print {
    .report-matrix { font-size: 0.7rem; }
    .report-matrix th, .report-matrix td { padding: 2px 4px !important; }
}

/* =========================================================================
   Payroll booking summary on a phone
   =========================================================================
   The pinned matrix above works from a tablet upwards. On a phone it does
   not: Code and Name cost 280px of a 360px screen, so a booking sheet that
   is eighteen columns wide before the company adds a component of its own
   shows about one column of figures at a time.

   Below 768px the view draws the same figures as one collapsed card per
   employee instead - grouped into earnings, deductions and employer
   contributions, the way the matrix groups them, rather than the flat run of
   twenty labelled lines the generic table stacker would produce. The grid is
   still one tap away for anyone who would rather swipe a spreadsheet.

   These rules come after the .report-matrix block on purpose: a media query
   adds no specificity, so a responsive override placed above the rule it
   overrides simply loses.
   ========================================================================= */

.booking-cards {
    padding: 0.5rem;
}

.booking-item {
    border: 1px solid #e9ecef;
    border-radius: 0.5rem;
    margin-bottom: 0.5rem;
    background: #fff;
}

.booking-item[open] {
    border-color: #ced4da;
}

.booking-item > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.625rem 0.75rem;
    cursor: pointer;
    list-style: none;
}

/* Safari and older Chrome draw their own triangle, which lands in the middle
   of the employee name. The chevron below replaces it. */
.booking-item > summary::-webkit-details-marker {
    display: none;
}

.booking-item > summary::after {
    content: '\f078';                       /* Font Awesome chevron-down */
    font-family: 'Font Awesome 6 Free', 'Font Awesome 5 Free';
    font-weight: 900;
    font-size: 0.7rem;
    color: #adb5bd;
    transition: transform 0.15s ease-in-out;
}

.booking-item[open] > summary::after {
    transform: rotate(180deg);
}

.booking-who {
    display: flex;
    flex-direction: column;
    min-width: 0;                           /* so a long name truncates */
    gap: 0.1rem;
}

.booking-who > strong {
    font-size: 0.9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.booking-sub {
    font-size: 0.75rem;
    color: #6c757d;
}

.booking-amount {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    margin-left: auto;
    font-weight: 600;
    font-size: 0.9rem;
    white-space: nowrap;
}

.booking-amount-label {
    font-size: 0.68rem;
    font-weight: 400;
    color: #6c757d;
}

.booking-item-total {
    border-color: #0d6efd;
    background: #f8fbff;
}

.booking-item-total .booking-amount {
    color: #0d6efd;
}

.booking-body {
    padding: 0 0.75rem 0.625rem;
    border-top: 1px solid #f1f3f5;
}

.booking-group {
    margin-top: 0.625rem;
    padding-bottom: 0.2rem;
    font-size: 0.68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #6c757d;
}

.booking-line {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.28rem 0;
    font-size: 0.82rem;
    border-bottom: 1px solid #f8f9fa;
}

.booking-line > span:last-child {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.booking-line-sum {
    font-weight: 600;
    border-bottom-color: #dee2e6;
}

.booking-line-total {
    margin-top: 0.3rem;
    padding-top: 0.4rem;
    border-top: 2px solid #dee2e6;
    border-bottom: none;
    font-weight: 700;
}

/* The grid has to take its natural width, not the container's.
   A Bootstrap .table is width:100%, so inside .table-responsive it squeezed
   eighteen columns into whatever space there was instead of overflowing:
   at 390px the headers came out one letter per line and "150,000.00" wrapped
   into a vertical column of digits. It never actually scrolled sideways -
   which is what the pinned Code and Name columns were there to support.

   width:auto with min-width:100% keeps a narrow report filling the card while
   letting a wide one grow past it, and nowrap stops a cell buying width back
   by wrapping. The overflow then belongs to .table-responsive, which scrolls. */
.report-matrix {
    width: auto;
    min-width: 100%;
}

.report-matrix th,
.report-matrix td {
    white-space: nowrap;
}

/* The Cards/Grid switch. Both layouts are rendered; this picks one. The
   cards carry .d-md-none, so from 768px up the grid is the only layout and
   the switch itself is hidden. */
@media (max-width: 767.98px) {
    .booking-matrix-card[data-view="cards"] .booking-grid {
        display: none;
    }

    .booking-matrix-card[data-view="matrix"] .booking-cards {
        display: none !important;
    }

    /* In grid mode on a phone, the pinned pair has to give up some width or
       there is nothing left for the figures. */
    .report-matrix { font-size: 0.72rem; }
    .report-matrix th,
    .report-matrix td { padding: 0.25rem 0.35rem; }
    .report-matrix .pin-1 { width: 68px;  min-width: 68px; }
    .report-matrix .pin-2 { left: 68px; width: 104px; min-width: 104px; }
}

/* Tablets and small laptops keep the grid but do not need the full 280px. */
@media (min-width: 768px) and (max-width: 991.98px) {
    .report-matrix { font-size: 0.8rem; }
    .report-matrix .pin-1 { width: 88px;  min-width: 88px; }
    .report-matrix .pin-2 { left: 88px; width: 132px; min-width: 132px; }
}

/* Two tiles across is the house style on a phone, but these four hold a whole
   company's payroll: "1,351,500.00" at the default tile size breaks after the
   rupee sign and again mid-number. Shrink the figure to fit the half-width
   tile in one piece, and drop the hint line - the journal and payables cards
   further down say the same thing in full. */
@media (max-width: 575.98px) {
    .booking-tiles .stat-value {
        font-size: 0.98rem;
        white-space: nowrap;
    }

    .booking-tiles .stat-label {
        font-size: 0.68rem;
    }

    .booking-tiles .stat-card > .small {
        display: none;
    }
}

/* The journal and the payables list are narrow enough to keep as tables at
   any width. What they must not do is wrap an amount: in a two-column card
   the net pay total broke after the rupee sign and put the figure on the next
   line, which reads as two different numbers. */
.booking-summary-table th.text-end,
.booking-summary-table td.text-end {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

@media (max-width: 575.98px) {
    .booking-summary-table th,
    .booking-summary-table td { font-size: 0.8rem; }
}

/* Printing a booking sheet means printing the grid, whichever layout the
   screen happens to be showing. */
@media print {
    .booking-cards { display: none !important; }
    .booking-matrix-card .booking-grid { display: block !important; }
}

/* =========================================================================
   Collapsible filter panels
   =========================================================================
   A list view's filter card is four or five controls on one row. On a
   desktop it is a strip above the table; on a phone each control owns a row
   and the panel is most of the screen before a single record is visible -
   the employee master list put five of them above the first employee.

   Below 768px the panel collapses behind a toggle. The mechanism is
   Bootstrap's own: `.collapse` hides the body, and `.d-md-block` (a display
   utility, so `!important`) forces it back open from 768px up, where the
   toggle itself is hidden by `.d-md-none`. Nothing is toggled on a desktop,
   so there is no state to get wrong at that width.

   A view renders the panel open when a filter is already applied - a
   filtered list that looks like the whole list is worse than a tall panel.
   ========================================================================= */
.filter-card > .filter-card-head {
    border-bottom: 1px solid rgba(0, 0, 0, 0.075);
}

.filter-toggle {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 0.75rem 1rem;
    border: 0;
    background: none;
    font-weight: 600;
    font-size: 0.95rem;
    color: #374151;
    text-align: left;
    /* A filter panel is opened with a thumb. */
    min-height: 48px;
}

.filter-toggle:focus-visible {
    outline: 2px solid #667eea;
    outline-offset: -2px;
}

.filter-toggle-caret {
    color: #9ca3af;
    transition: transform 0.2s ease;
}

/* Bootstrap puts .collapsed on the toggle while its target is closed, so the
   caret points down when there is more to see and up when there is not. */
.filter-toggle.collapsed .filter-toggle-caret {
    transform: rotate(0deg);
}

.filter-toggle:not(.collapsed) .filter-toggle-caret {
    transform: rotate(180deg);
}

/* The head only exists on small screens; without this the card would keep a
   stray border above its body from 768px up. */
@media (min-width: 768px) {
    .filter-card > .filter-card-head {
        display: none;
    }
}

/* =========================================================================
   Line-item forms in a table
   =========================================================================
   The generic .table-stack layout floats each cell's label into the left 36%
   and lets the value flow to its right. That works for text; it does not
   work for a form control, which is a block box at width:100% and so sits
   underneath the float, overlapping it.

   A .form-line-table carries data-no-stack to opt out of that layout and
   gets this one instead: below 768px each row becomes a card and each cell
   puts its label above a full-width control.
   ========================================================================= */
@media (max-width: 767.98px) {
    .form-line-table,
    .form-line-table tbody,
    .form-line-table tfoot,
    .form-line-table tr,
    .form-line-table td {
        display: block;
        width: 100%;
        max-width: 100%;
    }

    /* Off screen rather than display:none, so the cells keep their
       association with a header for a screen reader. */
    .form-line-table thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
    }

    .form-line-table tbody tr {
        border: 1px solid #e5e7eb;
        border-radius: 12px;
        padding: 0.5rem 0.35rem;
        margin-bottom: 0.85rem;
        background: #fff;
        box-shadow: 0 1px 3px rgba(16, 24, 40, 0.06);
    }

    .form-line-table tbody td {
        border: 0;
        padding: 0.35rem 0.6rem;
    }

    .form-line-table tbody td::before {
        content: attr(data-label);
        display: block;
        margin-bottom: 0.2rem;
        font-size: 0.72rem;
        font-weight: 600;
        color: #6b7280;
        text-transform: uppercase;
        letter-spacing: 0.02em;
    }

    .form-line-table tbody td:not([data-label])::before {
        content: none;
    }

    /* Removing a line is a destructive action reached with a thumb, so it
       gets a full-width labelled button rather than a 24px icon. */
    .form-line-table tbody td.line-actions .btn {
        width: 100%;
        min-height: 40px;
    }

    .form-line-table tfoot tr {
        display: flex;
        justify-content: space-between;
        gap: 1rem;
        border: 2px solid #dee2e6;
        border-radius: 12px;
        padding: 0.5rem 0.85rem;
        background: #f8f9fb;
    }

    .form-line-table tfoot td {
        width: auto;
        border: 0;
        padding: 0;
    }

    /* Two empty cells keeping the row's column count would show as blank
       gaps in a flex row. */
    .form-line-table tfoot td:empty {
        display: none;
    }

    /* A stacked cell holding a file input and its upload button - the
       receipt column on a claim. Same problem as the form tables above: the
       floated label and a full-width control cannot share a line. */
    .table-stack tbody td[data-label="Receipt"] {
        text-align: left !important;
    }

    .table-stack tbody td[data-label="Receipt"]::before {
        float: none;
        display: block;
        width: auto;
        margin: 0 0 0.35rem;
    }

    .table-stack tbody td[data-label="Receipt"] .receipt-upload .btn {
        min-height: 38px;
        min-width: 44px;
    }
}

/* =========================================================================
   Payroll batch summary tiles
   =========================================================================
   The four tiles are two-up on a phone (col-6). A whole batch's gross can be
   "13,51,500.00", which at the default h3 size broke after the rupee sign and
   again mid-number inside a ~150px tile - three lines reading as three
   figures. Keep it on one line and shrink it to fit the tile.
   ========================================================================= */
.batch-figure {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

@media (max-width: 575.98px) {
    .batch-figure {
        font-size: 1.15rem;
    }
}
