:root {
    --brand: #7c3aed;
    --brand-dark: #6d28d9;
    --brand-darker: #4c1d95;
    --brand-light: #f3effc;
    --sidebar-w: 240px;
    --sidebar-w-collapsed: 68px;
    --ink: #1e1b2e;
    --ink-muted: #6b6478;
    --border-soft: #ebe6f5;
}

body {
    background: #f6f5fa;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-size: .9rem;
    color: var(--ink);
}

h1, h2, h3, h4, h5, h6, .fw-bold, .fw-semibold { letter-spacing: -.01em; }

/* Consistent page-title row used at the top of every staff page */
.page-heading { margin-bottom: 1.25rem; }
.page-heading h4 { margin-bottom: .15rem; }
.page-heading .text-muted { font-size: .825rem; }

.topbar {
    background: #ffffff;
    color: var(--brand-darker);
    height: 56px;
    position: sticky;
    top: 0;
    z-index: 1040;
    border-bottom: 1px solid var(--border-soft);
    box-shadow: 0 1px 2px rgba(76,29,149,.04);
}
.topbar .navbar-brand, .btn-topbar { color: var(--brand-darker); }
.topbar .navbar-brand { font-size: 1.02rem; }
.btn-topbar { background: transparent; border: none; }
.btn-topbar:hover { color: var(--brand-darker); background: rgba(124,58,237,.08); }

/* Initials avatar in the topbar user menu - deterministic color from the
   name so the same person always gets the same tint, no image/upload needed. */
.avatar-initials {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: .78rem;
    font-weight: 700;
    color: #fff;
    background: var(--brand);
    flex-shrink: 0;
}

.app-shell {
    display: flex;
    min-height: calc(100vh - 56px);
}

.sidebar {
    width: var(--sidebar-w);
    flex-shrink: 0;
    background: #ffffff;
    color: #3b0764;
    position: sticky;
    top: 56px;
    align-self: flex-start;
    height: calc(100vh - 56px);
    overflow-y: auto;
    overflow-x: hidden;
    border-right: 1px solid var(--border-soft);
    transition: width .18s ease;
}
.sidebar .nav-section-label {
    font-size: .68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: #a89bc2;
    padding: .9rem .9rem .3rem;
    white-space: nowrap;
}
.sidebar .nav-section-label:first-child { padding-top: .6rem; }
.sidebar .nav-link {
    color: #4a3f5c;
    border-radius: 0;
    padding: .5rem .9rem .5rem 1rem;
    margin-bottom: 1px;
    font-size: .87rem;
    border-left: 3px solid transparent;
    white-space: nowrap;
    display: flex;
    align-items: center;
}
.sidebar .nav-link i { width: 1.4rem; display: inline-block; font-size: .95rem; }
.sidebar .nav-link:hover { background: var(--brand-light); color: var(--brand-darker); }
.sidebar .nav-link.active {
    background: var(--brand-light);
    color: var(--brand-darker);
    font-weight: 600;
    border-left-color: var(--brand);
}
.sidebar .nav-link.settings-open { background: var(--brand-light); color: var(--brand-darker); font-weight: 600; }
.sidebar hr { border-color: var(--border-soft); }

/* Desktop collapse: icon-only rail, labels/section headers hidden, tooltips
   via the native title attribute fall back for discoverability. State is
   persisted in localStorage (see app.js) so it survives a page reload. */
@media (min-width: 992px) {
    .sidebar.collapsed { width: var(--sidebar-w-collapsed); }
    .sidebar.collapsed .nav-section-label,
    .sidebar.collapsed .nav-link span,
    .sidebar.collapsed .nav-link .badge,
    .sidebar.collapsed .nav-link .bi-chevron-down { display: none !important; }
    .sidebar.collapsed .nav-link { justify-content: center; padding-left: .9rem; }
    .sidebar.collapsed #settingsSubmenu { display: none !important; }
}
#sidebarCollapseToggle { color: #a89bc2; }
#sidebarCollapseToggle:hover { color: var(--brand-darker); background: var(--brand-light); }

.content { flex-grow: 1; min-width: 0; }

@media (max-width: 991.98px) {
    .sidebar {
        position: fixed;
        left: -260px;
        top: 56px;
        z-index: 1035;
        transition: left .2s ease;
        box-shadow: 2px 0 8px rgba(0,0,0,.15);
    }
    .sidebar.show { left: 0; }
}

.card { border: 1px solid var(--border-soft); border-radius: .65rem; box-shadow: 0 1px 3px rgba(30,27,46,.04); }
.card-header { background: #fff; border-bottom: 1px solid var(--border-soft); font-size: .875rem; padding: .75rem 1.1rem; }

/* Hover-lift for cards that lead somewhere (dashboard stat cards, clickable
   summary cards) - not applied to plain content cards, which shouldn't imply
   interactivity they don't have. */
.card-elevated { transition: transform .12s ease, box-shadow .12s ease; }
.card-elevated:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(76,29,149,.09); }

.stat-card { border-radius: .7rem; }
.stat-card .stat-value { font-size: 1.6rem; font-weight: 700; line-height: 1.2; }
.stat-card .stat-icon-circle {
    width: 42px;
    height: 42px;
    border-radius: .6rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    flex-shrink: 0;
}
/* Named to match the same semantic meaning already used nearby on each
   card's own .stat-value (text-danger/text-info/text-warning/text-secondary)
   - previously each icon circle had its own one-off inline hex pair here,
   a private palette that didn't reuse either these tokens or the shared
   --brand variables used everywhere else in the app. */
.stat-icon-circle.success { background: #e7f6ee; color: #1e8a52; }
.stat-icon-circle.danger { background: #fbe9e7; color: #c0392b; }
.stat-icon-circle.info { background: #e6f3fb; color: #1d5fa8; }
.stat-icon-circle.warning { background: #fdf3e2; color: #b8860b; }
.stat-icon-circle.secondary { background: #eef0f3; color: #6c757d; }
.stat-icon-circle.teal { background: #e3f5f2; color: #0f8a7d; }
.stat-icon-circle.brand { background: var(--brand-light); color: var(--brand); }

/* Brand (purple) reskin for primary actions - kept separate from semantic status colors below */
.btn-success { background-color: var(--brand); border-color: var(--brand); }
.btn-success:hover, .btn-success:focus { background-color: var(--brand-dark); border-color: var(--brand-dark); }
.btn-outline-success { color: var(--brand); border-color: var(--brand); }
.btn-outline-success:hover, .btn-outline-success:focus { background-color: var(--brand); border-color: var(--brand); color: #fff; }
.btn-check:checked + .btn-outline-success,
.btn-check:active + .btn-outline-success,
.btn-outline-success.active { background-color: var(--brand); border-color: var(--brand); color: #fff; }
a { color: var(--brand-dark); }
.nav-tabs .nav-link.active { color: var(--brand-darker); border-bottom-color: var(--brand); font-weight: 600; }
.form-check-input:checked { background-color: var(--brand); border-color: var(--brand); }

/* All badges show a capitalized first letter regardless of the raw value's case */
.badge { text-transform: capitalize; }

/* Semantic status colors - deliberately NOT tied to brand, so meaning stays clear */
.badge-condition-good, .badge-condition-new { background: #2f6f4f; }
.badge-condition-fair { background: #b8860b; }
.badge-condition-damaged, .badge-condition-lost { background: #b3382c; }

.badge-type-tool { background: #1d5fa8; }
.badge-type-consumable { background: #0f8a7d; }
.badge-type-spare { background: var(--brand); }
.type-tool-border { border-left: 3px solid #1d5fa8; }
.type-consumable-border { border-left: 3px solid #0f8a7d; }
.type-spare-border { border-left: 3px solid var(--brand); }

table.dataTable thead th { border-bottom-width: 1px; white-space: nowrap; font-size: .78rem; text-transform: uppercase; letter-spacing: .03em; color: var(--ink-muted); }
.dataTables_wrapper .dataTables_info, .dataTables_wrapper .dataTables_length, .dataTables_wrapper .dataTables_paginate { font-size: .82rem; color: var(--ink-muted); }
.dataTables_wrapper .dataTables_length select {
    display: inline-block;
    width: auto;
    min-width: 4.5rem;
    padding: .25rem 1.75rem .25rem .55rem;
    border-radius: .35rem;
    border-color: #d6cdef;
    vertical-align: middle;
}

/* Compact, separated "pill" pagination buttons instead of Bootstrap's default
   connected/oversized pagination bar, which read too heavy against this
   page's smaller type scale and whose light borders blended into the white
   card background. */
.dataTables_wrapper .pagination { gap: .2rem; flex-wrap: wrap; }
.dataTables_wrapper .page-link {
    margin-left: 0 !important;
    padding: .25rem .6rem;
    font-size: .8rem;
    min-width: 1.9rem;
    text-align: center;
    border-radius: .35rem !important;
    border: 1px solid var(--border-soft);
    color: var(--brand-dark);
    background: #fff;
}
.dataTables_wrapper .page-link:hover { background: var(--brand-light); border-color: var(--brand-light); color: var(--brand-darker); }
.dataTables_wrapper .page-item.active .page-link { background-color: var(--brand); border-color: var(--brand); color: #fff; }
.dataTables_wrapper .page-item.disabled .page-link { color: #c7c2d6; background: #fff; border-color: var(--border-soft); }

/* Shared loading/empty row markup (see loadingRow()/emptyRow() in app.js) */
.table-state-row { text-align: center; color: var(--ink-muted); padding: 2.5rem 1rem; }
.table-state-row .spinner-border { width: 1.5rem; height: 1.5rem; }
.table-state-row i { font-size: 1.6rem; opacity: .4; display: block; margin-bottom: .35rem; }
.thumb-sm { width: 42px; height: 42px; object-fit: cover; border-radius: .35rem; }

/* Scan QR camera preview (store/includes/store_header.php's #scanQrReader,
   loans.php's #loanScanQrReader) - html5-qrcode only sizes the scan-target
   overlay box, not this container, so without a reserved aspect-ratio the
   modal visibly jumps once the camera stream attaches. Forced to a 1:1
   square (matching the square-ish qrbox scan-target overlay) rather than
   whatever aspect ratio the camera's own stream happens to be (commonly
   4:3 or 16:9) - object-fit:cover on the video crops to fill it instead of
   letterboxing. width/height need !important - html5-qrcode sets its own
   inline style="width:...;height:...;" on the <video> it injects, which
   would otherwise win over a plain stylesheet rule (confirmed empirically,
   not assumed - a first version without !important left the video at its
   library-assigned size instead of the intended square crop).

   The CONTAINER itself needs the same !important treatment on width/height
   (not just aspect-ratio) - html5-qrcode also sets an inline height on this
   element sized to the actual camera stream's native aspect ratio, and an
   explicit inline height beats a plain (non-!important) aspect-ratio rule
   regardless of specificity, since aspect-ratio only fills in a dimension
   that's still auto. A landscape desktop webcam's computed height happens
   to be close enough to the modal's rendered width that the square crop
   still looked right by coincidence; a phone's portrait rear camera (native
   height >> width) makes the mismatch obvious - the container ends up tall
   and narrow instead of square, and the video's own !important 100%/100%
   just fills THAT shape. Forcing height back to auto here restores the
   aspect-ratio rule below regardless of what the library sets. */
#scanQrReader, #loanScanQrReader {
    width: 100% !important;
    height: auto !important;
    aspect-ratio: 1 / 1;
    border-radius: .5rem;
    overflow: hidden;
    background: #000;
}
#scanQrReader video, #loanScanQrReader video {
    border-radius: .5rem;
    width: 100% !important;
    height: 100% !important;
    object-fit: cover;
}
#thumbHoverPreview {
    position: fixed;
    display: none;
    max-width: 320px;
    max-height: 320px;
    border-radius: .5rem;
    border: 3px solid #fff;
    box-shadow: 0 10px 30px rgba(0,0,0,.4);
    z-index: 3000;
    pointer-events: none;
}
.thumb-preview { max-width: 100%; max-height: 180px; border-radius: .4rem; }

/* Take Photo / Choose Photo / Remove widget auto-built around every
   accept="image/*" file input (see app.js) - a bordered preview box with
   its controls grouped directly underneath, in the spirit of
   https://nerdy-harry.github.io/bootstrap-imageupload/ */
.photo-picker { display: inline-flex; flex-direction: column; gap: .4rem; }
.photo-picker-box {
    /* Stretches to match the camera/gallery/remove button row beneath it
       (the .photo-picker column's actual width driver, ~103px with all 3
       buttons) rather than a fixed 96px - so the box's right edge lines up
       with the row's (both already share the same left edge), instead of
       falling short of it. */
    width: 100%;
    height: 96px;
    border: 1px solid #d6cdef;
    border-radius: .5rem;
    background: #fbfafd;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    position: relative;
}
.photo-picker-box .photo-picker-img.thumb-preview {
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    object-fit: cover;
    border-radius: 0;
}
/* Overlaid on the preview itself (not a 4th button in the row below) so an
   already-uploaded photo can be rotated without shifting the widget's
   layout - see app.js's rotate handler. Only shown while there's an actual
   photo to rotate (synced alongside the placeholder icon/Remove button). */
.photo-picker-rotate-btn {
    position: absolute;
    top: 3px;
    right: 3px;
    width: 20px;
    height: 20px;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, .55);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: .7rem;
    line-height: 1;
    padding: 0;
    cursor: pointer;
}
.photo-picker-rotate-btn:hover { background: rgba(0, 0, 0, .75); }
/* The column wrapping a modal's photo field (see bins.php, loans.php, etc.) -
   pinned to a fixed width matching the picker box, so a longer label like
   "Photo (override)" wraps onto two lines instead of stretching the column
   (and the fields beside it) wider than the compact 96px box needs. 120px,
   not 100px - the photo-picker widget's camera/gallery/remove button row
   (app.js) measures ~103px wide with all three buttons visible (Remove
   only shows once a photo is set), which overflowed a 100px column by
   several px into the field beside it - confirmed via direct measurement
   in a real Edit Item modal, not just visual inspection. */
.modal-photo-col { flex: 0 0 120px; max-width: 120px; }

/* To Do's rich-text task input (todo.php) - a contenteditable div styled to
   look like a normal .form-control, with a CSS-only placeholder (:empty
   only matches when there's truly no child content/whitespace, so typing
   and deleting back to nothing correctly brings it back). */
.todo-editable { flex: 1 1 auto; min-height: 38px; }
.todo-editable:empty::before { content: attr(data-placeholder); color: #6c757d; }
.todo-color-swatch {
    width: 20px; height: 20px; border-radius: 50%; border: 1px solid rgba(0,0,0,.15);
    padding: 0; cursor: pointer;
}
.todo-color-swatch:hover { transform: scale(1.15); }
.tree-toggle { cursor: pointer; color: var(--brand-dark); }
.subrow, .subitem-row { background: #fbfafd; }
.subitem-of-kit { background: #fff8e6; }
/* Compact modals site-wide: tighter padding/spacing so forms fit without scrolling */
.modal-header, .modal-footer { padding: .6rem 1.1rem; }
.modal-body { padding: .85rem 1.1rem; }
.modal .form-label { margin-bottom: .2rem; font-size: .825rem; font-weight: 600; }
.modal .form-text { font-size: .75rem; margin-top: .15rem; }
.modal .row.g-2 { row-gap: .5rem !important; }
.modal hr { margin: .6rem 0; }
.modal h6 { font-size: .85rem; margin-bottom: .5rem; }
.modal .nav-tabs { margin-bottom: .6rem !important; }

/* AJAX search combobox (createSearchSelect in app.js) */
.search-select { position: relative; }
.search-select-menu {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1060;
    max-height: 220px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid #d6cdef;
    border-radius: .4rem;
    box-shadow: 0 8px 20px rgba(0,0,0,.15);
    margin-top: 2px;
}
.search-select-menu.show { display: block; }
.search-select-item { padding: .4rem .65rem; cursor: pointer; font-size: .875rem; }
.search-select-item:hover { background: var(--brand-light); color: var(--brand-darker); }
.lot-radio-row:hover { background: var(--brand-light); }

/* Fillable/selectable fields stay clearly white/interactive; only real
   disabled/readonly fields get the muted grey, so the two are never confused */
.form-control, .form-select {
    background-color: #fff;
    border-color: #d6cdef;
}
.form-control:focus, .form-select:focus {
    border-color: var(--brand);
    box-shadow: 0 0 0 .2rem rgba(124,58,237,.15);
}
.form-control:disabled, .form-control[readonly], .form-select:disabled {
    background-color: #eef0f3;
    color: #8a8f98;
}

/* Loan/Issue slip modal (assets/js/slip_modal.js, opened via openLoanSlip()/
   openIssueSlip() from anywhere in the app) - styling for the slip content
   rendered inside #slipModal (see includes/footer.php). */
.slip-header { border-bottom: 2px solid var(--brand); padding-bottom: .75rem; margin-bottom: 1rem; }
.slip-section-title { font-size: .8rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--brand-darker); margin-bottom: .5rem; }
.slip-field-label { font-size: .75rem; color: #6c757d; margin-bottom: 0; }
.slip-field-value { font-weight: 600; }

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

/* Bulk slip printing (Print Selected on loans.php/issues.php, see
   bulkPrintSlips() in slip_modal.js): same page-isolation trick as the
   single-slip modal above, but the content itself is a grid of 6
   business-card-sized slips per A4 sheet (2 cols x 3 rows) instead of one
   full-size slip - "1/6 of A4" per printed entry. */
body.printing-bulk > *:not(#bulkPrintArea) { display: none !important; }
body.printing-bulk #bulkPrintArea { display: block !important; }
@media print {
    @page { size: A4; margin: 0; }
    body.printing-bulk { background: #fff !important; }
    body.printing-bulk a { color: inherit !important; text-decoration: none !important; }
    body.printing-bulk .badge { border: 1px solid #333; color: #000 !important; background: #fff !important; }
}
.bulk-slip-page {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: repeat(3, 1fr);
    width: 210mm;
    height: 297mm;
    page-break-after: always;
}
.bulk-slip-page:last-child { page-break-after: auto; }
.bulk-slip-cell {
    border: 1px solid #999;
    border-radius: 0.5mm;
    padding: 0 2.2mm 1.8mm;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 6.5px;
    line-height: 1.3;
    overflow: hidden;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    color: #222;
    /* A grid item's default min-height is its content's natural size, not 0 -
       without this, a content-heavy cell (return details + request + txns)
       silently grows past its 1fr track instead of being clipped by
       overflow:hidden, pushing the whole page past 297mm and cutting off
       whatever's near the bottom (usually Transactions, since it's last) at
       the physical paper edge instead of the cell's own edge. */
    min-height: 0;
}
.bulk-slip-cell .bsc-brandbar {
    background: #fff;
    color: #222;
    border-bottom: 1px solid var(--brand);
    margin: 0 -2.2mm 1mm;
    padding: 1.1mm 2.2mm .9mm;
    display: flex;
    align-items: center;
    gap: 1.2mm;
}
.bulk-slip-cell .bsc-logo { height: 5mm; width: auto; flex-shrink: 0; }
.bulk-slip-cell .bsc-brand-name { font-size: 6px; font-weight: 700; line-height: 1.15; }
.bulk-slip-cell .bsc-brand-sub { font-size: 4.5px; color: #777; text-transform: uppercase; letter-spacing: .04em; }
.bulk-slip-cell .bsc-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: .3mm; }
.bulk-slip-cell .bsc-title { font-size: 8px; font-weight: bold; color: #222; display: flex; align-items: center; gap: .8mm; }
.bulk-slip-cell .bsc-title i { color: var(--brand); }
.bulk-slip-cell .bsc-docno { font-size: 9px; font-weight: bold; letter-spacing: .02em; border-bottom: 1px solid #ccc; padding-bottom: .6mm; margin-bottom: .8mm; }
.bulk-slip-cell .bsc-row { display: flex; justify-content: space-between; align-items: baseline; gap: 1.5mm; padding: .2mm 0; }
.bulk-slip-cell .bsc-label { color: #666; font-weight: 700; flex-shrink: 0; display: flex; align-items: center; gap: .6mm; text-transform: uppercase; font-size: 5.5px; letter-spacing: .02em; }
.bulk-slip-cell .bsc-label i { color: #888; font-size: 6.5px; }
.bulk-slip-cell .bsc-val { text-align: right; font-weight: 600; }
.bulk-slip-cell .bsc-section { font-weight: bold; text-transform: uppercase; font-size: 5.5px; letter-spacing: .03em; border-top: 1px solid #ccc; margin-top: .8mm; padding-top: .8mm; color: #444; display: flex; align-items: center; gap: .6mm; }
.bulk-slip-cell .bsc-section i { color: var(--brand); }
.bulk-slip-cell .bsc-note { font-style: italic; color: #555; display: flex; align-items: flex-start; gap: .6mm; }
.bulk-slip-cell .bsc-note i { margin-top: .3mm; }
.bulk-slip-cell .bsc-txn { color: #555; word-break: break-word; }
.bulk-slip-cell .bsc-badge {
    display: inline-block;
    font-size: 5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .02em;
    background: #fff;
    border: .5px solid currentColor;
    padding: .3mm 1.3mm;
    border-radius: 1mm;
    white-space: nowrap;
}
.bulk-slip-cell .bsc-badge-info { color: #0b6e85; }
.bulk-slip-cell .bsc-badge-success { color: #198754; }
.bulk-slip-cell .bsc-badge-secondary { color: #555; }
.bulk-slip-cell .bsc-badge-good { color: #2f6f4f; }
.bulk-slip-cell .bsc-badge-fair { color: #8a6d1e; }
.bulk-slip-cell .bsc-badge-bad { color: #a13c30; }
.bulk-slip-cell .bsc-signrow { margin-top: auto; padding-top: 1.5mm; display: flex; gap: 3mm; }
.bulk-slip-cell .bsc-signbox { flex: 1; border-top: .75px solid #999; padding-top: .6mm; text-align: center; font-size: 5px; color: #666; text-transform: uppercase; letter-spacing: .02em; }

.login-wrap {
    min-height: 100vh;
    min-height: 100dvh; /* accounts for mobile browser address bars, where 100vh alone leaves the card pinned near the top */
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, var(--brand-darker), var(--brand));
}
.login-card { width: 100%; max-width: 380px; border-radius: .8rem; margin: 2rem auto; }
