/*
 * app.css — the whole application's styling, in one file.
 *
 * Before this, the look lived in three inline <style> blocks inside
 * layouts/top.php, which meant it could not be cached, could not be diffed
 * usefully, and had already drifted: .role-badge and .user-dropdown-toggle were
 * each declared twice with different values, so which one won depended on block
 * order rather than on anyone's decision.
 *
 * The approach here is to restyle Bootstrap through its own custom properties
 * rather than to override its rules. Setting --bs-primary-rgb once re-tints
 * buttons, links, badges, alerts, focus rings and form controls together, and
 * nothing needs !important to make it stick.
 *
 * Design notes, for whoever edits this next:
 *
 *   Type      Inter, not Poppins. Poppins is a geometric display face with a
 *             single-storey 'a' and near-circular counters; at the 12–13px this
 *             app runs at, in dense tables, it loses legibility. Inter was drawn
 *             for exactly this size on screens.
 *
 *   Numerals  Every figure in the app is set with tabular-nums. In a ledger,
 *             digits that do not share a width make columns of money fail to
 *             line up, and a column that does not line up cannot be scanned.
 *             This is the one thing worth being fussy about in a till system.
 *
 *   Colour    Cool neutrals, one blue for anything interactive, and semantic
 *             green/red/amber reserved for money and stock states. Colour means
 *             something here, so it is not spent on decoration.
 */

/* ------------------------------------------------------------------ tokens */

:root {
    /* Neutrals — cool, so they sit under the blue without muddying it. */
    --ink-950: #0d141c;
    --ink-900: #16202b;
    --ink-800: #1e2b3a;
    --ink-700: #35485e;
    --ink-500: #64748b;
    --ink-400: #94a3b8;
    --ink-300: #cbd5e1;
    --ink-200: #e2e8f0;
    --ink-100: #eef2f6;
    --ink-050: #f6f8fa;

    --accent: #2563eb;
    --accent-strong: #1d4ed8;
    --accent-soft: #eff4ff;

    --good: #15803d;
    --bad: #b91c1c;
    --warn: #b45309;

    --radius: 8px;
    --radius-sm: 6px;
    --radius-lg: 12px;

    --shadow-1: 0 1px 2px rgba(13, 20, 28, .06), 0 1px 3px rgba(13, 20, 28, .05);
    --shadow-2: 0 2px 6px rgba(13, 20, 28, .07), 0 6px 16px rgba(13, 20, 28, .07);
    --shadow-3: 0 8px 28px rgba(13, 20, 28, .16);

    --navbar-height: 52px;

    /* Bootstrap's own variables. Changing these restyles the framework. */
    --bs-primary: var(--accent);
    --bs-primary-rgb: 37, 99, 235;
    --bs-link-color: var(--accent-strong);
    --bs-link-color-rgb: 29, 78, 216;
    --bs-link-hover-color: var(--ink-900);
    --bs-body-color: var(--ink-900);
    --bs-body-bg: var(--ink-050);
    --bs-border-color: var(--ink-200);
    --bs-border-radius: var(--radius);
    --bs-border-radius-sm: var(--radius-sm);
    --bs-border-radius-lg: var(--radius-lg);
    --bs-body-font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --bs-body-font-size: .9375rem;
    --bs-body-line-height: 1.55;
}

/* ------------------------------------------------------------------- base */

* { -webkit-tap-highlight-color: transparent; }

body {
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    font-family: var(--bs-body-font-family);
    font-size: var(--bs-body-font-size);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    padding-top: var(--navbar-height);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* Figures line up. See the note at the top of this file. */
body,
.table,
input,
select,
textarea,
.badge,
.ss-control {
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1, 'cv05' 1;
}

h1, h2, h3, h4, h5, h6 {
    font-weight: 600;
    letter-spacing: -.011em;
    color: var(--ink-950);
}

h5 { font-size: 1.0625rem; }
h6 { font-size: .9375rem; }

a { text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.text-muted { color: var(--ink-500) !important; }

/* ----------------------------------------------------------------- navbar */

.navbar {
    height: var(--navbar-height);
    padding: 0 .875rem;
    background: var(--ink-900);
    border-bottom: 1px solid rgba(255, 255, 255, .06);
    box-shadow: 0 1px 3px rgba(13, 20, 28, .18);
}

.navbar-brand {
    font-weight: 600;
    font-size: 1.0625rem;
    letter-spacing: -.01em;
    color: #fff;
    display: flex;
    align-items: center;
}

.navbar-brand:hover { color: #fff; text-decoration: none; }
.navbar-brand i { color: var(--accent); }

.menu-btn,
.user-dropdown-toggle {
    background: transparent;
    border: 0;
    color: rgba(255, 255, 255, .82);
    padding: .375rem .625rem;
    border-radius: var(--radius-sm);
    transition: background .15s ease, color .15s ease;
    display: flex;
    align-items: center;
}

.menu-btn:hover,
.menu-btn:focus,
.user-dropdown-toggle:hover,
.user-dropdown-toggle:focus {
    color: #fff;
    background: rgba(255, 255, 255, .1);
}

.user-dropdown-toggle::after { margin-left: .5rem; opacity: .6; }

.role-badge {
    font-size: .6875rem;
    font-weight: 500;
    padding: .15rem .4rem;
    border-radius: 4px;
    background: var(--ink-100);
    color: var(--ink-700);
}

.company-badge {
    font-size: .6875rem;
    font-weight: 500;
    padding: .15rem .4rem;
    border-radius: 4px;
    background: var(--accent-soft);
    color: var(--accent-strong);
}

.user-avatar-container {
    position: relative;
    height: 34px;
    width: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.company-indicator {
    position: absolute;
    bottom: -3px;
    right: -3px;
    background: var(--accent);
    color: #fff;
    font-size: 9px;
    font-weight: 700;
    height: 17px;
    width: 17px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid #fff;
}

/* ---------------------------------------------------------------- layout */

.main-content { flex: 1; padding: 1rem 0 1.5rem; }

.footer {
    background: #fff;
    border-top: 1px solid var(--ink-200);
    color: var(--ink-500);
    font-size: .8125rem;
}

/* ----------------------------------------------------------------- cards */

.card {
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    box-shadow: var(--shadow-1);
    background: #fff;
}

.card-header {
    background: #fff;
    border-bottom: 1px solid var(--ink-200);
    padding: .625rem .875rem;
    font-weight: 600;
    font-size: .875rem;
    color: var(--ink-800);
    letter-spacing: -.005em;
}

.card-body { padding: .875rem; }

.card-footer {
    background: var(--ink-050);
    border-top: 1px solid var(--ink-200);
    padding: .625rem .875rem;
}

.compact-card { margin-bottom: .75rem; border-radius: var(--radius); }
.compact-card .card-header { padding: .625rem .875rem; }
.compact-card .card-body { padding: .875rem; }

.hover-raise { transition: transform .15s ease, box-shadow .15s ease; }
.hover-raise:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); }

/* ---------------------------------------------------------------- tables */

.table {
    --bs-table-color: var(--ink-900);
    --bs-table-border-color: var(--ink-200);
    margin-bottom: 0;
    font-size: .875rem;
}

.table > thead > tr > th {
    background: var(--ink-050);
    color: var(--ink-500);
    font-weight: 600;
    font-size: .75rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    border-bottom: 1px solid var(--ink-200);
    padding: .5rem .625rem;
    white-space: nowrap;
}

.table > tbody > tr > td { padding: .5rem .625rem; vertical-align: middle; }
.table > tbody > tr:hover > td { background: var(--accent-soft); }
.table-sm > :not(caption) > * > * { padding: .375rem .5rem; }

/* Money reads better right-aligned and slightly tightened. */
.table .text-end { font-variant-numeric: tabular-nums; letter-spacing: -.01em; }

/* --------------------------------------------------------------- controls */

.form-control,
.form-select {
    border-color: var(--ink-300);
    border-radius: var(--radius-sm);
    color: var(--ink-900);
    transition: border-color .12s ease, box-shadow .12s ease;
}

.form-control:focus,
.form-select:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(37, 99, 235, .14);
}

.form-control::placeholder { color: var(--ink-400); }

.form-label {
    font-size: .8125rem;
    font-weight: 500;
    color: var(--ink-700);
    margin-bottom: .25rem;
}

.btn {
    border-radius: var(--radius-sm);
    font-weight: 500;
    letter-spacing: -.005em;
    transition: background-color .12s ease, border-color .12s ease,
                box-shadow .12s ease, transform .06s ease;
}

.btn:active { transform: translateY(1px); }

.btn-primary {
    --bs-btn-bg: var(--accent);
    --bs-btn-border-color: var(--accent);
    --bs-btn-hover-bg: var(--accent-strong);
    --bs-btn-hover-border-color: var(--accent-strong);
    --bs-btn-active-bg: var(--accent-strong);
    box-shadow: var(--shadow-1);
}

.btn-sm { font-size: .8125rem; padding: .3rem .625rem; }

.btn-outline-secondary {
    --bs-btn-color: var(--ink-700);
    --bs-btn-border-color: var(--ink-300);
    --bs-btn-hover-bg: var(--ink-100);
    --bs-btn-hover-color: var(--ink-900);
    --bs-btn-hover-border-color: var(--ink-300);
}

/* A pressed segment in a toggle group should look pressed. */
.btn-group .btn.active,
.btn-check:checked + .btn {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.badge { font-weight: 500; letter-spacing: .01em; border-radius: 5px; }

.dropdown-menu {
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    box-shadow: var(--shadow-2);
    padding: .3125rem;
    min-width: 220px;
}

.dropdown-item { border-radius: var(--radius-sm); padding: .4rem .625rem; font-size: .875rem; }
.dropdown-item:hover,
.dropdown-item:focus { background: var(--ink-100); }
.dropdown-item .fas { width: 18px; text-align: center; color: var(--ink-400); }
.dropdown-item.text-danger:hover { background: rgba(185, 28, 28, .08); }
.dropdown-divider { margin: .25rem 0; border-color: var(--ink-200); opacity: 1; }

.alert { border-radius: var(--radius); border-width: 1px; font-size: .875rem; }

.modal-content {
    border: 0;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-3);
}

.modal-header { border-bottom: 1px solid var(--ink-200); padding: .75rem 1rem; }
.modal-footer { border-top: 1px solid var(--ink-200); padding: .75rem 1rem; }

/* ================================================================ */
/*  Searchable select — see public/js/searchable-select.js          */
/* ================================================================ */

.ss { position: relative; display: block; width: 100%; }

/* Set when the select carried an inline width (e.g. style="width:auto"), so a
   narrow picker stays narrow instead of filling its row. */
.ss-shrink { display: inline-block; min-width: 4.5rem; }

/* The real <select>. Kept in the DOM and in the layout, but invisible, so
   `required` validation can still focus it and forms post unchanged. */
.ss-native {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    pointer-events: none;
    border: 0;
    padding: 0;
    margin: 0;
    appearance: none;
}

.ss-control {
    display: flex;
    align-items: center;
    gap: .375rem;
    width: 100%;
    min-height: calc(1.5em + .75rem + 2px);
    padding: .375rem .625rem;
    background: #fff;
    border: 1px solid var(--ink-300);
    border-radius: var(--radius-sm);
    color: var(--ink-900);
    font-size: .9375rem;
    line-height: 1.5;
    cursor: pointer;
    user-select: none;
    transition: border-color .12s ease, box-shadow .12s ease;
}

.ss-sm .ss-control {
    min-height: calc(1.5em + .5rem + 2px);
    padding: .25rem .5rem;
    font-size: .8125rem;
}

.ss-control:hover { border-color: var(--ink-400); }

.ss-open .ss-control,
.ss-control:focus-visible {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(37, 99, 235, .14);
    outline: none;
}

.ss-invalid .ss-control { border-color: var(--bad); }

.ss-disabled .ss-control {
    background: var(--ink-100);
    color: var(--ink-400);
    cursor: not-allowed;
}

.ss-value {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ss-empty .ss-value { color: var(--ink-400); }

.ss-caret {
    flex: 0 0 auto;
    display: flex;
    color: var(--ink-400);
    transition: transform .16s ease;
}

.ss-open .ss-caret { transform: rotate(180deg); color: var(--accent); }

/* Multi-select chips */
.ss-multiple .ss-value {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem;
    white-space: normal;
}

.ss-chip {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: .0625rem .25rem .0625rem .4rem;
    background: var(--accent-soft);
    color: var(--accent-strong);
    border-radius: 4px;
    font-size: .8125rem;
    line-height: 1.4;
}

.ss-chip-x {
    border: 0;
    background: transparent;
    color: inherit;
    opacity: .6;
    font-size: 1rem;
    line-height: 1;
    padding: 0 .125rem;
    cursor: pointer;
}

.ss-chip-x:hover { opacity: 1; }

/* The panel is a child of <body>, positioned fixed, so nothing clips it —
   not a card, not .table-responsive, not a modal. */
.ss-panel {
    position: fixed;
    z-index: 1080;
    display: flex;
    flex-direction: column;
    max-height: 320px;
    max-width: min(560px, calc(100vw - 16px));
    background: #fff;
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    box-shadow: var(--shadow-3);
    overflow: hidden;
    animation: ss-in .12s ease-out;
}

.ss-panel[hidden] { display: none; }

@keyframes ss-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: none; }
}

.ss-search {
    flex: 0 0 auto;
    padding: .4375rem;
    border-bottom: 1px solid var(--ink-200);
    background: var(--ink-050);
}

.ss-search[hidden] { display: none; }

.ss-search-input {
    width: 100%;
    padding: .3125rem .5rem;
    border: 1px solid var(--ink-300);
    border-radius: var(--radius-sm);
    font-size: .875rem;
    font-family: inherit;
    color: var(--ink-900);
    background: #fff;
}

.ss-search-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(37, 99, 235, .14);
}

.ss-list {
    flex: 1 1 auto;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: .25rem;
}

.ss-group {
    padding: .375rem .5rem .1875rem;
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--ink-400);
}

.ss-option {
    padding: .375rem .5rem;
    border-radius: var(--radius-sm);
    font-size: .875rem;
    line-height: 1.45;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ss-active { background: var(--ink-100); }

.ss-selected {
    color: var(--accent-strong);
    font-weight: 500;
}

.ss-selected.ss-active { background: var(--accent-soft); }

.ss-option-disabled { color: var(--ink-400); cursor: not-allowed; }

.ss-note {
    flex: 0 0 auto;
    padding: .4375rem .625rem;
    border-top: 1px solid var(--ink-200);
    background: var(--ink-050);
    color: var(--ink-500);
    font-size: .75rem;
}

.ss-note[hidden] { display: none; }

/* ------------------------------------------------- navigation feedback */
/*  Paired with public/js/common.js. A click that takes a while has to look
    like it did something, or the only signal is that nothing happened.  */

#app-progress {
    position: fixed;
    top: 0;
    left: 0;
    height: 2px;
    width: 0;
    z-index: 2000;
    background: var(--accent);
    box-shadow: 0 0 8px rgba(37, 99, 235, .6);
    opacity: 0;
    transition: width .2s ease, opacity .25s ease;
    pointer-events: none;
}

#app-progress.on { opacity: 1; }

.is-busy { position: relative; pointer-events: none; opacity: .65; }

.btn .spinner-border-sm { vertical-align: -.125em; }

#app-stuck {
    position: fixed;
    left: 50%;
    bottom: 1.25rem;
    transform: translateX(-50%);
    z-index: 2001;
    display: flex;
    align-items: center;
    gap: .625rem;
    padding: .5rem .875rem;
    background: var(--ink-900);
    color: #fff;
    border-radius: 999px;
    box-shadow: var(--shadow-3);
    font-size: .8125rem;
}

#app-stuck button {
    border: 0;
    background: rgba(255, 255, 255, .16);
    color: #fff;
    border-radius: 999px;
    padding: .1875rem .625rem;
    font-size: .75rem;
    cursor: pointer;
}

#app-stuck button:hover { background: rgba(255, 255, 255, .28); }

/* -------------------------------------------------------- dashboard bits */

.tile-link {
    border: 1px solid var(--ink-200);
    color: var(--ink-700);
    transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease;
}

.tile-link:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-2);
    border-color: var(--accent);
    color: var(--accent-strong);
    text-decoration: none;
}

/* .stat-value / .stat-label used to live here. Nothing referenced them — the
   summary cards now use .stat-card__value / __label (see SUMMARY CARDS), and
   the figures the dashboard, account ledger, archive and stock count screens
   render by hand all use Bootstrap's fs-* utilities instead. So the treatment
   is applied to those directly, which is what actually reaches the screen.

   Only the semibold ones: fs-* on its own is used for plain headings too, and
   tightening those would change type that is not a figure.

   No colour is set here on purpose. Bootstrap's text-danger / text-warning
   utilities carry !important, so the semantic red and amber on the stock flags
   keep winning; setting a colour would only affect the uncoloured ones and make
   the rule look like it does more than it does. */
.fs-3.fw-semibold,
.fs-4.fw-semibold,
.fs-5.fw-semibold {
    letter-spacing: -.02em;
    line-height: 1.2;
}

/* ============================================================================
 *  IN-PAGE DATA LOADING
 *
 *  common.js already covers whole-page navigation: a click that loads a new
 *  document gets a progress bar and, if it drags, a stuck notice. What it could
 *  not cover is the case where the document never changes — every list screen
 *  fetches its rows over AJAX, so clicking page 2, sorting a column or typing a
 *  search left the OLD rows on screen with no indication that anything was
 *  happening. Nothing moved, so the only reasonable conclusion was that the
 *  click missed.
 *
 *  Three states, and the distinction between them is the point:
 *
 *    FIRST LOAD    Nothing worth keeping on screen, so skeleton rows stand in.
 *                  They occupy the height the real rows will, which is what
 *                  stops the card snapping taller a moment later.
 *
 *    RELOAD        There ARE rows worth keeping. They stay, dimmed, under a
 *                  veil and a spinner. Replacing them with skeletons would
 *                  throw away context the person is still reading, and would
 *                  make a 200ms page change flash the whole table.
 *
 *    STALE FIGURE  Summary cards are computed server-side over the whole
 *                  filtered range, so during a reload their numbers are
 *                  briefly wrong. Marked rather than blanked: a dimmed old
 *                  number is more useful than an empty box, and it is honest
 *                  about being the previous value.
 * ========================================================================= */

@keyframes app-shimmer {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
}

/* A stand-in for text that has not arrived. Transparent colour rather than
   empty content, so any placeholder character inside still reserves width. */
.skeleton {
    background: linear-gradient(
        90deg,
        var(--ink-100) 25%,
        var(--ink-200) 37%,
        var(--ink-100) 63%
    );
    background-size: 200% 100%;
    animation: app-shimmer 1.4s ease-in-out infinite;
    border-radius: 4px;
    color: transparent !important;
    user-select: none;
}

.skeleton-line {
    display: inline-block;
    height: .72em;
    vertical-align: middle;
    width: 100%;
}

/* Widths that vary per column read as data rather than as a loading bar. */
.skeleton-line.w-40 { width: 40%; }
.skeleton-line.w-60 { width: 60%; }
.skeleton-line.w-80 { width: 80%; }

/* ---- the table's own busy state ---- */

.table-host { position: relative; }

.table-host__veil {
    position: absolute;
    inset: 0;
    z-index: 3;
    display: none;
    align-items: flex-start;
    justify-content: center;
    padding-top: 3.5rem;
    background: rgba(255, 255, 255, .55);
    backdrop-filter: saturate(.85) blur(.5px);
    border-radius: inherit;
}

.table-host.is-loading > .table-host__veil { display: flex; }

/* Dim what is being replaced, and stop it being clicked while it is stale —
   a Delete button belonging to a row that is about to disappear is a trap. */
.table-host.is-loading tbody {
    opacity: .4;
    pointer-events: none;
    transition: opacity .12s ease;
}

.table-host__spinner {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    padding: .375rem .75rem;
    background: #fff;
    border: 1px solid var(--ink-200);
    border-radius: 999px;
    box-shadow: var(--shadow-2);
    font-size: .8125rem;
    font-weight: 500;
    color: var(--ink-700);
}

/* Paging and sorting are disabled mid-fetch, because a second click before the
   first returns races two responses into the same tbody and the loser wins. */
.table-host.is-loading ~ .card-footer .pagination,
.card.is-loading .pagination { opacity: .5; pointer-events: none; }

.table-host.is-loading th.sortable { cursor: progress; }

/* ---- a figure that is known to be out of date ---- */

[data-stat].is-stale {
    opacity: .45;
    transition: opacity .12s ease;
}

/* ============================================================================
 *  SUMMARY CARDS
 *
 *  Was: a Bootstrap card with border-{colour} on all four sides and a 2x icon
 *  at opacity .25. The all-round border made every card shout its colour while
 *  saying nothing about which figure mattered, and a quarter-opacity glyph the
 *  size of the number competed with the number.
 *
 *  Now: colour appears once, as a left edge and a small tinted icon chip. The
 *  figure is the largest thing in the card because the figure is the point.
 * ========================================================================= */

.stat-card {
    position: relative;
    height: 100%;
    background: #fff;
    border: 1px solid var(--ink-200);
    border-left: 3px solid var(--ink-300);
    border-radius: var(--radius);
    box-shadow: var(--shadow-1);
    padding: .75rem .875rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    transition: box-shadow .15s ease, border-color .15s ease;
}

.stat-card:hover { box-shadow: var(--shadow-2); }

.stat-card__text { min-width: 0; }

.stat-card__label {
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .045em;
    text-transform: uppercase;
    color: var(--ink-500);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.stat-card__value {
    font-size: 1.375rem;
    font-weight: 650;
    letter-spacing: -.02em;
    line-height: 1.2;
    color: var(--ink-950);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.stat-card__sub {
    font-size: .75rem;
    color: var(--ink-500);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.stat-card__icon {
    flex: 0 0 auto;
    height: 36px;
    width: 36px;
    border-radius: var(--radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: .9375rem;
    background: var(--ink-100);
    color: var(--ink-500);
}

/* Tone. Reads from data-tone so an unrecognised colour name degrades to the
   neutral above instead of producing an unstyled card. */
.stat-card[data-tone="primary"]   { border-left-color: var(--accent); }
.stat-card[data-tone="primary"]   .stat-card__icon { background: var(--accent-soft); color: var(--accent-strong); }

.stat-card[data-tone="success"]   { border-left-color: var(--good); }
.stat-card[data-tone="success"]   .stat-card__icon { background: #e8f6ed; color: var(--good); }

.stat-card[data-tone="danger"]    { border-left-color: var(--bad); }
.stat-card[data-tone="danger"]    .stat-card__icon { background: #fdeaea; color: var(--bad); }

.stat-card[data-tone="warning"]   { border-left-color: var(--warn); }
.stat-card[data-tone="warning"]   .stat-card__icon { background: #fdf3e3; color: var(--warn); }

.stat-card[data-tone="info"]      { border-left-color: #0e7490; }
.stat-card[data-tone="info"]      .stat-card__icon { background: #e6f5f8; color: #0e7490; }

.stat-card[data-tone="secondary"] { border-left-color: var(--ink-400); }

/* A card whose figure is the point of the page can say so. */
.stat-card--lead .stat-card__value { font-size: 1.625rem; }

/* ============================================================================
 *  PAGE FURNITURE
 *  The header row and the filter/quick-link strips that sit above the content
 *  on nearly every screen. These were being hand-assembled per view out of
 *  d-flex utilities, which is why no two pages had quite the same spacing.
 * ========================================================================= */

.page-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    flex-wrap: wrap;
    margin-bottom: 1rem;
}

.page-head h5 { margin: 0; display: flex; align-items: center; gap: .5rem; }
.page-head h5 .fas { color: var(--ink-400); font-size: .9375rem; }

/* The "Go to" / "Quick create" strip on the dashboard, and the filter row on
   list screens. Same container so they line up with each other. */
.action-strip {
    background: #fff;
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    box-shadow: var(--shadow-1);
    padding: .625rem .75rem;
    margin-bottom: 1rem;
}

.action-strip + .action-strip { margin-top: -.5rem; }

.action-strip__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .375rem;
}

.action-strip__row + .action-strip__row {
    margin-top: .5rem;
    padding-top: .5rem;
    border-top: 1px dashed var(--ink-200);
}

.action-strip__legend {
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .045em;
    text-transform: uppercase;
    color: var(--ink-400);
    margin-right: .25rem;
    white-space: nowrap;
}

/* ---- empty and error states ---- */

.state-note {
    text-align: center;
    padding: 2rem 1rem;
    color: var(--ink-500);
}

.state-note__icon {
    font-size: 1.75rem;
    color: var(--ink-300);
    margin-bottom: .5rem;
    display: block;
}

/* ---- table refinements ---- */

/* A long table scrolled past its own header loses the meaning of its columns.
 *
 * Opt-in, and only useful with a height to scroll inside: .table-responsive sets
 * overflow-x, which makes IT the sticky containing block rather than the
 * viewport, so without a max-height the header has nowhere to travel and the
 * class does nothing. Apply both together:
 *
 *     <div class="table-responsive table-sticky" style="max-height:70vh">
 *
 * Not applied to the list screens by default: those tables page at 25 rows and
 * scroll the document rather than a box, and capping their height would trade a
 * lost header for a nested scrollbar, which is worse. */
.table-sticky thead th {
    position: sticky;
    top: 0;
    z-index: 2;
}

.table > thead > tr > th.sortable { user-select: none; transition: color .12s ease; }
.table > thead > tr > th.sortable:hover { color: var(--ink-800); background: var(--ink-100); }

.pagination {
    --bs-pagination-color: var(--ink-700);
    --bs-pagination-border-color: var(--ink-200);
    --bs-pagination-hover-bg: var(--ink-100);
    --bs-pagination-hover-border-color: var(--ink-300);
    --bs-pagination-active-bg: var(--accent);
    --bs-pagination-active-border-color: var(--accent);
    --bs-pagination-focus-box-shadow: 0 0 0 3px rgba(37, 99, 235, .14);
}

/* Action buttons in a table row: quiet until the row is hovered, so a wide
   table is not a wall of outlined buttons competing with the data. */
.table tbody tr td .btn-sm { opacity: .82; transition: opacity .12s ease; }
.table tbody tr:hover td .btn-sm { opacity: 1; }

/* ---- narrow screens ---- */

@media (max-width: 575.98px) {
    .stat-card { padding: .625rem .75rem; }
    .stat-card__value { font-size: 1.1875rem; }
    .stat-card__icon { display: none; }
    .page-head { align-items: flex-start; }
    .action-strip__legend { width: 100%; margin-bottom: .125rem; }
}

/* ============================================================================
 *  APPLICATION MENU
 *
 *  Moved here from an inline <style> block in
 *  resources/views/partials/menu_modal.php, which was the last genuine one
 *  outside the print layouts. Inline component CSS could not be cached with
 *  this file, could not be diffed usefully, and was re-parsed on every page
 *  that rendered the nav — the same three reasons given at the top of this
 *  file for consolidating the original blocks out of top.php.
 *
 *  It also already depended on --accent from here, via a var() fallback
 *  written specifically to survive being in a different file. That fallback
 *  is kept: it costs nothing and still covers the stylesheet failing to load.
 * ========================================================================= */

/* ---- Application menu (self-contained, no icon font) ------------------ */
.app-menu {
    /* --primary-color went away with the inline <style> blocks in top.php, so
       this was silently falling back to #2c3e50 — the old slate palette — and
       the menu header no longer matched the restyled app. Point it at the
       token app.css actually defines, keeping a literal last resort in case
       the stylesheet has not loaded. */
    --am-accent: var(--accent, #2563eb);
}

.app-menu-header {
    background: var(--am-accent);
    color: #fff;
}

.app-menu-header .app-menu-ico {
    width: 20px;
    height: 20px;
}

/* Search field with a leading icon */
.app-menu-search {
    position: relative;
}

.app-menu-search-ico {
    position: absolute;
    top: 50%;
    left: 10px;
    transform: translateY(-50%);
    color: #9aa0a6;
    pointer-events: none;
    display: inline-flex;
}

.app-menu-search-ico .app-menu-ico {
    width: 16px;
    height: 16px;
}

.app-menu-search .form-control {
    padding-left: 32px;
    border-radius: 8px;
}

/* Group heading */
.app-menu-group-title {
    font-size: .68rem;
    font-weight: 600;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: #8b8f94;
    padding: 0 .35rem .35rem;
    margin-bottom: .1rem;
    border-bottom: 1px solid rgba(0, 0, 0, .05);
}

.app-menu-list {
    display: flex;
    flex-direction: column;
    gap: 1px;
    margin-top: .35rem;
}

/* Menu item */
.app-menu-item {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .42rem .55rem;
    border-radius: .5rem;
    text-decoration: none;
    color: #3c4147;
    font-size: .86rem;
    line-height: 1.2;
    transition: background-color .12s ease, color .12s ease;
}

.app-menu-item .app-menu-ico {
    width: 18px;
    height: 18px;
    flex: 0 0 18px;
    opacity: .65;
    transition: opacity .12s ease;
}

.app-menu-item:hover,
.app-menu-item:focus-visible {
    background: rgba(0, 0, 0, .05);
    color: var(--am-accent);
    outline: none;
}

.app-menu-item:hover .app-menu-ico,
.app-menu-item:focus-visible .app-menu-ico {
    opacity: 1;
}

/* Current page */
.app-menu-item.active {
    background: var(--am-accent);
    color: #fff;
}

.app-menu-item.active .app-menu-ico {
    opacity: 1;
}

/* Keyboard highlight.
   A separate treatment from :hover and from .active, because it has to be
   legible on top of both — the arrow keys can land on the current page, and
   the mouse can be resting anywhere while the hands are on the keyboard. An
   inset ring reads clearly over the accent fill without repainting it. */
.app-menu-item.kb-active {
    background: rgba(0, 0, 0, .05);
    color: var(--am-accent);
    box-shadow: inset 0 0 0 2px var(--am-accent);
}

.app-menu-item.kb-active .app-menu-ico {
    opacity: 1;
}

.app-menu-item.active.kb-active {
    background: var(--am-accent);
    color: #fff;
    box-shadow: inset 0 0 0 2px #fff, 0 0 0 2px var(--am-accent);
}

@media (prefers-contrast: more) {
    .app-menu-item.kb-active {
        box-shadow: inset 0 0 0 3px var(--am-accent);
    }
}

.app-menu-hint kbd {
    display: inline-block;
    min-width: 1.35em;
    padding: .05rem .3rem;
    margin-right: .15rem;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, .18);
    border-bottom-width: 2px;
    border-radius: .25rem;
    color: #55606b;
    font-family: inherit;
    font-size: .72rem;
    line-height: 1.4;
    text-align: center;
}

.app-menu-hint kbd:not(:first-child) {
    margin-left: .1rem;
}

/* The hint is for a keyboard, so it is noise on a touch-only device. */
@media (hover: none) and (pointer: coarse) {
    .app-menu-hint {
        display: none;
    }
}

/* Compact the modal footer chrome */
.app-menu-footer {
    background: #fafbfc;
}

@media (max-width: 575.98px) {
    .app-menu-group {
        margin-bottom: .25rem;
    }
}

/* --------------------------------------------------------------- print */

@media print {
    .navbar, .footer, .btn, .page-toolbar, #app-progress, #app-stuck,
    .table-host__veil, .action-strip { display: none !important; }
    body { padding-top: 0; background: #fff; }
    .card { box-shadow: none; border-color: #ccc; }
    .stat-card { box-shadow: none; border-color: #ccc; }

    /* A printed page cannot finish loading, so a veil or a shimmer would be
       baked into the paper. Show whatever rows exist at full strength. */
    .table-host.is-loading tbody { opacity: 1; }
    .skeleton { animation: none; background: none; color: inherit !important; }
}

/* ------------------------------------------------------- accessibility */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: .001ms !important;
        transition-duration: .001ms !important;
    }

    /* The rule above stops the shimmer, which would otherwise leave the
       skeleton mid-gradient. Flatten it to a plain block so it still reads as
       "not here yet" without moving. */
    .skeleton {
        background: var(--ink-100);
        animation: none !important;
    }
}
